mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-22 22:27:56 +08:00
1. There is only one esxi agent needed in OneCloud. 2. Workspace path shoule be EsxiAgentPath not the parent directory of this. 3. The VM name is too long for vcenter when importing VM so that shrink it.
435 lines
11 KiB
Go
435 lines
11 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/version"
|
|
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/agent/iagent"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/object"
|
|
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
|
"yunion.io/x/onecloud/pkg/util/httputils"
|
|
)
|
|
|
|
type SZoneInfo struct {
|
|
Name string `json:"name"`
|
|
Id string `json:"id"`
|
|
}
|
|
|
|
type SBaseAgent struct {
|
|
object.SObject
|
|
|
|
ListenInterface *net.Interface
|
|
ListenIPs []net.IP
|
|
AgentId string
|
|
AgentName string
|
|
Zone *SZoneInfo
|
|
|
|
CachePath string
|
|
CacheManager *storageman.SLocalImageCacheManager
|
|
|
|
stop bool
|
|
}
|
|
|
|
func getIfaceIPs(iface *net.Interface) ([]net.IP, error) {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ips := make([]net.IP, 0)
|
|
for _, a := range addrs {
|
|
if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
|
if ipnet.IP.To4() != nil {
|
|
ips = append(ips, ipnet.IP)
|
|
}
|
|
}
|
|
}
|
|
return ips, nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) IAgent() iagent.IAgent {
|
|
return agent.GetVirtualObject().(iagent.IAgent)
|
|
}
|
|
|
|
func (agent *SBaseAgent) Init(iagent iagent.IAgent, ifname string, cachePath string) error {
|
|
iface, err := net.InterfaceByName(ifname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var ips []net.IP
|
|
MAX := 60
|
|
wait := 0
|
|
for wait < MAX {
|
|
ips, err = getIfaceIPs(iface)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(ips) == 0 {
|
|
time.Sleep(2 * time.Second)
|
|
wait += 2
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if len(ips) == 0 {
|
|
return fmt.Errorf("Interface %s ip address not found", ifname)
|
|
}
|
|
log.Debugf("Interface %s ip address: %v", iface.Name, ips)
|
|
agent.SetVirtualObject(iagent)
|
|
agent.ListenInterface = iface
|
|
agent.ListenIPs = ips
|
|
agent.CachePath = cachePath
|
|
return nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) GetListenIPs() []net.IP {
|
|
return agent.ListenIPs
|
|
}
|
|
|
|
func (agent *SBaseAgent) FindListenIP(listenAddr string) (net.IP, error) {
|
|
ips := agent.GetListenIPs()
|
|
if listenAddr == "" {
|
|
return ips[0], nil
|
|
}
|
|
if listenAddr == "0.0.0.0" {
|
|
return net.ParseIP(listenAddr), nil
|
|
}
|
|
for _, ip := range ips {
|
|
if ip.String() == listenAddr {
|
|
return ip, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("Not found Address %s on Interface %#v", listenAddr, agent.ListenInterface)
|
|
}
|
|
|
|
func (agent *SBaseAgent) FindAccessIP(accessAddr string) (net.IP, error) {
|
|
if accessAddr == "0.0.0.0" {
|
|
return nil, fmt.Errorf("Access address must be specific, should not be 0.0.0.0")
|
|
}
|
|
return agent.FindListenIP(accessAddr)
|
|
}
|
|
|
|
func (agent *SBaseAgent) startRegister() error {
|
|
// if agent.AgentId != "" {
|
|
// return
|
|
// }
|
|
|
|
var delayRetryTime = 30 * time.Second
|
|
var lastTry time.Time
|
|
|
|
for !agent.stop {
|
|
if time.Now().Sub(lastTry) >= delayRetryTime {
|
|
session := agent.IAgent().GetAdminSession()
|
|
err := agent.register(session)
|
|
if err == nil {
|
|
log.Infof("Register success!")
|
|
return nil
|
|
}
|
|
log.Errorf("Register error: %v, retry after %s...", err, delayRetryTime)
|
|
lastTry = time.Now()
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
return fmt.Errorf("Error Stop")
|
|
}
|
|
|
|
func (agent *SBaseAgent) register(session *mcclient.ClientSession) error {
|
|
var err error
|
|
err = agent.fetchZone(session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = agent.createOrUpdateBaremetalAgent(session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Infof("%s %s:%s register success, do offline", agent.IAgent().GetAgentType(), agent.AgentName, agent.AgentId)
|
|
err = agent.doOffline(session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) fetchZone(session *mcclient.ClientSession) error {
|
|
zoneName := agent.IAgent().GetZoneName()
|
|
var zoneInfoObj jsonutils.JSONObject
|
|
var err error
|
|
if zoneName != "" {
|
|
zoneInfoObj, err = modules.Zones.Get(session, zoneName, nil)
|
|
} else {
|
|
zoneInfoObj, err = agent.getZoneByIP(session)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
zone := SZoneInfo{}
|
|
err = zoneInfoObj.Unmarshal(&zone)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
agent.Zone = &zone
|
|
return nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) getZoneByIP(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
|
|
params := jsonutils.NewDict()
|
|
listenIP, err := agent.IAgent().GetListenIP()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Add(jsonutils.NewString(listenIP.String()), "ip")
|
|
params.Add(jsonutils.JSONTrue, "is_on_premise")
|
|
networks, err := modules.Networks.List(session, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(networks.Data) == 0 {
|
|
return nil, fmt.Errorf("Not found networks by agent listen ip: %s", listenIP)
|
|
}
|
|
wireId, err := networks.Data[0].GetString("wire_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wire, err := modules.Wires.Get(session, wireId, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
zoneId, err := wire.GetString("zone_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
zone, err := modules.Zones.Get(session, zoneId, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return zone, nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSession) error {
|
|
params := jsonutils.NewDict()
|
|
naccessIP, err := agent.IAgent().GetAccessIP()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if agent.IAgent().GetAgentType() != string(api.AgentTypeEsxi) {
|
|
params.Add(jsonutils.NewString(naccessIP.String()), "access_ip")
|
|
}
|
|
params.Add(jsonutils.NewString(agent.IAgent().GetAgentType()), "agent_type")
|
|
ret, err := modules.Baremetalagents.List(session, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var (
|
|
cloudObj jsonutils.JSONObject
|
|
agentId string
|
|
agentName string
|
|
)
|
|
// create or update BaremetalAgent
|
|
if len(ret.Data) == 0 {
|
|
cloudObj, err = agent.createBaremetalAgent(session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
cloudBmAgent := ret.Data[0]
|
|
agentId, _ := cloudBmAgent.GetString("id")
|
|
cloudObj, err = agent.updateBaremetalAgent(session, agentId, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
agentId, err = cloudObj.GetString("id")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
agentName, err = cloudObj.GetString("name")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
agent.AgentId = agentId
|
|
agent.AgentName = agentName
|
|
|
|
storageCacheId, _ := cloudObj.GetString("storagecache_id")
|
|
if len(storageCacheId) > 0 {
|
|
err = agent.updateStorageCache(session, storageCacheId)
|
|
if err != nil {
|
|
if httputils.ErrorCode(errors.Cause(err)) == 404 {
|
|
storageCacheId = ""
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if len(storageCacheId) == 0 {
|
|
storageCacheId, err = agent.createStorageCache(session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cloudObj, err = agent.updateBaremetalAgent(session, agentId, storageCacheId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newStorageCacheId, _ := cloudObj.GetString("storagecache_id")
|
|
if newStorageCacheId != storageCacheId {
|
|
// cleanup the newly created storagecache
|
|
modules.Storagecaches.Delete(session, storageCacheId, nil)
|
|
return errors.Error("agent not support storagecache_id, region might not be up-to-date")
|
|
}
|
|
}
|
|
agent.CacheManager = storageman.NewLocalImageCacheManager(agent.IAgent(), agent.CachePath, storageCacheId)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) GetManagerUri() string {
|
|
accessIP, _ := agent.IAgent().GetAccessIP()
|
|
proto := "http"
|
|
if agent.IAgent().GetEnableSsl() {
|
|
proto = "https"
|
|
}
|
|
return fmt.Sprintf("%s://%s:%d", proto, accessIP, agent.IAgent().GetPort())
|
|
}
|
|
|
|
func (agent *SBaseAgent) GetListenUri() string {
|
|
listenIP, _ := agent.IAgent().GetListenIP()
|
|
proto := "http"
|
|
if agent.IAgent().GetEnableSsl() {
|
|
proto = "https"
|
|
}
|
|
return fmt.Sprintf("%s://%s:%d", proto, listenIP, agent.IAgent().GetPort())
|
|
}
|
|
|
|
func (agent *SBaseAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) {
|
|
params := jsonutils.NewDict()
|
|
if agent.AgentId == "" {
|
|
agentName, err := agent.getName()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "agent.getName")
|
|
}
|
|
params.Add(jsonutils.NewString(agentName), "name")
|
|
}
|
|
accessIP, err := agent.IAgent().GetAccessIP()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "agent.IAgent().GetAccessIP()")
|
|
}
|
|
params.Add(jsonutils.NewString(accessIP.String()), "access_ip")
|
|
params.Add(jsonutils.NewString(agent.GetManagerUri()), "manager_uri")
|
|
params.Add(jsonutils.NewString(agent.Zone.Id), "zone_id")
|
|
params.Add(jsonutils.NewString(agent.IAgent().GetAgentType()), "agent_type")
|
|
params.Add(jsonutils.NewString(version.GetShortString()), "version")
|
|
|
|
return params, nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) getName() (string, error) {
|
|
accessIP, err := agent.IAgent().GetAccessIP()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf("%s-%s", agent.IAgent().GetAgentType(), accessIP), nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) createStorageCache(session *mcclient.ClientSession) (string, error) {
|
|
body := jsonutils.NewDict()
|
|
agentName, err := agent.getName()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "agent.getName")
|
|
}
|
|
body.Set("name", jsonutils.NewString("imagecache-"+agentName))
|
|
body.Set("path", jsonutils.NewString(agent.CachePath))
|
|
body.Set("external_id", jsonutils.NewString(agent.AgentId))
|
|
sc, err := modules.Storagecaches.Create(session, body)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "modules.Storagecaches.Create")
|
|
}
|
|
storageCacheId, err := sc.GetString("id")
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "sc.GetString id")
|
|
}
|
|
return storageCacheId, nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) updateStorageCache(session *mcclient.ClientSession, storageCacheId string) error {
|
|
body := jsonutils.NewDict()
|
|
body.Set("path", jsonutils.NewString(agent.CachePath))
|
|
body.Set("external_id", jsonutils.NewString(agent.AgentId))
|
|
_, err := modules.Storagecaches.Update(session, storageCacheId, body)
|
|
if err != nil {
|
|
return errors.Wrap(err, "modules.Storagecaches.Update")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (agent *SBaseAgent) createBaremetalAgent(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
|
|
params, err := agent.getCreateUpdateInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return modules.Baremetalagents.Create(session, params)
|
|
}
|
|
|
|
func (agent *SBaseAgent) updateBaremetalAgent(session *mcclient.ClientSession, id string, storageCacheId string) (jsonutils.JSONObject, error) {
|
|
var params jsonutils.JSONObject
|
|
var err error
|
|
if len(storageCacheId) > 0 {
|
|
params = jsonutils.NewDict()
|
|
params.(*jsonutils.JSONDict).Set("storagecache_id", jsonutils.NewString(storageCacheId))
|
|
} else {
|
|
params, err = agent.getCreateUpdateInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return modules.Baremetalagents.Update(session, id, params)
|
|
}
|
|
|
|
func (agent *SBaseAgent) doOffline(session *mcclient.ClientSession) error {
|
|
_, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "offline", nil)
|
|
return err
|
|
}
|
|
|
|
func (agent *SBaseAgent) DoOnline(session *mcclient.ClientSession) error {
|
|
_, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "online", nil)
|
|
return err
|
|
}
|
|
|
|
func (agent *SBaseAgent) Start() error {
|
|
err := agent.startRegister()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
agent.IAgent().TuneSystem()
|
|
return agent.IAgent().StartService()
|
|
}
|
|
|
|
func (agent *SBaseAgent) Stop() {
|
|
agent.stop = true
|
|
agent.IAgent().StopService()
|
|
}
|