mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 02:37:24 +08:00
取消userCred.IsSystemAdmin(),在使用policy的应用中,通过userCred.IsAdminAllow判断是否是管理员且具备相应的权限。获取token时,需要传入policy.FilterPolicyCredential,将普通userCred转换成支持rbac的userCred 2. 在应用启动时候,必须设置 SetServiceType,否则退出
2994 lines
98 KiB
Go
2994 lines
98 KiB
Go
package models
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/tristate"
|
|
"yunion.io/x/pkg/util/compare"
|
|
"yunion.io/x/pkg/util/fileutils"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/util/regutils"
|
|
"yunion.io/x/pkg/utils"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
|
"yunion.io/x/onecloud/pkg/compute/options"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
|
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
|
"yunion.io/x/onecloud/pkg/util/httputils"
|
|
"yunion.io/x/onecloud/pkg/util/logclient"
|
|
)
|
|
|
|
const (
|
|
HOST_TYPE_BAREMETAL = "baremetal"
|
|
HOST_TYPE_HYPERVISOR = "hypervisor" // KVM
|
|
HOST_TYPE_ESXI = "esxi" // # VMWare vSphere ESXi
|
|
HOST_TYPE_KUBELET = "kubelet" // # Kubernetes Kubelet
|
|
HOST_TYPE_HYPERV = "hyperv" // # Microsoft Hyper-V
|
|
HOST_TYPE_XEN = "xen" // # XenServer
|
|
HOST_TYPE_ALIYUN = "aliyun"
|
|
HOST_TYPE_AWS = "aws"
|
|
HOST_TYPE_QCLOUD = "qcloud"
|
|
HOST_TYPE_AZURE = "azure"
|
|
|
|
HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR
|
|
|
|
// # possible status
|
|
HOST_ONLINE = "online"
|
|
HOST_ENABLED = "online"
|
|
HOST_OFFLINE = "offline"
|
|
HOST_DISABLED = "offline"
|
|
|
|
NIC_TYPE_IPMI = "ipmi"
|
|
NIC_TYPE_ADMIN = "admin"
|
|
// #NIC_TYPE_NORMAL = 'normal'
|
|
|
|
BAREMETAL_INIT = "init"
|
|
BAREMETAL_PREPARE = "prepare"
|
|
BAREMETAL_PREPARE_FAIL = "prepare_fail"
|
|
BAREMETAL_READY = "ready"
|
|
BAREMETAL_RUNNING = "running"
|
|
BAREMETAL_MAINTAINING = "maintaining"
|
|
BAREMETAL_START_MAINTAIN = "start_maintain"
|
|
BAREMETAL_DELETING = "deleting"
|
|
BAREMETAL_DELETE = "delete"
|
|
BAREMETAL_DELETE_FAIL = "delete_fail"
|
|
BAREMETAL_UNKNOWN = "unknown"
|
|
BAREMETAL_SYNCING_STATUS = "syncing_status"
|
|
BAREMETAL_SYNC = "sync"
|
|
BAREMETAL_SYNC_FAIL = "sync_fail"
|
|
BAREMETAL_START_CONVERT = "start_convert"
|
|
BAREMETAL_CONVERTING = "converting"
|
|
BAREMETAL_START_FAIL = "start_fail"
|
|
BAREMETAL_STOP_FAIL = "stop_fail"
|
|
|
|
HOST_STATUS_RUNNING = BAREMETAL_RUNNING
|
|
HOST_STATUS_READY = BAREMETAL_READY
|
|
HOST_STATUS_UNKNOWN = BAREMETAL_UNKNOWN
|
|
)
|
|
|
|
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE, HOST_TYPE_AWS, HOST_TYPE_QCLOUD}
|
|
|
|
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
|
|
|
|
type SHostManager struct {
|
|
db.SEnabledStatusStandaloneResourceBaseManager
|
|
SInfrastructureManager
|
|
}
|
|
|
|
var HostManager *SHostManager
|
|
|
|
func init() {
|
|
HostManager = &SHostManager{
|
|
SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
|
|
SHost{},
|
|
"hosts_tbl",
|
|
"host",
|
|
"hosts",
|
|
),
|
|
}
|
|
HostManager.SetAlias("baremetal", "baremetals")
|
|
}
|
|
|
|
type SHost struct {
|
|
db.SEnabledStatusStandaloneResourceBase
|
|
SInfrastructure
|
|
SManagedResourceBase
|
|
|
|
Rack string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
|
|
Slots string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
|
|
|
|
AccessMac string `width:"32" charset:"ascii" nullable:"false" index:"true" list:"admin" update:"admin" create:"admin_required"` // Column(VARCHAR(32, charset='ascii'), nullable=False, index=True)
|
|
AccessIp string `width:"16" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
|
|
ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(256, charset='ascii'), nullable=True)
|
|
|
|
SysInfo jsonutils.JSONObject `nullable:"true" search:"admin" get:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True)
|
|
SN string `width:"128" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(128, charset='ascii'), nullable=True)
|
|
|
|
CpuCount int8 `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(TINYINT, nullable=True) # cpu count
|
|
NodeCount int8 `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(TINYINT, nullable=True)
|
|
CpuDesc string `width:"64" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
|
|
CpuMhz int `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # cpu MHz
|
|
CpuCache int `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # cpu Cache in KB
|
|
CpuReserved int8 `nullable:"true" default:"0" list:"admin" update:"admin" create:"admin_optional"` // Column(TINYINT, nullable=True, default=0)
|
|
CpuCmtbound float32 `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // = Column(Float, nullable=True)
|
|
|
|
MemSize int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # memory size in MB
|
|
MemReserved int `nullable:"true" default:"0" list:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True, default=0) # memory reserved in MB
|
|
MemCmtbound float32 `nullable:"true" update:"admin" create:"admin_optional"` // = Column(Float, nullable=True)
|
|
|
|
StorageSize int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(Integer, nullable=True) # storage size in MB
|
|
StorageType string `width:"20" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(20, charset='ascii'), nullable=True)
|
|
StorageDriver string `width:"20" charset:"ascii" nullable:"true" update:"admin" create:"admin_optional"` // Column(VARCHAR(20, charset='ascii'), nullable=True)
|
|
StorageInfo jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True)
|
|
|
|
IpmiInfo jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True)
|
|
|
|
// Status string = Column(VARCHAR(16, charset='ascii'), nullable=False, default=baremetalstatus.INIT) # status
|
|
HostStatus string `width:"16" charset:"ascii" nullable:"false" default:"offline" list:"admin"` // Column(VARCHAR(16, charset='ascii'), nullable=False, server_default=HOST_OFFLINE, default=HOST_OFFLINE)
|
|
|
|
ZoneId string `width:"128" charset:"ascii" nullable:"false" list:"admin" create:"admin_optional"` // Column(VARCHAR(ID_LENGTH, charset='ascii'), nullable=False)
|
|
|
|
HostType string `width:"36" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` // Column(VARCHAR(36, charset='ascii'), nullable=False)
|
|
|
|
Version string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(64, charset='ascii'))
|
|
|
|
IsBaremetal bool `nullable:"true" default:"false" list:"admin" update:"true" create:"admin_optional"` // Column(Boolean, nullable=True, default=False)
|
|
|
|
IsMaintenance bool `nullable:"true" default:"false" list:"admin"` // Column(Boolean, nullable=True, default=False)
|
|
|
|
LastPingAt time.Time ``
|
|
}
|
|
|
|
func (manager *SHostManager) GetContextManager() []db.IModelManager {
|
|
return []db.IModelManager{ZoneManager}
|
|
}
|
|
|
|
func (manager *SHostManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
|
// allow create host without zone_id
|
|
/*zoneId, err := data.GetString("zone_id")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
_, err = ZoneManager.FetchById(zoneId)
|
|
if err != nil {
|
|
return false
|
|
}*/
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionCreate)
|
|
}
|
|
|
|
func (manager *SHostManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
|
q, err := manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
anyMac, _ := query.GetString("any_mac")
|
|
if len(anyMac) > 0 {
|
|
anyMac := netutils.FormatMacAddr(anyMac)
|
|
if len(anyMac) == 0 {
|
|
return nil, httperrors.NewInputParameterError("invalid any_mac address")
|
|
}
|
|
netif, _ := NetInterfaceManager.FetchByMac(anyMac)
|
|
if netif != nil && len(netif.BaremetalId) > 0 {
|
|
q = q.Equals("id", netif.BaremetalId)
|
|
} else {
|
|
q = q.Equals("access_mac", anyMac)
|
|
}
|
|
}
|
|
// var scopeQuery *sqlchemy.SSubQuery
|
|
|
|
schedTagStr := jsonutils.GetAnyString(query, []string{"schedtag", "schedtag_id"})
|
|
if len(schedTagStr) > 0 {
|
|
schedTag, _ := SchedtagManager.FetchByIdOrName(nil, schedTagStr)
|
|
if schedTag == nil {
|
|
return nil, httperrors.NewResourceNotFoundError("Schedtag %s not found", schedTagStr)
|
|
}
|
|
hostschedtags := HostschedtagManager.Query().SubQuery()
|
|
scopeQuery := hostschedtags.Query(hostschedtags.Field("host_id")).Equals("schedtag_id", schedTag.GetId()).SubQuery()
|
|
q = q.In("id", scopeQuery)
|
|
}
|
|
|
|
wireStr := jsonutils.GetAnyString(query, []string{"wire", "wire_id"})
|
|
if len(wireStr) > 0 {
|
|
wire, _ := WireManager.FetchByIdOrName(nil, wireStr)
|
|
if wire == nil {
|
|
return nil, httperrors.NewResourceNotFoundError("Wire %s not found", wireStr)
|
|
}
|
|
hostwires := HostwireManager.Query().SubQuery()
|
|
scopeQuery := hostwires.Query(hostwires.Field("host_id")).Equals("wire_id", wire.GetId()).SubQuery()
|
|
q = q.In("id", scopeQuery)
|
|
}
|
|
|
|
storageStr := jsonutils.GetAnyString(query, []string{"storage", "storage_id"})
|
|
if len(storageStr) > 0 {
|
|
storage, _ := StorageManager.FetchByIdOrName(nil, storageStr)
|
|
if storage == nil {
|
|
return nil, httperrors.NewResourceNotFoundError("Storage %s not found", storageStr)
|
|
}
|
|
hoststorages := HoststorageManager.Query().SubQuery()
|
|
scopeQuery := hoststorages.Query(hoststorages.Field("host_id")).Equals("storage_id", storage.GetId()).SubQuery()
|
|
q = q.In("id", scopeQuery)
|
|
}
|
|
|
|
zoneStr := jsonutils.GetAnyString(query, []string{"zone", "zone_id"})
|
|
if len(zoneStr) > 0 {
|
|
zone, _ := ZoneManager.FetchByIdOrName(nil, zoneStr)
|
|
if zone == nil {
|
|
return nil, httperrors.NewResourceNotFoundError("Zone %s not found", zoneStr)
|
|
}
|
|
q = q.Filter(sqlchemy.Equals(q.Field("zone_id"), zone.GetId()))
|
|
}
|
|
// vcenter
|
|
// zone
|
|
// cachedimage
|
|
|
|
managerStr := jsonutils.GetAnyString(query, []string{"manager", "provider", "manager_id", "provider_id"})
|
|
if len(managerStr) > 0 {
|
|
provider := CloudproviderManager.FetchCloudproviderByIdOrName(managerStr)
|
|
if provider == nil {
|
|
return nil, httperrors.NewResourceNotFoundError("provider %s not found", managerStr)
|
|
}
|
|
q = q.Filter(sqlchemy.Equals(q.Field("manager_id"), provider.GetId()))
|
|
}
|
|
|
|
usable := jsonutils.QueryBoolean(query, "usable", false)
|
|
if usable {
|
|
hostwires := HostwireManager.Query().SubQuery()
|
|
networks := NetworkManager.Query().SubQuery()
|
|
|
|
hostQ := hostwires.Query(sqlchemy.DISTINCT("host_id", hostwires.Field("host_id")))
|
|
hostQ = hostQ.Join(networks, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
|
|
hostQ = hostQ.Filter(sqlchemy.Equals(networks.Field("status"), NETWORK_STATUS_AVAILABLE))
|
|
|
|
q = q.In("id", hostQ.SubQuery())
|
|
}
|
|
|
|
return q, nil
|
|
}
|
|
|
|
func (self *SHost) GetZone() *SZone {
|
|
if len(self.ZoneId) == 0 {
|
|
return nil
|
|
}
|
|
zone, _ := ZoneManager.FetchById(self.ZoneId)
|
|
if zone != nil {
|
|
return zone.(*SZone)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) GetRegion() *SCloudregion {
|
|
zone := self.GetZone()
|
|
if zone != nil {
|
|
return zone.GetRegion()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) GetCpuCount() int {
|
|
if self.CpuReserved > 0 && self.CpuReserved < self.CpuCount {
|
|
return int(self.CpuCount - self.CpuReserved)
|
|
} else {
|
|
return int(self.CpuCount)
|
|
}
|
|
}
|
|
|
|
func (self *SHost) GetMemSize() int {
|
|
if self.MemReserved > 0 && self.MemReserved < self.MemSize {
|
|
return self.MemSize - self.MemReserved
|
|
} else {
|
|
return self.MemSize
|
|
}
|
|
}
|
|
|
|
func (self *SHost) GetMemoryOvercommitBound() float32 {
|
|
if self.MemCmtbound > 0 {
|
|
return self.MemCmtbound
|
|
}
|
|
return options.Options.DefaultMemoryOvercommitBound
|
|
}
|
|
|
|
func (self *SHost) GetVirtualMemorySize() float32 {
|
|
return float32(self.GetMemSize()) * self.GetMemoryOvercommitBound()
|
|
}
|
|
|
|
func (self *SHost) GetCPUOvercommitBound() float32 {
|
|
if self.CpuCmtbound > 0 {
|
|
return self.CpuCmtbound
|
|
}
|
|
return options.Options.DefaultCPUOvercommitBound
|
|
}
|
|
|
|
func (self *SHost) GetVirtualCPUCount() float32 {
|
|
return float32(self.GetCpuCount()) * self.GetCPUOvercommitBound()
|
|
}
|
|
|
|
/*func (manager *SHostManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return userCred.IsSystemAdmin()
|
|
}
|
|
|
|
func (manager *SHostManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
|
return userCred.IsSystemAdmin()
|
|
}
|
|
|
|
func (self *SHost) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return userCred.IsSystemAdmin()
|
|
}
|
|
|
|
func (self *SHost) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
|
|
return userCred.IsSystemAdmin()
|
|
}
|
|
|
|
func (self *SHost) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
|
|
return userCred.IsSystemAdmin()
|
|
}*/
|
|
|
|
func (self *SHost) ValidateDeleteCondition(ctx context.Context) error {
|
|
if self.IsBaremetal && self.HostType != HOST_TYPE_BAREMETAL {
|
|
return httperrors.NewInvalidStatusError("Host is a converted baremetal, should be unconverted before delete")
|
|
}
|
|
if self.Enabled {
|
|
return httperrors.NewInvalidStatusError("Host is not disabled")
|
|
}
|
|
if self.GetGuestCount() > 0 {
|
|
return httperrors.NewNotEmptyError("Not an empty host")
|
|
}
|
|
for _, hoststorage := range self.GetHoststorages() {
|
|
storage := hoststorage.GetStorage()
|
|
if storage != nil && storage.IsLocal() && storage.GetDiskCount() > 0 {
|
|
return httperrors.NewNotEmptyError("Local host storage is not empty???")
|
|
}
|
|
}
|
|
return self.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx)
|
|
}
|
|
|
|
func (self *SHost) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
|
log.Infof("Host delete do nothing")
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
|
if self.IsBaremetal {
|
|
return self.StartDeleteBaremetalTask(ctx, userCred, "")
|
|
} else {
|
|
return self.RealDelete(ctx, userCred)
|
|
}
|
|
}
|
|
|
|
func (self *SHost) StartDeleteBaremetalTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalDeleteTask", self, userCred, nil, parentTaskId, "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
|
for _, hostschedtag := range self.GetHostschedtags() {
|
|
hostschedtag.Delete(ctx, userCred)
|
|
}
|
|
|
|
IsolatedDeviceManager.DeleteDevicesByHost(ctx, userCred, self)
|
|
|
|
for _, hoststorage := range self.GetHoststorages() {
|
|
storage := hoststorage.GetStorage()
|
|
if storage != nil && storage.IsLocal() && storage.GetDiskCount() > 0 {
|
|
return httperrors.NewNotEmptyError("Inconsistent: local storage is not empty???")
|
|
}
|
|
}
|
|
for _, hoststorage := range self.GetHoststorages() {
|
|
storage := hoststorage.GetStorage()
|
|
if storage != nil && storage.IsLocal() {
|
|
storage.Delete(ctx, userCred)
|
|
}
|
|
hoststorage.Delete(ctx, userCred)
|
|
}
|
|
for _, bn := range self.GetBaremetalnetworks() {
|
|
self.DeleteBaremetalnetwork(ctx, userCred, &bn, false)
|
|
}
|
|
for _, netif := range self.GetNetInterfaces() {
|
|
netif.Remove(ctx, userCred)
|
|
}
|
|
for _, hostwire := range self.GetHostwires() {
|
|
hostwire.Detach(ctx, userCred)
|
|
// hostwire.Delete(ctx, userCred) ???
|
|
}
|
|
baremetalStorage := self.GetBaremetalstorage()
|
|
if baremetalStorage != nil {
|
|
store := baremetalStorage.GetStorage()
|
|
baremetalStorage.Delete(ctx, userCred)
|
|
if store != nil {
|
|
store.Delete(ctx, userCred)
|
|
}
|
|
}
|
|
return self.SEnabledStatusStandaloneResourceBase.Delete(ctx, userCred)
|
|
}
|
|
|
|
func (self *SHost) GetHostschedtags() []SHostschedtag {
|
|
q := HostschedtagManager.Query().Equals("host_id", self.Id)
|
|
hostschedtags := make([]SHostschedtag, 0)
|
|
err := db.FetchModelObjects(HostschedtagManager, q, &hostschedtags)
|
|
if err != nil {
|
|
log.Errorf("GetHostschedtags error %s", err)
|
|
return nil
|
|
}
|
|
return hostschedtags
|
|
}
|
|
|
|
func (self *SHost) GetHoststoragesQuery() *sqlchemy.SQuery {
|
|
return HoststorageManager.Query().Equals("host_id", self.Id)
|
|
}
|
|
|
|
func (self *SHost) GetStorageCount() int {
|
|
return self.GetHoststoragesQuery().Count()
|
|
}
|
|
|
|
func (self *SHost) GetHoststorages() []SHoststorage {
|
|
hoststorages := make([]SHoststorage, 0)
|
|
q := self.GetHoststoragesQuery()
|
|
err := db.FetchModelObjects(HoststorageManager, q, &hoststorages)
|
|
if err != nil {
|
|
log.Errorf("GetHoststorages error %s", err)
|
|
return nil
|
|
}
|
|
return hoststorages
|
|
}
|
|
|
|
func (self *SHost) GetHoststorageOfId(storageId string) *SHoststorage {
|
|
hoststorage := SHoststorage{}
|
|
hoststorage.SetModelManager(HoststorageManager)
|
|
err := self.GetHoststoragesQuery().Equals("storage_id", storageId).First(&hoststorage)
|
|
if err != nil {
|
|
log.Errorf("GetHoststorageOfId fail %s", err)
|
|
return nil
|
|
}
|
|
return &hoststorage
|
|
}
|
|
|
|
func (self *SHost) GetBaremetalstorage() *SHoststorage {
|
|
if !self.IsBaremetal {
|
|
return nil
|
|
}
|
|
hoststorages := HoststorageManager.Query().SubQuery()
|
|
storages := StorageManager.Query().SubQuery()
|
|
q := hoststorages.Query()
|
|
q = q.Join(storages, sqlchemy.AND(sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")),
|
|
sqlchemy.IsFalse(storages.Field("deleted"))))
|
|
q = q.Filter(sqlchemy.Equals(storages.Field("storage_type"), STORAGE_BAREMETAL))
|
|
q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id))
|
|
if q.Count() == 1 {
|
|
hs := SHoststorage{}
|
|
hs.SetModelManager(HoststorageManager)
|
|
err := q.First(&hs)
|
|
if err != nil {
|
|
log.Errorf("error %s", err)
|
|
return nil
|
|
}
|
|
return &hs
|
|
}
|
|
log.Errorf("Cannof find baremetalstorage??")
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) GetFetchUrl() string {
|
|
managerUrl, err := url.Parse(self.ManagerUri)
|
|
if err != nil {
|
|
log.Errorf("GetFetchUrl fail to parse url: %s", err)
|
|
}
|
|
portStr := managerUrl.Port()
|
|
var port int
|
|
if len(portStr) > 0 {
|
|
port, _ = strconv.Atoi(portStr)
|
|
} else {
|
|
if managerUrl.Scheme == "https" {
|
|
port = 443
|
|
} else if managerUrl.Scheme == "http" {
|
|
port = 80
|
|
}
|
|
}
|
|
return fmt.Sprintf("%s://%s:%d", managerUrl.Scheme, managerUrl.Host, port+40000)
|
|
}
|
|
|
|
func (self *SHost) GetAttachedStorages(storageType string) []SStorage {
|
|
return self._getAttachedStorages(tristate.False, tristate.True, storageType)
|
|
}
|
|
|
|
func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled tristate.TriState, storageType string) []SStorage {
|
|
storages := StorageManager.Query().SubQuery()
|
|
hoststorages := HoststorageManager.Query().SubQuery()
|
|
q := storages.Query()
|
|
q = q.Join(hoststorages, sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")))
|
|
if enabled.IsTrue() {
|
|
q = q.IsTrue("enabled")
|
|
} else if enabled.IsFalse() {
|
|
q = q.IsFalse("enabled")
|
|
}
|
|
if isBaremetal.IsTrue() {
|
|
q = q.Equals("storage_type", STORAGE_BAREMETAL)
|
|
} else if isBaremetal.IsFalse() {
|
|
q = q.NotEquals("storage_type", STORAGE_BAREMETAL)
|
|
}
|
|
if len(storageType) > 0 {
|
|
q = q.Equals("storage_type", storageType)
|
|
}
|
|
q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id))
|
|
ret := make([]SStorage, 0)
|
|
err := db.FetchModelObjects(StorageManager, q, &ret)
|
|
if err != nil {
|
|
log.Errorf("GetAttachedStorages fail %s", err)
|
|
return nil
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (self *SHost) SyncAttachedStorageStatus() {
|
|
storages := self.GetAttachedStorages("")
|
|
if storages != nil {
|
|
for _, storage := range storages {
|
|
storage.SyncStatusWithHosts()
|
|
}
|
|
self.ClearSchedDescCache()
|
|
}
|
|
}
|
|
|
|
func (self *SHostManager) AllowGetPropertyBmStartRegisterScript(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return true
|
|
}
|
|
|
|
func (self *SHostManager) GetPropertyBmStartRegisterScript(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
regionUri, err := auth.GetServiceURL("compute", options.Options.Region, "", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var script string
|
|
script += fmt.Sprintf("curl -fsSL -H 'X-Auth-Token: %s' %s/misc/bm-prepare-script", userCred.GetTokenString(), regionUri)
|
|
res := jsonutils.NewDict()
|
|
res.Add(jsonutils.NewString(script), "script")
|
|
return res, nil
|
|
}
|
|
|
|
func (maanger *SHostManager) ClearAllSchedDescCache() error {
|
|
s := auth.GetAdminSession(options.Options.Region, "")
|
|
return modules.SchedManager.CleanCache(s, "")
|
|
}
|
|
|
|
func (maanger *SHostManager) ClearSchedDescCache(hostId string) error {
|
|
s := auth.GetAdminSession(options.Options.Region, "")
|
|
return modules.SchedManager.CleanCache(s, hostId)
|
|
}
|
|
|
|
func (self *SHost) ClearSchedDescCache() error {
|
|
return HostManager.ClearSchedDescCache(self.Id)
|
|
}
|
|
|
|
func (self *SHost) GetSpec(statusCheck bool) *jsonutils.JSONDict {
|
|
if statusCheck {
|
|
if utils.IsInStringArray(self.Status, []string{BAREMETAL_INIT, BAREMETAL_PREPARE_FAIL, BAREMETAL_PREPARE}) ||
|
|
self.GetBaremetalServer() != nil {
|
|
return nil
|
|
}
|
|
if self.MemSize == 0 || self.CpuCount == 0 {
|
|
return nil
|
|
}
|
|
}
|
|
spec := self.GetHardwareSpecification()
|
|
spec.Remove("storage_info")
|
|
nifs := self.GetNetInterfaces()
|
|
var nicCount int64
|
|
for _, nif := range nifs {
|
|
if nif.NicType != NIC_TYPE_IPMI {
|
|
nicCount++
|
|
}
|
|
}
|
|
spec.Set("nic_count", jsonutils.NewInt(nicCount))
|
|
manufacture, err := self.SysInfo.Get("manufacture")
|
|
if err != nil {
|
|
manufacture = jsonutils.NewString("Unknown")
|
|
}
|
|
spec.Set("manufacture", manufacture)
|
|
model, err := self.SysInfo.Get("model")
|
|
if err != nil {
|
|
model = jsonutils.NewString("Unknown")
|
|
}
|
|
spec.Set("model", model)
|
|
return spec
|
|
}
|
|
|
|
func (manager *SHostManager) GetSpecIdent(spec *jsonutils.JSONDict) []string {
|
|
nCpu, _ := spec.Int("cpu")
|
|
memSize, _ := spec.Int("mem")
|
|
memGB, err := utils.GetSizeGB(fmt.Sprintf("%d", memSize), "M")
|
|
if err != nil {
|
|
log.Errorf("Get mem size %d GB error: %v", memSize, err)
|
|
}
|
|
nicCount, _ := spec.Int("nic_count")
|
|
manufacture, _ := spec.GetString("manufacture")
|
|
model, _ := spec.GetString("model")
|
|
|
|
specKeys := []string{
|
|
fmt.Sprintf("cpu:%d", nCpu),
|
|
fmt.Sprintf("mem:%dG", memGB),
|
|
fmt.Sprintf("nic:%d", nicCount),
|
|
fmt.Sprintf("manufacture:%s", manufacture),
|
|
fmt.Sprintf("model:%s", model),
|
|
}
|
|
diskSpec, _ := spec.Get("disk")
|
|
if diskSpec != nil {
|
|
driverSpecs, _ := diskSpec.GetMap()
|
|
for driver, driverSpec := range driverSpecs {
|
|
specKeys = append(specKeys, parseDiskDriverSpec(driver, driverSpec)...)
|
|
}
|
|
}
|
|
sort.Strings(specKeys)
|
|
return specKeys
|
|
}
|
|
|
|
func parseDiskDriverSpec(driver string, spec jsonutils.JSONObject) []string {
|
|
ret := make([]string, 0)
|
|
adapterSpecs, _ := spec.GetMap()
|
|
for adapterKey, adapterSpec := range adapterSpecs {
|
|
for _, diskType := range []string{baremetal.HDD_DISK_SPEC_TYPE, baremetal.SSD_DISK_SPEC_TYPE} {
|
|
sizeCountMap, _ := adapterSpec.GetMap(diskType)
|
|
if sizeCountMap == nil {
|
|
continue
|
|
}
|
|
for size, count := range sizeCountMap {
|
|
sizeGB, _ := utils.GetSizeGB(size, "M")
|
|
diskKey := fmt.Sprintf("disk:%s_%s_%s_%dGx%s", driver, adapterKey, diskType, sizeGB, count)
|
|
ret = append(ret, diskKey)
|
|
}
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func ConvertStorageInfo2BaremetalStorages(storageInfo jsonutils.JSONObject) []*baremetal.BaremetalStorage {
|
|
storages := []baremetal.BaremetalStorage{}
|
|
err := storageInfo.Unmarshal(&storages)
|
|
if err != nil {
|
|
log.Errorf("Unmarshal to baremetal storage error: %v", err)
|
|
return nil
|
|
}
|
|
ret := make([]*baremetal.BaremetalStorage, len(storages))
|
|
for i := range storages {
|
|
ret[i] = &storages[i]
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func GetDiskSpecV2(storageInfo jsonutils.JSONObject) jsonutils.JSONObject {
|
|
refStorages := ConvertStorageInfo2BaremetalStorages(storageInfo)
|
|
if refStorages == nil {
|
|
return nil
|
|
}
|
|
diskSpec := baremetal.GetDiskSpecV2(refStorages)
|
|
return jsonutils.Marshal(diskSpec)
|
|
}
|
|
|
|
func (self *SHost) GetHardwareSpecification() *jsonutils.JSONDict {
|
|
spec := jsonutils.NewDict()
|
|
spec.Set("cpu", jsonutils.NewInt(int64(self.CpuCount)))
|
|
spec.Set("mem", jsonutils.NewInt(int64(self.MemSize)))
|
|
if self.StorageInfo != nil {
|
|
spec.Set("disk", GetDiskSpecV2(self.StorageInfo))
|
|
spec.Set("driver", jsonutils.NewString(self.StorageDriver))
|
|
spec.Set("storage_info", self.StorageInfo)
|
|
}
|
|
return spec
|
|
}
|
|
|
|
type SStorageCapacity struct {
|
|
Capacity int
|
|
Used int
|
|
Wasted int
|
|
VCapacity int
|
|
}
|
|
|
|
func (self *SHost) GetAttachedStorageCapacity() SStorageCapacity {
|
|
ret := SStorageCapacity{}
|
|
storages := self.GetAttachedStorages("")
|
|
if storages != nil {
|
|
for _, s := range storages {
|
|
ret.Capacity += s.GetCapacity()
|
|
ret.Used += s.GetUsedCapacity(tristate.True)
|
|
ret.Wasted += s.GetUsedCapacity(tristate.False)
|
|
ret.VCapacity += int(float32(s.GetCapacity()) * s.GetOvercommitBound())
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func _getLeastUsedStorage(storages []SStorage, backends []string) *SStorage {
|
|
var best *SStorage
|
|
var bestCap int
|
|
for i := 0; i < len(storages); i++ {
|
|
s := storages[i]
|
|
if len(backends) > 0 {
|
|
in, _ := utils.InStringArray(s.StorageType, backends)
|
|
if !in {
|
|
continue
|
|
}
|
|
}
|
|
capa := s.GetFreeCapacity()
|
|
if best == nil || bestCap < capa {
|
|
bestCap = capa
|
|
best = &s
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
func getLeastUsedStorage(storages []SStorage, backend string) *SStorage {
|
|
var backends []string
|
|
if backend == STORAGE_LOCAL {
|
|
backends = []string{STORAGE_NAS, STORAGE_LOCAL}
|
|
} else if len(backend) > 0 {
|
|
backends = []string{backend}
|
|
} else {
|
|
backends = []string{}
|
|
}
|
|
return _getLeastUsedStorage(storages, backends)
|
|
}
|
|
|
|
func (self *SHost) GetLeastUsedStorage(backend string) *SStorage {
|
|
storages := self.GetAttachedStorages("")
|
|
if storages != nil {
|
|
return getLeastUsedStorage(storages, backend)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) GetWiresQuery() *sqlchemy.SQuery {
|
|
return HostwireManager.Query().Equals("host_id", self.Id)
|
|
}
|
|
|
|
func (self *SHost) GetWireCount() int {
|
|
return self.GetWiresQuery().Count()
|
|
}
|
|
|
|
func (self *SHost) GetHostwires() []SHostwire {
|
|
hw := make([]SHostwire, 0)
|
|
q := self.GetWiresQuery()
|
|
err := db.FetchModelObjects(HostwireManager, q, &hw)
|
|
if err != nil {
|
|
log.Errorf("GetWires error %s", err)
|
|
return nil
|
|
}
|
|
return hw
|
|
}
|
|
|
|
func (self *SHost) getAttachedWires() []SWire {
|
|
wires := WireManager.Query().SubQuery()
|
|
hostwires := HostwireManager.Query().SubQuery()
|
|
q := wires.Query()
|
|
q = q.Join(hostwires, sqlchemy.AND(sqlchemy.IsFalse(hostwires.Field("deleted")),
|
|
sqlchemy.Equals(hostwires.Field("wire_id"), wires.Field("id"))))
|
|
q = q.Filter(sqlchemy.Equals(hostwires.Field("host_id"), self.Id))
|
|
ret := make([]SWire, 0)
|
|
err := db.FetchModelObjects(WireManager, q, &ret)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (self *SHost) GetMasterHostwire() *SHostwire {
|
|
hw := SHostwire{}
|
|
hw.SetModelManager(HostwireManager)
|
|
|
|
q := self.GetWiresQuery().IsTrue("is_master")
|
|
err := q.First(&hw)
|
|
if err != nil {
|
|
log.Errorf("GetMasterHostwire %s", err)
|
|
return nil
|
|
}
|
|
return &hw
|
|
}
|
|
|
|
func (self *SHost) GetMasterWire() *SWire {
|
|
wires := WireManager.Query().SubQuery()
|
|
hostwires := HostwireManager.Query().SubQuery()
|
|
q := wires.Query()
|
|
q = q.Join(hostwires, sqlchemy.AND(sqlchemy.Equals(hostwires.Field("wire_id"), wires.Field("id")),
|
|
sqlchemy.IsFalse(hostwires.Field("deleted"))))
|
|
q = q.Filter(sqlchemy.Equals(hostwires.Field("host_id"), self.Id))
|
|
q = q.Filter(sqlchemy.IsTrue(hostwires.Field("is_master")))
|
|
wire := SWire{}
|
|
wire.SetModelManager(WireManager)
|
|
|
|
err := q.First(&wire)
|
|
if err != nil {
|
|
log.Errorf("GetMasterWire fail %s", err)
|
|
return nil
|
|
}
|
|
return &wire
|
|
}
|
|
|
|
func (self *SHost) getHostwireOfId(wireId string) *SHostwire {
|
|
hostwire := SHostwire{}
|
|
hostwire.SetModelManager(HostwireManager)
|
|
|
|
q := self.GetWiresQuery().Equals("wire_id", wireId)
|
|
err := q.First(&hostwire)
|
|
if err != nil {
|
|
log.Errorf("getHostwireOfId fail %s", err)
|
|
return nil
|
|
}
|
|
return &hostwire
|
|
}
|
|
|
|
func (self *SHost) GetGuestsQuery() *sqlchemy.SQuery {
|
|
return GuestManager.Query().Equals("host_id", self.Id)
|
|
}
|
|
|
|
func (self *SHost) GetGuests() []SGuest {
|
|
q := self.GetGuestsQuery()
|
|
guests := make([]SGuest, 0)
|
|
err := db.FetchModelObjects(GuestManager, q, &guests)
|
|
if err != nil {
|
|
log.Errorf("GetGuests %s", err)
|
|
return nil
|
|
}
|
|
return guests
|
|
}
|
|
|
|
func (self *SHost) GetGuestCount() int {
|
|
q := self.GetGuestsQuery()
|
|
return q.Count()
|
|
}
|
|
|
|
func (self *SHost) GetNonsystemGuestCount() int {
|
|
q := self.GetGuestsQuery()
|
|
q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("is_system")), sqlchemy.IsFalse(q.Field("is_system"))))
|
|
return q.Count()
|
|
}
|
|
|
|
func (self *SHost) GetRunningGuestCount() int {
|
|
q := self.GetGuestsQuery()
|
|
q = q.In("status", VM_RUNNING_STATUS)
|
|
return q.Count()
|
|
}
|
|
|
|
func (self *SHost) GetBaremetalnetworksQuery() *sqlchemy.SQuery {
|
|
return HostnetworkManager.Query().Equals("baremetal_id", self.Id)
|
|
}
|
|
|
|
func (self *SHost) GetBaremetalnetworks() []SHostnetwork {
|
|
q := self.GetBaremetalnetworksQuery()
|
|
hns := make([]SHostnetwork, 0)
|
|
err := db.FetchModelObjects(HostnetworkManager, q, &hns)
|
|
if err != nil {
|
|
log.Errorf("GetBaremetalnetworks error: %s", err)
|
|
}
|
|
return hns
|
|
}
|
|
|
|
func (self *SHost) GetAttach2Network(network *SNetwork) *SHostnetwork {
|
|
q := self.GetBaremetalnetworksQuery()
|
|
q = q.Equals("network_id", network.Id)
|
|
hn := SHostnetwork{}
|
|
hn.SetModelManager(HostnetworkManager)
|
|
|
|
err := q.First(&hn)
|
|
if err != nil {
|
|
log.Errorf("GetAttach2Network fail %s", err)
|
|
return nil
|
|
}
|
|
return &hn
|
|
}
|
|
|
|
func (self *SHost) isAttach2Network(network *SNetwork) bool {
|
|
hn := self.GetAttach2Network(network)
|
|
return hn != nil
|
|
}
|
|
|
|
func (self *SHost) GetNetInterfaces() []SNetInterface {
|
|
q := NetInterfaceManager.Query().Equals("baremetal_id", self.Id).Asc("index")
|
|
netifs := make([]SNetInterface, 0)
|
|
err := db.FetchModelObjects(NetInterfaceManager, q, &netifs)
|
|
if err != nil {
|
|
log.Errorf("GetNetInterfaces fail %s", err)
|
|
return nil
|
|
}
|
|
return netifs
|
|
}
|
|
|
|
func (self *SHost) GetAdminNetInterface() *SNetInterface {
|
|
netif := SNetInterface{}
|
|
netif.SetModelManager(NetInterfaceManager)
|
|
|
|
q := NetInterfaceManager.Query().Equals("baremetal_id", self.Id).Equals("nic_type", NIC_TYPE_ADMIN)
|
|
err := q.First(&netif)
|
|
if err != nil {
|
|
log.Errorf("GetAdminNetInterface fail %s", err)
|
|
return nil
|
|
}
|
|
return &netif
|
|
}
|
|
|
|
func (self *SHost) GetNetInterface(mac string) *SNetInterface {
|
|
netif, _ := NetInterfaceManager.FetchByMac(mac)
|
|
if netif != nil && netif.BaremetalId == self.Id {
|
|
return netif
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) DeleteBaremetalnetwork(ctx context.Context, userCred mcclient.TokenCredential, bn *SHostnetwork, reserve bool) {
|
|
net := bn.GetNetwork()
|
|
bn.Delete(ctx, userCred)
|
|
db.OpsLog.LogDetachEvent(self, net, userCred, nil)
|
|
if reserve && net != nil && len(bn.IpAddr) > 0 && regutils.MatchIP4Addr(bn.IpAddr) {
|
|
ReservedipManager.ReserveIP(userCred, net, bn.IpAddr, "Delete baremetalnetwork to reserve")
|
|
}
|
|
}
|
|
|
|
func (self *SHost) GetHostDriver() IHostDriver {
|
|
if !utils.IsInStringArray(self.HostType, HOST_TYPES) {
|
|
log.Fatalf("Unsupported host type %s", self.HostType)
|
|
}
|
|
return GetHostDriver(self.HostType)
|
|
}
|
|
|
|
func (manager *SHostManager) getHostsByZone(zone *SZone, provider *SCloudprovider) ([]SHost, error) {
|
|
hosts := make([]SHost, 0)
|
|
q := manager.Query().Equals("zone_id", zone.Id)
|
|
if provider != nil {
|
|
q = q.Equals("manager_id", provider.Id)
|
|
}
|
|
err := db.FetchModelObjects(manager, q, &hosts)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil, err
|
|
}
|
|
return hosts, nil
|
|
}
|
|
|
|
func (manager *SHostManager) SyncHosts(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, zone *SZone, hosts []cloudprovider.ICloudHost, projectSync bool) ([]SHost, []cloudprovider.ICloudHost, compare.SyncResult) {
|
|
localHosts := make([]SHost, 0)
|
|
remoteHosts := make([]cloudprovider.ICloudHost, 0)
|
|
syncResult := compare.SyncResult{}
|
|
|
|
dbHosts, err := manager.getHostsByZone(zone, provider)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return nil, nil, syncResult
|
|
}
|
|
|
|
removed := make([]SHost, 0)
|
|
commondb := make([]SHost, 0)
|
|
commonext := make([]cloudprovider.ICloudHost, 0)
|
|
added := make([]cloudprovider.ICloudHost, 0)
|
|
|
|
err = compare.CompareSets(dbHosts, hosts, &removed, &commondb, &commonext, &added)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return nil, nil, syncResult
|
|
}
|
|
|
|
for i := 0; i < len(removed); i += 1 {
|
|
err = removed[i].ValidateDeleteCondition(ctx)
|
|
if err != nil { // cannot delete
|
|
err = removed[i].SetStatus(userCred, HOST_OFFLINE, "sync to delete")
|
|
if err == nil {
|
|
_, err = removed[i].PerformDisable(ctx, userCred, nil, nil)
|
|
}
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
} else {
|
|
err = removed[i].Delete(ctx, userCred)
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}
|
|
}
|
|
for i := 0; i < len(commondb); i += 1 {
|
|
err = commondb[i].syncWithCloudHost(commonext[i], projectSync)
|
|
if err != nil {
|
|
syncResult.UpdateError(err)
|
|
} else {
|
|
localHosts = append(localHosts, commondb[i])
|
|
remoteHosts = append(remoteHosts, commonext[i])
|
|
syncResult.Update()
|
|
}
|
|
}
|
|
for i := 0; i < len(added); i += 1 {
|
|
new, err := manager.newFromCloudHost(added[i], zone)
|
|
if err != nil {
|
|
syncResult.AddError(err)
|
|
} else {
|
|
localHosts = append(localHosts, *new)
|
|
remoteHosts = append(remoteHosts, added[i])
|
|
syncResult.Add()
|
|
}
|
|
}
|
|
|
|
return localHosts, remoteHosts, syncResult
|
|
}
|
|
|
|
func (self *SHost) syncWithCloudHost(extHost cloudprovider.ICloudHost, projectSync bool) error {
|
|
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
|
|
self.Name = extHost.GetName()
|
|
self.Status = extHost.GetStatus()
|
|
self.HostStatus = extHost.GetHostStatus()
|
|
self.AccessIp = extHost.GetAccessIp()
|
|
self.AccessMac = extHost.GetAccessMac()
|
|
self.SN = extHost.GetSN()
|
|
self.SysInfo = extHost.GetSysInfo()
|
|
self.CpuCount = extHost.GetCpuCount()
|
|
self.NodeCount = extHost.GetNodeCount()
|
|
self.CpuDesc = extHost.GetCpuDesc()
|
|
self.CpuMhz = extHost.GetCpuMhz()
|
|
self.MemSize = extHost.GetMemSizeMB()
|
|
self.StorageSize = extHost.GetStorageSizeMB()
|
|
self.StorageType = extHost.GetStorageType()
|
|
self.HostType = extHost.GetHostType()
|
|
|
|
self.ManagerId = extHost.GetManagerId()
|
|
self.IsEmulated = extHost.IsEmulated()
|
|
self.Enabled = extHost.GetEnabled()
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Errorf("syncWithCloudZone error %s", err)
|
|
}
|
|
|
|
if projectSync {
|
|
if err := HostManager.ClearSchedDescCache(self.Id); err != nil {
|
|
log.Errorf("ClearSchedDescCache for host %s error %v", self.Name, err)
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (manager *SHostManager) newFromCloudHost(extHost cloudprovider.ICloudHost, zone *SZone) (*SHost, error) {
|
|
host := SHost{}
|
|
host.SetModelManager(manager)
|
|
|
|
host.Name = extHost.GetName()
|
|
host.ExternalId = extHost.GetGlobalId()
|
|
host.ZoneId = zone.Id
|
|
// host.ManagerId = extHost.GetManagerId()
|
|
host.HostType = extHost.GetHostType()
|
|
|
|
host.Status = extHost.GetStatus()
|
|
host.HostStatus = extHost.GetHostStatus()
|
|
host.Enabled = extHost.GetEnabled()
|
|
|
|
host.AccessIp = extHost.GetAccessIp()
|
|
host.AccessMac = extHost.GetAccessMac()
|
|
host.SN = extHost.GetSN()
|
|
host.SysInfo = extHost.GetSysInfo()
|
|
host.CpuCount = extHost.GetCpuCount()
|
|
host.NodeCount = extHost.GetNodeCount()
|
|
host.CpuDesc = extHost.GetCpuDesc()
|
|
host.CpuMhz = extHost.GetCpuMhz()
|
|
host.MemSize = extHost.GetMemSizeMB()
|
|
host.StorageSize = extHost.GetStorageSizeMB()
|
|
host.StorageType = extHost.GetStorageType()
|
|
|
|
host.ManagerId = extHost.GetManagerId()
|
|
host.IsEmulated = extHost.IsEmulated()
|
|
|
|
err := manager.TableSpec().Insert(&host)
|
|
if err != nil {
|
|
log.Errorf("newFromCloudHost fail %s", err)
|
|
return nil, err
|
|
}
|
|
|
|
if err := manager.ClearSchedDescCache(host.Id); err != nil {
|
|
log.Errorf("ClearSchedDescCache for host %s error %v", host.Name, err)
|
|
}
|
|
|
|
return &host, nil
|
|
}
|
|
|
|
func (self *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.TokenCredential, storages []cloudprovider.ICloudStorage) compare.SyncResult {
|
|
syncResult := compare.SyncResult{}
|
|
|
|
dbStorages := make([]SStorage, 0)
|
|
|
|
hostStorages := self.GetHoststorages()
|
|
for i := 0; i < len(hostStorages); i += 1 {
|
|
storage := hostStorages[i].GetStorage()
|
|
if storage == nil {
|
|
hostStorages[i].Delete(ctx, userCred)
|
|
} else {
|
|
dbStorages = append(dbStorages, *storage)
|
|
}
|
|
}
|
|
|
|
// dbStorages := self._getAttachedStorages(tristate.None, tristate.None)
|
|
|
|
removed := make([]SStorage, 0)
|
|
commondb := make([]SStorage, 0)
|
|
commonext := make([]cloudprovider.ICloudStorage, 0)
|
|
added := make([]cloudprovider.ICloudStorage, 0)
|
|
|
|
err := compare.CompareSets(dbStorages, storages, &removed, &commondb, &commonext, &added)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return syncResult
|
|
}
|
|
|
|
for i := 0; i < len(removed); i += 1 {
|
|
log.Infof("host %s not connected with %s any more, to detach...", self.Id, removed[i].Id)
|
|
hs := self.GetHoststorageOfId(removed[i].Id)
|
|
err := hs.Detach(ctx, userCred)
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(commondb); i += 1 {
|
|
log.Infof("host %s is still connected with %s, to update ...", self.Id, commondb[i].Id)
|
|
err := self.syncWithCloudHostStorage(commonext[i])
|
|
if err != nil {
|
|
syncResult.UpdateError(err)
|
|
} else {
|
|
syncResult.Update()
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(added); i += 1 {
|
|
log.Infof("host %s is found connected with %s, to add ...", self.Id, added[i].GetId())
|
|
err := self.newCloudHostStorage(ctx, userCred, added[i])
|
|
if err != nil {
|
|
syncResult.AddError(err)
|
|
} else {
|
|
syncResult.Add()
|
|
}
|
|
}
|
|
return syncResult
|
|
}
|
|
|
|
func (self *SHost) syncWithCloudHostStorage(extStorage cloudprovider.ICloudStorage) error {
|
|
// do nothing
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) Attach2Storage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, mountPoint string) error {
|
|
hs := SHoststorage{}
|
|
hs.SetModelManager(HoststorageManager)
|
|
|
|
hs.StorageId = storage.Id
|
|
hs.HostId = self.Id
|
|
hs.MountPoint = mountPoint
|
|
err := HoststorageManager.TableSpec().Insert(&hs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db.OpsLog.LogAttachEvent(self, storage, userCred, nil)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) newCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage) error {
|
|
storageObj, err := StorageManager.FetchByExternalId(extStorage.GetGlobalId())
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
storage := storageObj.(*SStorage)
|
|
err = self.Attach2Storage(ctx, userCred, storage, "")
|
|
return err
|
|
}
|
|
|
|
func (self *SHost) SyncHostWires(ctx context.Context, userCred mcclient.TokenCredential, wires []cloudprovider.ICloudWire) compare.SyncResult {
|
|
syncResult := compare.SyncResult{}
|
|
|
|
dbWires := make([]SWire, 0)
|
|
|
|
hostWires := self.GetHostwires()
|
|
for i := 0; i < len(hostWires); i += 1 {
|
|
wire := hostWires[i].GetWire()
|
|
if wire == nil {
|
|
hostWires[i].Delete(ctx, userCred)
|
|
} else {
|
|
dbWires = append(dbWires, *wire)
|
|
}
|
|
}
|
|
|
|
// dbWires := self.getAttachedWires()
|
|
|
|
removed := make([]SWire, 0)
|
|
commondb := make([]SWire, 0)
|
|
commonext := make([]cloudprovider.ICloudWire, 0)
|
|
added := make([]cloudprovider.ICloudWire, 0)
|
|
|
|
err := compare.CompareSets(dbWires, wires, &removed, &commondb, &commonext, &added)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return syncResult
|
|
}
|
|
|
|
for i := 0; i < len(removed); i += 1 {
|
|
log.Infof("host %s not connected with %s any more, to detach...", self.Id, removed[i].Id)
|
|
hw := self.getHostwireOfId(removed[i].Id)
|
|
err := hw.Detach(ctx, userCred)
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(commondb); i += 1 {
|
|
log.Infof("host %s is still connected with %s, to update...", self.Id, commondb[i].Id)
|
|
err := self.syncWithCloudHostWire(commonext[i])
|
|
if err != nil {
|
|
syncResult.UpdateError(err)
|
|
} else {
|
|
syncResult.Update()
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(added); i += 1 {
|
|
log.Infof("host %s is found connected with %s, to add...", self.Id, added[i].GetId())
|
|
err := self.newCloudHostWire(ctx, userCred, added[i])
|
|
if err != nil {
|
|
syncResult.AddError(err)
|
|
} else {
|
|
syncResult.Add()
|
|
}
|
|
}
|
|
|
|
return syncResult
|
|
}
|
|
|
|
func (self *SHost) syncWithCloudHostWire(extWire cloudprovider.ICloudWire) error {
|
|
// do nothing
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) Attach2Wire(ctx context.Context, userCred mcclient.TokenCredential, wire *SWire) error {
|
|
hs := SHostwire{}
|
|
hs.SetModelManager(HostwireManager)
|
|
|
|
hs.WireId = wire.Id
|
|
hs.HostId = self.Id
|
|
err := HostwireManager.TableSpec().Insert(&hs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db.OpsLog.LogAttachEvent(self, wire, userCred, nil)
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) newCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire) error {
|
|
wireObj, err := WireManager.FetchByExternalId(extWire.GetGlobalId())
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
wire := wireObj.(*SWire)
|
|
err = self.Attach2Wire(ctx, userCred, wire)
|
|
return err
|
|
}
|
|
|
|
func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCredential, vms []cloudprovider.ICloudVM, projectId string, projectSync bool) ([]SGuest, []cloudprovider.ICloudVM, compare.SyncResult) {
|
|
localVMs := make([]SGuest, 0)
|
|
remoteVMs := make([]cloudprovider.ICloudVM, 0)
|
|
syncResult := compare.SyncResult{}
|
|
|
|
dbVMs := self.GetGuests()
|
|
|
|
removed := make([]SGuest, 0)
|
|
commondb := make([]SGuest, 0)
|
|
commonext := make([]cloudprovider.ICloudVM, 0)
|
|
added := make([]cloudprovider.ICloudVM, 0)
|
|
|
|
err := compare.CompareSets(dbVMs, vms, &removed, &commondb, &commonext, &added)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return nil, nil, syncResult
|
|
}
|
|
|
|
for i := 0; i < len(removed); i += 1 {
|
|
err := removed[i].SetStatus(userCred, VM_UNKNOWN, "Sync lost")
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(commondb); i += 1 {
|
|
err := commondb[i].syncWithCloudVM(ctx, userCred, self, commonext[i], projectId, projectSync)
|
|
if err != nil {
|
|
syncResult.UpdateError(err)
|
|
} else {
|
|
localVMs = append(localVMs, commondb[i])
|
|
remoteVMs = append(remoteVMs, commonext[i])
|
|
syncResult.Update()
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(added); i += 1 {
|
|
new, err := GuestManager.newCloudVM(ctx, userCred, self, added[i], projectId)
|
|
if err != nil {
|
|
syncResult.AddError(err)
|
|
} else {
|
|
localVMs = append(localVMs, *new)
|
|
remoteVMs = append(remoteVMs, added[i])
|
|
syncResult.Add()
|
|
}
|
|
}
|
|
|
|
return localVMs, remoteVMs, syncResult
|
|
}
|
|
|
|
func (self *SHost) getNetworkOfIPOnHost(ipAddr string) (*SNetwork, error) {
|
|
net, err := NetworkManager.GetNetworkOfIP(ipAddr, "", tristate.None)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hw := self.getHostwireOfId(net.WireId)
|
|
if hw == nil {
|
|
return nil, fmt.Errorf("IP %s not reachable on this host", ipAddr)
|
|
}
|
|
return net, nil
|
|
}
|
|
|
|
func (self *SHost) GetNetinterfaceWithNetworkAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetInterface, *SNetwork) {
|
|
netif, net := self.getNetifWithNetworkAndCredential(netId, userCred, true, reserved)
|
|
if netif != nil {
|
|
return netif, net
|
|
}
|
|
return self.getNetifWithNetworkAndCredential(netId, userCred, false, reserved)
|
|
}
|
|
|
|
func (self *SHost) getNetifWithNetworkAndCredential(netId string, userCred mcclient.TokenCredential, isPublic bool, reserved bool) (*SNetInterface, *SNetwork) {
|
|
netifs := self.GetNetInterfaces()
|
|
var maxFreeCnt = 0
|
|
var maxFreeNet *SNetwork
|
|
var maxFreeNetif *SNetInterface
|
|
for i := 0; i < len(netifs); i++ {
|
|
if !netifs[i].IsUsableServernic() {
|
|
continue
|
|
}
|
|
wire := netifs[i].GetWire()
|
|
if wire != nil {
|
|
if isPublic {
|
|
nets, _ := wire.getPublicNetworks()
|
|
for _, net := range nets {
|
|
if net.Id == netId || net.GetName() == netId {
|
|
freeCnt := net.getFreeAddressCount()
|
|
if maxFreeNet == nil || maxFreeCnt < freeCnt {
|
|
maxFreeNetif = &netifs[i]
|
|
maxFreeNet = &net
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
nets, _ := wire.getPrivateNetworks(userCred)
|
|
for _, net := range nets {
|
|
if net.Id == netId || net.GetName() == netId {
|
|
freeCnt := net.getFreeAddressCount()
|
|
if maxFreeNet == nil || maxFreeCnt < freeCnt {
|
|
maxFreeNetif = &netifs[i]
|
|
maxFreeNet = &net
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return maxFreeNetif, maxFreeNet
|
|
}
|
|
|
|
func (self *SHost) GetNetworkWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetwork, error) {
|
|
net, err := self.getNetworkWithIdAndCredential(netId, userCred, true, reserved)
|
|
if err == nil {
|
|
return net, nil
|
|
}
|
|
return self.getNetworkWithIdAndCredential(netId, userCred, false, reserved)
|
|
}
|
|
|
|
func (self *SHost) getNetworkWithIdAndCredential(netId string, userCred mcclient.TokenCredential, isPublic bool, reserved bool) (*SNetwork, error) {
|
|
networks := NetworkManager.Query().SubQuery()
|
|
hostwires := HostwireManager.Query().SubQuery()
|
|
hosts := HostManager.Query().SubQuery()
|
|
|
|
q := networks.Query()
|
|
q = q.Join(hostwires, sqlchemy.AND(sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")),
|
|
sqlchemy.IsFalse(hostwires.Field("deleted"))))
|
|
q = q.Join(hosts, sqlchemy.AND(sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")),
|
|
sqlchemy.IsFalse(hosts.Field("deleted"))))
|
|
q = q.Filter(sqlchemy.Equals(hosts.Field("id"), self.Id))
|
|
q = q.Filter(sqlchemy.OR(sqlchemy.Equals(networks.Field("id"), netId),
|
|
sqlchemy.Equals(networks.Field("name"), netId)))
|
|
if isPublic {
|
|
q = q.Filter(sqlchemy.IsTrue(networks.Field("is_public")))
|
|
} else {
|
|
q = q.Filter(sqlchemy.Equals(networks.Field("tenant_id"), userCred.GetProjectId()))
|
|
}
|
|
|
|
nets := make([]SNetwork, 0)
|
|
err := db.FetchModelObjects(NetworkManager, q, &nets)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var maxFreeNet *SNetwork
|
|
maxFrees := 0
|
|
for i := 0; i < len(nets); i += 1 {
|
|
freeCnt := nets[i].getFreeAddressCount()
|
|
if maxFreeNet == nil || maxFrees < freeCnt {
|
|
maxFrees = freeCnt
|
|
maxFreeNet = &nets[i]
|
|
}
|
|
}
|
|
if reserved || maxFrees > 0 {
|
|
return maxFreeNet, nil
|
|
}
|
|
return nil, fmt.Errorf("No IP address")
|
|
}
|
|
|
|
func (manager *SHostManager) FetchHostById(hostId string) *SHost {
|
|
host := SHost{}
|
|
host.SetModelManager(manager)
|
|
err := manager.Query().Equals("id", hostId).First(&host)
|
|
if err != nil {
|
|
log.Errorf("fetchHostById fail %s", err)
|
|
return nil
|
|
} else {
|
|
return &host
|
|
}
|
|
}
|
|
|
|
func (manager *SHostManager) totalCountQ(
|
|
userCred mcclient.TokenCredential,
|
|
rangeObj db.IStandaloneModel,
|
|
hostStatus, status string,
|
|
hostTypes []string,
|
|
enabled, isBaremetal tristate.TriState,
|
|
) *sqlchemy.SQuery {
|
|
hosts := manager.Query().SubQuery()
|
|
q := hosts.Query()
|
|
if len(status) > 0 {
|
|
q = q.Filter(sqlchemy.Equals(hosts.Field("status"), status))
|
|
}
|
|
if len(hostStatus) > 0 {
|
|
q = q.Filter(sqlchemy.Equals(hosts.Field("host_status"), hostStatus))
|
|
}
|
|
if !enabled.IsNone() {
|
|
cond := sqlchemy.IsFalse
|
|
if enabled.Bool() {
|
|
cond = sqlchemy.IsTrue
|
|
}
|
|
q = q.Filter(cond(hosts.Field("enabled")))
|
|
}
|
|
if !isBaremetal.IsNone() {
|
|
cond := sqlchemy.IsFalse
|
|
if isBaremetal.Bool() {
|
|
cond = sqlchemy.IsTrue
|
|
}
|
|
q = q.Filter(cond(hosts.Field("is_baremetal")))
|
|
}
|
|
q = AttachUsageQuery(q, hosts, hosts.Field("id"), hostTypes, rangeObj)
|
|
return q
|
|
}
|
|
|
|
type HostStat struct {
|
|
MemSize int
|
|
MemReserved int
|
|
MemCmtbound float32
|
|
CpuCount int8
|
|
CpuReserved int8
|
|
CpuCmtbound float32
|
|
StorageSize int
|
|
}
|
|
|
|
type HostsCountStat struct {
|
|
StorageSize int64
|
|
Count int64
|
|
Memory int64
|
|
MemoryVirtual float64
|
|
CPU int64
|
|
CPUVirtual float64
|
|
}
|
|
|
|
func (manager *SHostManager) calculateCount(q *sqlchemy.SQuery) HostsCountStat {
|
|
usableSize := func(act, reserved int) int {
|
|
aSize := 0
|
|
if reserved > 0 && reserved < act {
|
|
aSize = act - reserved
|
|
} else {
|
|
aSize = act
|
|
}
|
|
return aSize
|
|
}
|
|
var (
|
|
tStore int64 = 0
|
|
tCnt int64 = 0
|
|
tMem int64 = 0
|
|
tVmem float64 = 0.0
|
|
tCPU int64 = 0
|
|
tVCPU float64 = 0.0
|
|
)
|
|
stats := make([]HostStat, 0)
|
|
err := q.All(&stats)
|
|
if err != nil {
|
|
log.Errorf("%v", err)
|
|
}
|
|
for _, stat := range stats {
|
|
if stat.MemSize == 0 {
|
|
continue
|
|
}
|
|
tCnt += 1
|
|
if stat.StorageSize > 0 {
|
|
tStore += int64(stat.StorageSize)
|
|
}
|
|
aMem := usableSize(stat.MemSize, stat.MemReserved)
|
|
aCpu := usableSize(int(stat.CpuCount), int(stat.CpuReserved))
|
|
tMem += int64(aMem)
|
|
tCPU += int64(aCpu)
|
|
if stat.MemCmtbound <= 0.0 {
|
|
stat.MemCmtbound = options.Options.DefaultMemoryOvercommitBound
|
|
}
|
|
if stat.CpuCmtbound <= 0.0 {
|
|
stat.CpuCmtbound = options.Options.DefaultCPUOvercommitBound
|
|
}
|
|
tVmem += float64(float32(aMem) * stat.MemCmtbound)
|
|
tVCPU += float64(float32(aCpu) * stat.CpuCmtbound)
|
|
}
|
|
return HostsCountStat{
|
|
StorageSize: tStore,
|
|
Count: tCnt,
|
|
Memory: tMem,
|
|
MemoryVirtual: tVmem,
|
|
CPU: tCPU,
|
|
CPUVirtual: tVCPU,
|
|
}
|
|
}
|
|
|
|
func (manager *SHostManager) totalCount(
|
|
userCred mcclient.TokenCredential,
|
|
rangeObj db.IStandaloneModel,
|
|
hostStatus, status string,
|
|
hostTypes []string,
|
|
enabled, isBaremetal tristate.TriState,
|
|
) HostsCountStat {
|
|
return manager.calculateCount(manager.totalCountQ(userCred, rangeObj, hostStatus, status, hostTypes, enabled, isBaremetal))
|
|
}
|
|
|
|
func (manager *SHostManager) TotalCount(
|
|
userCred mcclient.TokenCredential,
|
|
rangeObj db.IStandaloneModel,
|
|
hostStatus, status string,
|
|
hostTypes []string,
|
|
enabled, isBaremetal tristate.TriState,
|
|
) HostsCountStat {
|
|
stat1 := manager.totalCount(userCred, rangeObj, hostStatus, status, hostTypes, enabled, isBaremetal)
|
|
return stat1
|
|
}
|
|
|
|
/*
|
|
func (self *SHost) GetIZone() (cloudprovider.ICloudZone, error) {
|
|
provider, err := self.GetCloudProvider()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("No cloudprovide for host: %s", err)
|
|
}
|
|
zone := self.GetZone()
|
|
if zone == nil {
|
|
return nil, fmt.Errorf("no zone for host???")
|
|
}
|
|
region := zone.GetRegion()
|
|
if region == nil {
|
|
return nil, fmt.Errorf("No region for zone???")
|
|
}
|
|
iregion, err := provider.GetIRegionById(region.ExternalId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fail to find iregion by id %s", err)
|
|
}
|
|
izone, err := iregion.GetIZoneById(zone.ExternalId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fail to find izone by id %s", err)
|
|
}
|
|
return izone, nil
|
|
}
|
|
*/
|
|
|
|
func (self *SHost) GetIHost() (cloudprovider.ICloudHost, error) {
|
|
provider, err := self.GetDriver()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("No cloudprovide for host: %s", err)
|
|
}
|
|
ihost, err := provider.GetIHostById(self.ExternalId)
|
|
if err != nil {
|
|
log.Errorf("fail to find ihost by id %s %s", self.ExternalId, err)
|
|
return nil, fmt.Errorf("fail to find ihost by id %s", err)
|
|
}
|
|
return ihost, nil
|
|
}
|
|
|
|
func (self *SHost) GetIRegion() (cloudprovider.ICloudRegion, error) {
|
|
provider, err := self.GetDriver()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("No cloudprovide for host %s: %s", self.Name, err)
|
|
}
|
|
region := self.GetRegion()
|
|
if region == nil {
|
|
return nil, fmt.Errorf("failed to find host %s region info", self.Name)
|
|
}
|
|
iregion, err := provider.GetIRegionById(region.ExternalId)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("fail to find iregion by id %s: %v", region.ExternalId, err)
|
|
return nil, fmt.Errorf(msg)
|
|
}
|
|
return iregion, nil
|
|
}
|
|
|
|
func (self *SHost) getDiskConfig() jsonutils.JSONObject {
|
|
bs := self.GetBaremetalstorage()
|
|
if bs != nil {
|
|
return bs.Config
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) GetBaremetalServer() *SGuest {
|
|
if !self.IsBaremetal {
|
|
return nil
|
|
}
|
|
guestObj, err := db.NewModelObject(GuestManager)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
q := GuestManager.Query().Equals("host_id", self.Id).Equals("hypervisor", HOST_TYPE_BAREMETAL)
|
|
err = q.First(guestObj)
|
|
if err != nil {
|
|
log.Errorf("query fail %s", err)
|
|
return nil
|
|
}
|
|
return guestObj.(*SGuest)
|
|
}
|
|
|
|
func (self *SHost) getSchedtags() []SSchedtag {
|
|
tags := make([]SSchedtag, 0)
|
|
schedtags := SchedtagManager.Query().SubQuery()
|
|
hostschedtags := HostschedtagManager.Query().SubQuery()
|
|
q := schedtags.Query()
|
|
q = q.Join(hostschedtags, sqlchemy.AND(sqlchemy.Equals(hostschedtags.Field("schedtag_id"), schedtags.Field("id")),
|
|
sqlchemy.IsFalse(hostschedtags.Field("deleted"))))
|
|
q = q.Filter(sqlchemy.Equals(hostschedtags.Field("host_id"), self.Id))
|
|
err := q.All(&tags)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
return tags
|
|
}
|
|
|
|
type SHostGuestResourceUsage struct {
|
|
GuestCount int
|
|
GuestVcpuCount int
|
|
GuestVmemSize int
|
|
}
|
|
|
|
func (self *SHost) getGuestsResource(status string) *SHostGuestResourceUsage {
|
|
guests := GuestManager.Query().SubQuery()
|
|
q := guests.Query(sqlchemy.COUNT("guest_count"),
|
|
sqlchemy.SUM("guest_vcpu_count", guests.Field("vcpu_count")),
|
|
sqlchemy.SUM("guest_vmem_size", guests.Field("vmem_size")))
|
|
q = q.Equals("host_id", self.Id)
|
|
if len(status) > 0 {
|
|
q = q.Equals("status", status)
|
|
}
|
|
stat := SHostGuestResourceUsage{}
|
|
err := q.First(&stat)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
return &stat
|
|
}
|
|
|
|
func (self *SHost) getMoreDetails(ctx context.Context, extra *jsonutils.JSONDict) *jsonutils.JSONDict {
|
|
zone := self.GetZone()
|
|
if zone != nil {
|
|
extra.Add(jsonutils.NewString(zone.Id), "zone_id")
|
|
extra.Add(jsonutils.NewString(zone.Name), "zone")
|
|
extra.Add(jsonutils.NewString(zone.GetRegion().GetName()), "region")
|
|
extra.Add(jsonutils.NewString(zone.GetRegion().GetId()), "region_id")
|
|
}
|
|
server := self.GetBaremetalServer()
|
|
if server != nil {
|
|
extra.Add(jsonutils.NewString(server.Id), "server_id")
|
|
extra.Add(jsonutils.NewString(server.Name), "server")
|
|
}
|
|
netifs := self.GetNetInterfaces()
|
|
if netifs != nil && len(netifs) > 0 {
|
|
nicInfos := []jsonutils.JSONObject{}
|
|
for i := 0; i < len(netifs); i += 1 {
|
|
nicInfo := netifs[i].getBaremetalJsonDesc()
|
|
if nicInfo == nil {
|
|
log.Errorf("netif %s get baremetal desc failed", netifs[i].GetId())
|
|
continue
|
|
}
|
|
nicInfos = append(nicInfos, nicInfo)
|
|
}
|
|
extra.Add(jsonutils.NewInt(int64(len(nicInfos))), "nic_count")
|
|
extra.Add(jsonutils.NewArray(nicInfos...), "nic_info")
|
|
}
|
|
schedtags := self.getSchedtags()
|
|
if schedtags != nil && len(schedtags) > 0 {
|
|
info := make([]jsonutils.JSONObject, len(schedtags))
|
|
for i := 0; i < len(schedtags); i += 1 {
|
|
info[i] = schedtags[i].GetShortDesc()
|
|
}
|
|
extra.Add(jsonutils.NewArray(info...), "schedtags")
|
|
}
|
|
var usage *SHostGuestResourceUsage
|
|
if options.Options.IgnoreNonrunningGuests {
|
|
usage = self.getGuestsResource(VM_RUNNING)
|
|
} else {
|
|
usage = self.getGuestsResource("")
|
|
}
|
|
if usage != nil {
|
|
extra.Add(jsonutils.NewInt(int64(usage.GuestVcpuCount)), "cpu_commit")
|
|
extra.Add(jsonutils.NewInt(int64(usage.GuestVmemSize)), "mem_commit")
|
|
}
|
|
extra.Add(jsonutils.NewInt(int64(self.GetGuestCount())), "guests")
|
|
extra.Add(jsonutils.NewInt(int64(self.GetNonsystemGuestCount())), "nonsystem_guests")
|
|
totalCpu := self.GetCpuCount()
|
|
cpuCommitRate := 0.0
|
|
if totalCpu > 0 && usage.GuestVcpuCount > 0 {
|
|
cpuCommitRate = float64(usage.GuestVcpuCount) * 1.0 / float64(totalCpu)
|
|
}
|
|
extra.Add(jsonutils.NewFloat(cpuCommitRate), "cpu_commit_rate")
|
|
totalMem := self.GetMemSize()
|
|
memCommitRate := 0.0
|
|
if totalMem > 0 && usage.GuestVmemSize > 0 {
|
|
memCommitRate = float64(usage.GuestVmemSize) * 1.0 / float64(totalMem)
|
|
}
|
|
extra.Add(jsonutils.NewFloat(memCommitRate), "mem_commit_rate")
|
|
extra = self.SManagedResourceBase.getExtraDetails(ctx, extra)
|
|
return extra
|
|
}
|
|
|
|
func (self *SHost) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
|
|
extra := self.SEnabledStatusStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query)
|
|
return self.getMoreDetails(ctx, extra)
|
|
}
|
|
|
|
func (self *SHost) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
|
|
extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
|
|
return self.getMoreDetails(ctx, extra)
|
|
}
|
|
|
|
func (self *SHost) AllowGetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionGet, "vnc")
|
|
}
|
|
|
|
func (self *SHost) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) {
|
|
retval := jsonutils.NewDict()
|
|
retval.Set("host_id", jsonutils.NewString(self.Id))
|
|
zone := self.GetZone()
|
|
retval.Set("zone", jsonutils.NewString(zone.GetName()))
|
|
return retval, nil
|
|
}
|
|
return jsonutils.NewDict(), nil
|
|
}
|
|
|
|
func (self *SHost) AllowGetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionGet, "ipmi")
|
|
}
|
|
|
|
func (self *SHost) GetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
ret := self.IpmiInfo.(*jsonutils.JSONDict)
|
|
if ret != nil {
|
|
password, err := ret.GetString("password")
|
|
if err == nil {
|
|
descryptedPassword, err := utils.DescryptAESBase64(self.Id, password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret.Set("password", jsonutils.NewString(descryptedPassword))
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (manager *SHostManager) GetHostsByManagerAndRegion(managerId string, regionId string) []SHost {
|
|
hosts := HostManager.Query().SubQuery()
|
|
zones := ZoneManager.Query().SubQuery()
|
|
q := hosts.Query()
|
|
q = q.Join(zones, sqlchemy.Equals(hosts.Field("zone_id"), zones.Field("id")))
|
|
q = q.Filter(sqlchemy.Equals(hosts.Field("manager_id"), managerId))
|
|
q = q.Filter(sqlchemy.Equals(zones.Field("cloudregion_id"), regionId))
|
|
ret := make([]SHost, 0)
|
|
err := db.FetchModelObjects(HostManager, q, &ret)
|
|
if err != nil {
|
|
log.Errorf("GetHostsByManagerAndRegion fail %s", err)
|
|
return nil
|
|
}
|
|
return ret
|
|
}
|
|
|
|
/*func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId, parentTaskId string, isForce bool) error {
|
|
//Todo
|
|
// HostcachedimagesManager.Register(userCred, self, imageId)
|
|
data := jsonutils.NewDict()
|
|
data.Set("image_id", jsonutils.NewString(imageId))
|
|
if isForce {
|
|
data.Set("is_force", jsonutils.JSONTrue)
|
|
}
|
|
task, err := taskman.TaskManager.NewTask(ctx, "StorageCacheImageTask", self, userCred, data, parentTaskId, "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}*/
|
|
|
|
func (self *SHost) Request(userCred mcclient.TokenCredential, method string, url string, headers http.Header, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
s := auth.GetSession(userCred, "", "")
|
|
_, ret, err := s.JSONRequest(self.ManagerUri, "", method, url, headers, body)
|
|
return ret, err
|
|
}
|
|
|
|
func (self *SHost) GetLocalStoragecache() *SStoragecache {
|
|
localStorages := self.GetAttachedStorages(STORAGE_LOCAL)
|
|
for i := 0; i < len(localStorages); i += 1 {
|
|
sc := localStorages[i].GetStoragecache()
|
|
if sc != nil {
|
|
return sc
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) IsBaremetalAgentReady() bool {
|
|
url, err := auth.GetServiceURL("baremetal", options.Options.Region, self.GetZone().GetName(), "")
|
|
if err != nil {
|
|
log.Errorln("is baremetal agent ready: false")
|
|
return false
|
|
}
|
|
log.Infof("baremetal url:%s", url)
|
|
return true
|
|
}
|
|
|
|
func (self *SHost) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
|
self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data)
|
|
kwargs := data.(*jsonutils.JSONDict)
|
|
ipmiInfo, err := self.FetchIpmiInfo(kwargs)
|
|
if err != nil {
|
|
log.Errorln(err.Error())
|
|
return
|
|
}
|
|
if ipmiInfo.Length() > 0 {
|
|
_, err := HostManager.TableSpec().Update(self, func() error {
|
|
self.IpmiInfo = ipmiInfo
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Errorln(err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func (manager *SHostManager) ValidateSizeParams(data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
memStr, _ := data.GetString("mem_size")
|
|
if len(memStr) > 0 {
|
|
if !regutils.MatchSize(memStr) {
|
|
return nil, fmt.Errorf("Memory size must be number[+unit], like 256M, 1G or 256")
|
|
}
|
|
memSize, err := fileutils.GetSizeMb(memStr, 'M', 1024)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data.Set("mem_size", jsonutils.NewInt(int64(memSize)))
|
|
}
|
|
memReservedStr, _ := data.GetString("mem_reserved")
|
|
if len(memReservedStr) > 0 {
|
|
if !regutils.MatchSize(memReservedStr) {
|
|
return nil, fmt.Errorf("Memory size must be number[+unit], like 256M, 1G or 256")
|
|
}
|
|
memSize, err := fileutils.GetSizeMb(memReservedStr, 'M', 1024)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data.Set("mem_reserved", jsonutils.NewInt(int64(memSize)))
|
|
}
|
|
cpuCacheStr, _ := data.GetString("cpu_cache")
|
|
if len(cpuCacheStr) > 0 {
|
|
if !regutils.MatchSize(cpuCacheStr) {
|
|
return nil, fmt.Errorf("Illegal cpu cache size %s", cpuCacheStr)
|
|
}
|
|
cpuCache, err := fileutils.GetSizeKb(cpuCacheStr, 'K', 1024)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data.Set("cpu_cache", jsonutils.NewInt(int64(cpuCache)))
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
zoneId, _ := data.GetString("zone_id")
|
|
if len(zoneId) == 0 || ZoneManager.Query().Equals("id", zoneId).Count() == 0 {
|
|
return nil, httperrors.NewInputParameterError("Zone id %s not found", zoneId)
|
|
}
|
|
mangerUri, err := data.GetString("manager_uri")
|
|
if err == nil {
|
|
count := manager.TableSpec().Query().Equals("manager_uri", mangerUri).Count()
|
|
if count > 0 {
|
|
return nil, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri)
|
|
}
|
|
}
|
|
accessIp, err := data.GetString("access_ip")
|
|
if err == nil {
|
|
count := manager.TableSpec().Query().Equals("access_ip", accessIp).Count()
|
|
if count > 0 {
|
|
return nil, httperrors.NewDuplicateResourceError("Duplicate access_ip %s", accessIp)
|
|
}
|
|
}
|
|
accessMac, err := data.GetString("access_mac")
|
|
if err == nil {
|
|
count := manager.TableSpec().Query().Equals("access_mac", accessMac).Count()
|
|
if count > 0 {
|
|
return nil, httperrors.NewDuplicateResourceError("Duplicate access_mac %s", accessMac)
|
|
}
|
|
}
|
|
data, err = manager.ValidateSizeParams(data)
|
|
if err != nil {
|
|
return nil, httperrors.NewInputParameterError(err.Error())
|
|
}
|
|
memReserved, err := data.Int("mem_reserved")
|
|
if err != nil {
|
|
hostType, _ := data.GetString("host_type")
|
|
if hostType != HOST_TYPE_BAREMETAL {
|
|
memSize, _ := data.Int("mem_size")
|
|
memReserved = memSize / 8
|
|
if memReserved > 4096 {
|
|
memReserved = 4096
|
|
}
|
|
data.Set("mem_reserved", jsonutils.NewInt(memReserved))
|
|
} else {
|
|
data.Set("mem_reserved", jsonutils.NewInt(0))
|
|
}
|
|
}
|
|
return manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data)
|
|
}
|
|
|
|
func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
mangerUri, err := data.GetString("manager_uri")
|
|
if err == nil {
|
|
count := HostManager.Query().Equals("manager_uri", mangerUri).
|
|
NotEquals("id", self.Id).Equals("zone_id", self.ZoneId).Count()
|
|
if count > 0 {
|
|
return nil, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri)
|
|
}
|
|
}
|
|
accessIp, err := data.GetString("access_ip")
|
|
if err == nil {
|
|
count := HostManager.Query().Equals("access_ip", accessIp).
|
|
NotEquals("id", self.Id).Equals("zone_id", self.ZoneId).Count()
|
|
if count > 0 {
|
|
return nil, httperrors.NewDuplicateResourceError("Duplicate access_ip %s", accessIp)
|
|
}
|
|
}
|
|
accessMac, err := data.GetString("access_mac")
|
|
if err == nil {
|
|
accessMac = netutils.FormatMacAddr(accessMac)
|
|
if len(accessMac) == 0 {
|
|
return nil, httperrors.NewInputParameterError("invalid access_mac address")
|
|
}
|
|
q := HostManager.Query().Equals("access_mac", accessMac).NotEquals("id", self.Id)
|
|
if q.Count() > 0 {
|
|
return nil, httperrors.NewDuplicateResourceError("Duplicate access_mac %s", accessMac)
|
|
}
|
|
}
|
|
data, err = HostManager.ValidateSizeParams(data)
|
|
if err != nil {
|
|
return nil, httperrors.NewInputParameterError(err.Error())
|
|
}
|
|
ipmiInfo, err := self.FetchIpmiInfo(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ipmiInfo.Length() > 0 {
|
|
val := jsonutils.NewDict()
|
|
val.Update(self.IpmiInfo)
|
|
val.Update(ipmiInfo)
|
|
data.Set("impi_info", val)
|
|
}
|
|
data, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if data.Contains("name") {
|
|
self.UpdateDnsRecords(false)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (self *SHost) UpdateDnsRecords(isAdd bool) {
|
|
for _, netif := range self.GetNetInterfaces() {
|
|
self.UpdateDnsRecord(&netif, isAdd)
|
|
}
|
|
}
|
|
|
|
func (self *SHost) UpdateDnsRecord(netif *SNetInterface, isAdd bool) {
|
|
name := self.GetNetifName(netif)
|
|
if len(name) == 0 {
|
|
return
|
|
}
|
|
bn := netif.GetBaremetalNetwork()
|
|
if bn == nil {
|
|
log.Errorf("Interface %s not enable", netif.GetId())
|
|
return
|
|
}
|
|
net := bn.GetNetwork()
|
|
if net == nil {
|
|
log.Errorf("BaremetalNetwoke %s not found network", bn.GetId())
|
|
}
|
|
net._updateDnsRecord(name, bn.IpAddr, isAdd)
|
|
}
|
|
|
|
func (self *SHost) GetNetifName(netif *SNetInterface) string {
|
|
if netif.NicType == NIC_TYPE_IPMI {
|
|
return self.GetName()
|
|
} else if netif.NicType == NIC_TYPE_ADMIN {
|
|
return self.GetName() + "-admin"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (self *SHost) FetchIpmiInfo(data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
IPMI_KEY_PERFIX := "ipmi_"
|
|
ipmiInfo := jsonutils.NewDict()
|
|
kv, _ := data.GetMap()
|
|
for key := range kv {
|
|
value, err := ipmiInfo.GetString(key)
|
|
if strings.HasPrefix(value, IPMI_KEY_PERFIX) {
|
|
subkey := key[len(IPMI_KEY_PERFIX):]
|
|
data.Remove(key)
|
|
if subkey == "password" {
|
|
value, err = utils.EncryptAESBase64(self.Id, value)
|
|
if err != nil {
|
|
log.Errorf("encrypt password failed %s", err)
|
|
return nil, err
|
|
}
|
|
}
|
|
ipmiInfo.Set(key, jsonutils.NewString(value))
|
|
}
|
|
}
|
|
return ipmiInfo, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformStart(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "start")
|
|
}
|
|
|
|
func (self *SHost) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if !self.IsBaremetal {
|
|
return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host")
|
|
}
|
|
if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY}) {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot start baremetal with active guest")
|
|
}
|
|
guest := self.GetBaremetalServer()
|
|
if guest != nil {
|
|
if self.HostType != HOST_TYPE_BAREMETAL {
|
|
if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) {
|
|
return nil, httperrors.NewBadRequestError("Cannot start baremetal with active guest")
|
|
}
|
|
} else {
|
|
if utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) {
|
|
return nil, self.InitializedGuestStart(ctx, userCred, guest)
|
|
}
|
|
self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "")
|
|
return guest.PerformStart(ctx, userCred, query, data)
|
|
}
|
|
}
|
|
params := jsonutils.NewDict()
|
|
params.Set("force_reboot", jsonutils.NewBool(false))
|
|
params.Set("action", jsonutils.NewString("start"))
|
|
return self.PerformMaintenance(ctx, userCred, nil, params)
|
|
}
|
|
|
|
func (self *SHost) AllowPerformStop(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "stop")
|
|
}
|
|
|
|
func (self *SHost) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if !self.IsBaremetal {
|
|
return nil, httperrors.NewBadRequestError("Cannot stop a non-baremetal host")
|
|
}
|
|
if !utils.IsInStringArray(self.Status, []string{BAREMETAL_RUNNING}) {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot stop baremetal with non-active guest")
|
|
}
|
|
guest := self.GetBaremetalServer()
|
|
if guest != nil {
|
|
if self.HostType != HOST_TYPE_BAREMETAL {
|
|
if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) {
|
|
return nil, httperrors.NewBadRequestError("Cannot stop baremetal with active guest")
|
|
}
|
|
} else {
|
|
if utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) {
|
|
return nil, self.InitializedGuestStop(ctx, userCred, guest)
|
|
}
|
|
self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "")
|
|
return guest.PerformStop(ctx, userCred, query, data)
|
|
}
|
|
}
|
|
return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, false, "stop")
|
|
}
|
|
|
|
func (self *SHost) InitializedGuestStart(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error {
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerStartTask", guest, userCred, nil, "", "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) InitializedGuestStop(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error {
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerStopTask", guest, userCred, nil, "", "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformMaintenance(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "maintenance")
|
|
|
|
}
|
|
|
|
func (self *SHost) PerformMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot do maintenance in status %s", self.Status)
|
|
}
|
|
guest := self.GetBaremetalServer()
|
|
if guest != nil && !utils.IsInStringArray(guest.Status, []string{VM_READY, VM_RUNNING, VM_ADMIN}) {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot do maintenance while guest status %s", guest.Status)
|
|
}
|
|
params := jsonutils.NewDict()
|
|
if guest != nil {
|
|
if guest.Status == VM_RUNNING {
|
|
params.Set("guest_running", jsonutils.NewBool(true))
|
|
}
|
|
guest.SetStatus(userCred, VM_ADMIN, "")
|
|
}
|
|
if self.Status == BAREMETAL_RUNNING && jsonutils.QueryBoolean(data, "force_reboot", false) {
|
|
params.Set("force_reboot", jsonutils.NewBool(true))
|
|
}
|
|
action := "maintenance"
|
|
if data.Contains("action") {
|
|
action, _ = data.GetString("action")
|
|
}
|
|
params.Set("action", jsonutils.NewString(action))
|
|
self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "")
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalMaintenanceTask", self, userCred, params, "", "", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformUnmaintenance(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "unmaintenance")
|
|
}
|
|
|
|
func (self *SHost) PerformUnmaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if !utils.IsInStringArray(self.Status, []string{BAREMETAL_RUNNING, BAREMETAL_READY}) {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot do unmaintenance in status %s", self.Status)
|
|
}
|
|
guest := self.GetBaremetalServer()
|
|
if guest != nil && guest.Status != VM_ADMIN {
|
|
return nil, httperrors.NewInvalidStatusError("Wrong guest status %s", guest.Status)
|
|
}
|
|
action, _ := data.GetString("action")
|
|
if len(action) == 0 {
|
|
action = "unmaintenance"
|
|
}
|
|
guestRunning := self.GetMetadata("__maint_guest_running", userCred)
|
|
var startGuest = false
|
|
if utils.ToBool(guestRunning) {
|
|
startGuest = true
|
|
}
|
|
return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, startGuest, action)
|
|
}
|
|
|
|
func (self *SHost) StartBaremetalUnmaintenanceTask(ctx context.Context, userCred mcclient.TokenCredential, startGuest bool, action string) error {
|
|
self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "")
|
|
params := jsonutils.NewDict()
|
|
params.Set("guest_running", jsonutils.NewBool(startGuest))
|
|
if len(action) == 0 {
|
|
action = "unmaintenance"
|
|
}
|
|
params.Set("action", jsonutils.NewString(action))
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnmaintenanceTask", self, userCred, params, "", "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) BaremetalSyncRequest(ctx context.Context, method, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
|
serviceUrl, err := auth.GetServiceURL("baremetal", options.Options.Region, self.GetZone().GetName(), "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
url = serviceUrl + url
|
|
_, data, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, method, url, headers, body, false)
|
|
return data, err
|
|
}
|
|
|
|
func (self *SHost) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncStatusTask", self, userCred, nil, parentTaskId, "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformOffline(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "offline")
|
|
}
|
|
|
|
func (self *SHost) PerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if self.Status != HOST_OFFLINE {
|
|
self.GetModelManager().TableSpec().Update(self, func() error {
|
|
self.Status = HOST_OFFLINE
|
|
return nil
|
|
})
|
|
db.OpsLog.LogEvent(self, db.ACT_OFFLINE, "", userCred)
|
|
logclient.AddActionLog(self, logclient.ACT_ONLINE, nil, userCred, true)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformOnline(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "online")
|
|
}
|
|
|
|
func (self *SHost) PerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if self.HostStatus != HOST_ONLINE {
|
|
self.GetModelManager().TableSpec().Update(self, func() error {
|
|
self.LastPingAt = time.Now()
|
|
self.HostStatus = HOST_ONLINE
|
|
self.Status = BAREMETAL_RUNNING
|
|
return nil
|
|
})
|
|
db.OpsLog.LogEvent(self, db.ACT_ONLINE, "", userCred)
|
|
logclient.AddActionLog(self, logclient.ACT_ONLINE, nil, userCred, true)
|
|
self.SyncAttachedStorageStatus()
|
|
self.StartSyncAllGuestsStatusTask(ctx, userCred)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) StartSyncAllGuestsStatusTask(ctx context.Context, userCred mcclient.TokenCredential) error {
|
|
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncAllGuestsStatusTask", self, userCred, nil, "", "", nil); err != nil {
|
|
log.Errorf(err.Error())
|
|
return err
|
|
} else {
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (self *SHost) AllowPerformPing(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "ping")
|
|
}
|
|
|
|
func (self *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if self.HostStatus != HOST_ONLINE {
|
|
self.PerformOnline(ctx, userCred, query, data)
|
|
} else {
|
|
self.GetModelManager().TableSpec().Update(self, func() error {
|
|
self.LastPingAt = time.Now()
|
|
return nil
|
|
})
|
|
}
|
|
result := jsonutils.NewDict()
|
|
result.Set("name", jsonutils.NewString(self.GetName()))
|
|
dependSvcs := []string{"ntpd", "kafka", "influxdb", "elasticsearch"}
|
|
catalog := auth.GetCatalogData(dependSvcs, options.Options.Region)
|
|
if catalog == nil {
|
|
return nil, fmt.Errorf("Get catalog error")
|
|
}
|
|
result.Set("catalog", catalog)
|
|
return result, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformPrepare(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "prepare")
|
|
}
|
|
|
|
func (self *SHost) PerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING, BAREMETAL_PREPARE_FAIL}) {
|
|
var onfinish string
|
|
if self.GetBaremetalServer() != nil {
|
|
if self.Status == BAREMETAL_RUNNING {
|
|
onfinish = "restart"
|
|
} else if self.Status == BAREMETAL_READY {
|
|
onfinish = "shotdown"
|
|
}
|
|
}
|
|
return nil, self.StartPrepareTask(ctx, userCred, onfinish, "")
|
|
}
|
|
return nil, httperrors.NewInvalidStatusError("Cannot prepare baremetal in status %s", self.Status)
|
|
}
|
|
|
|
func (self *SHost) StartPrepareTask(ctx context.Context, userCred mcclient.TokenCredential, onfinish, parentTaskId string) error {
|
|
data := jsonutils.NewDict()
|
|
if len(onfinish) > 0 {
|
|
data.Set("on_finish", jsonutils.NewString(onfinish))
|
|
}
|
|
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalPrepareTask", self, userCred, data, parentTaskId, "", nil); err != nil {
|
|
log.Errorf(err.Error())
|
|
return err
|
|
} else {
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (self *SHost) AllowPerformAddNetif(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "add-netif")
|
|
}
|
|
|
|
func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
mac, _ := data.GetString("mac")
|
|
if len(mac) == 0 || len(netutils.FormatMacAddr(mac)) == 0 {
|
|
return nil, httperrors.NewBadRequestError("Invaild mac address")
|
|
}
|
|
wire, _ := data.GetString("wire")
|
|
ipAddr, _ := data.GetString("ip_addr")
|
|
var sw *SWire
|
|
if len(wire) > 0 && len(ipAddr) == 0 {
|
|
iWire, err := WireManager.FetchByIdOrName(userCred, wire)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError("Wire %s not found", wire)
|
|
}
|
|
sw = iWire.(*SWire)
|
|
} else if len(ipAddr) > 0 && len(wire) == 0 {
|
|
ipWire, err := WireManager.GetWireOfIp(ipAddr)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr)
|
|
}
|
|
sw = ipWire
|
|
} else if len(wire) > 0 && len(ipAddr) > 0 {
|
|
ipWire, err := WireManager.GetWireOfIp(ipAddr)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr)
|
|
}
|
|
if ipWire.Id != wire && ipWire.GetName() != wire {
|
|
httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire)
|
|
}
|
|
sw = ipWire
|
|
}
|
|
netif, err := NetInterfaceManager.FetchByMac(mac)
|
|
if err != nil {
|
|
netif = &SNetInterface{}
|
|
netif.Mac = mac
|
|
netif.BaremetalId = self.Id
|
|
if sw != nil {
|
|
netif.WireId = sw.Id
|
|
}
|
|
if rate, err := data.Int("rate"); err == nil {
|
|
netif.Rate = int(rate)
|
|
}
|
|
if nicType, err := data.GetString("nic_type"); err == nil {
|
|
netif.NicType = nicType
|
|
}
|
|
if index, err := data.Int("index"); err == nil {
|
|
netif.Index = int8(index)
|
|
}
|
|
if linkUp, err := data.GetString("link_up"); err == nil {
|
|
netif.LinkUp = utils.ToBool(linkUp)
|
|
}
|
|
if mtu, err := data.Int("mtu"); err == nil {
|
|
netif.Mtu = int16(mtu)
|
|
}
|
|
err = NetInterfaceManager.TableSpec().Insert(netif)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
var changed = false
|
|
_, err := NetInterfaceManager.TableSpec().Update(netif, func() error {
|
|
if netif.BaremetalId != self.Id {
|
|
changed = true
|
|
netif.BaremetalId = self.Id
|
|
}
|
|
if sw != nil && netif.WireId != sw.Id {
|
|
changed = true
|
|
netif.WireId = sw.Id
|
|
}
|
|
if rate, err := data.Int("rate"); err == nil {
|
|
changed = true
|
|
netif.Rate = int(rate)
|
|
}
|
|
if nicType, err := data.GetString("nic_type"); err == nil {
|
|
changed = true
|
|
netif.NicType = nicType
|
|
}
|
|
if index, err := data.Int("index"); err == nil && index >= 0 {
|
|
changed = true
|
|
netif.Index = int8(index)
|
|
}
|
|
if linkUp, err := data.GetString("link_up"); err != nil {
|
|
changed = true
|
|
netif.LinkUp = utils.ToBool(linkUp)
|
|
}
|
|
if mtu, err := data.Int("mtu"); err != nil {
|
|
changed = true
|
|
netif.Mtu = int16(mtu)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if changed || jsonutils.QueryBoolean(data, "reset", false) {
|
|
self.DisableNetif(ctx, userCred, netif, false)
|
|
}
|
|
}
|
|
sw = netif.GetWire()
|
|
if sw != nil {
|
|
strInterface, err := data.GetString("interface")
|
|
if err != nil {
|
|
strInterface = fmt.Sprintf("eth%d", netif.Index)
|
|
}
|
|
if len(strInterface) > 0 {
|
|
bridge, err := data.GetString("bridge")
|
|
if err != nil {
|
|
bridge = fmt.Sprintf("br%s", sw.GetName())
|
|
}
|
|
var isMaster = netif.NicType == NIC_TYPE_ADMIN
|
|
ihw, err := HostwireManager.FetchByIds(self.Id, sw.Id)
|
|
if err != nil {
|
|
hw := &SHostwire{}
|
|
hw.Bridge = bridge
|
|
hw.Interface = strInterface
|
|
hw.HostId = self.Id
|
|
hw.WireId = sw.Id
|
|
hw.IsMaster = isMaster
|
|
hw.MacAddr = mac
|
|
err := HostwireManager.TableSpec().Insert(hw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
hw := ihw.(*SHostwire)
|
|
HostwireManager.TableSpec().Update(hw, func() error {
|
|
hw.Bridge = bridge
|
|
hw.Interface = strInterface
|
|
hw.MacAddr = mac
|
|
hw.IsMaster = isMaster
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
}
|
|
reserve := jsonutils.QueryBoolean(data, "reserve", false)
|
|
requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false)
|
|
if len(ipAddr) > 0 {
|
|
err = self.EnableNetif(ctx, userCred, netif, "", ipAddr, "", reserve, requireDesignatedIp)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError(err.Error())
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformEnableNetif(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "enable-netif")
|
|
}
|
|
|
|
func (self *SHost) PerformEnableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
mac, _ := data.GetString("mac")
|
|
netif := self.GetNetInterface(mac)
|
|
if netif == nil {
|
|
return nil, httperrors.NewBadRequestError("Interface %s not exist", mac)
|
|
}
|
|
if !utils.IsInStringArray(netif.NicType, NIC_TYPES) {
|
|
return nil, httperrors.NewBadRequestError("Only ADMIN and IPMI nic can be enable")
|
|
}
|
|
network, _ := data.GetString("network")
|
|
ipAddr, _ := data.GetString("ip_addr")
|
|
allocDir, _ := data.GetString("alloc_dir")
|
|
reserve := jsonutils.QueryBoolean(data, "reserve", false)
|
|
requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false)
|
|
err := self.EnableNetif(ctx, userCred, netif, network, ipAddr, allocDir, reserve, requireDesignatedIp)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError(err.Error())
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, network, ipAddr, allocDir string, reserve, requireDesignatedIp bool) error {
|
|
bn := netif.GetBaremetalNetwork()
|
|
if bn != nil {
|
|
return nil
|
|
}
|
|
var net *SNetwork
|
|
var err error
|
|
if len(ipAddr) > 0 {
|
|
net, err = netif.GetCandidateNetworkForIp(userCred, ipAddr)
|
|
if net != nil {
|
|
log.Infof("find network %s for ip %s", net.GetName(), ipAddr)
|
|
} else if requireDesignatedIp {
|
|
log.Errorf("Cannot allocate IP %s, not reachable", ipAddr)
|
|
return fmt.Errorf("Cannot allocate IP %s, not reachable", ipAddr)
|
|
}
|
|
}
|
|
wire := netif.GetWire()
|
|
if wire == nil {
|
|
return fmt.Errorf("No wire attached")
|
|
}
|
|
hw, err := HostwireManager.FetchByIds(self.Id, wire.Id)
|
|
if hw == nil {
|
|
return fmt.Errorf("host not attach to this wire")
|
|
}
|
|
if net == nil {
|
|
if len(network) > 0 {
|
|
iNet, err := NetworkManager.FetchByIdOrName(userCred, network)
|
|
if err != nil {
|
|
return fmt.Errorf("Network %s not found: %s", network, err)
|
|
}
|
|
net = iNet.(*SNetwork)
|
|
if len(net.WireId) == 0 || net.WireId != wire.Id {
|
|
return fmt.Errorf("Network %s not reacheable on mac %s", network, netif.Mac)
|
|
}
|
|
} else {
|
|
net, err = wire.GetCandidatePrivateNetwork(userCred, false, SERVER_TYPE_GUEST)
|
|
if err != nil || net == nil {
|
|
return fmt.Errorf("No network found")
|
|
}
|
|
}
|
|
} else if net.WireId != wire.Id {
|
|
return fmt.Errorf("conflict??? candiate net is not on wire")
|
|
}
|
|
return self.Attach2Network(ctx, userCred, netif, net, ipAddr, allocDir, reserve, requireDesignatedIp)
|
|
}
|
|
|
|
func (self *SHost) AllowPerformDisableNetif(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "disable-netif")
|
|
}
|
|
|
|
func (self *SHost) PerformDisableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
mac, _ := data.GetString("mac")
|
|
netif := self.GetNetInterface(mac)
|
|
if netif == nil {
|
|
return nil, httperrors.NewBadRequestError("Interface %s not exists", mac)
|
|
}
|
|
reserve := jsonutils.QueryBoolean(data, "reserve", false)
|
|
err := self.DisableNetif(ctx, userCred, netif, reserve)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError(err.Error())
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) DisableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error {
|
|
bn := netif.GetBaremetalNetwork()
|
|
if bn != nil {
|
|
self.UpdateDnsRecord(netif, false)
|
|
self.DeleteBaremetalnetwork(ctx, userCred, bn, reserve)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) Attach2Network(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, net *SNetwork, ipAddr, allocDir string, reserved, requireDesignatedIp bool) error {
|
|
lockman.LockObject(ctx, net)
|
|
defer lockman.ReleaseObject(ctx, net)
|
|
usedAddr := net.GetUsedAddresses()
|
|
freeIp, err := net.GetFreeIP(ctx, userCred, usedAddr, nil, ipAddr, IPAddlocationDirection(allocDir), reserved)
|
|
if err != nil {
|
|
log.Errorf("attach2network: %s", err)
|
|
return err
|
|
}
|
|
if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp {
|
|
return fmt.Errorf("IP address %s is occupied", ipAddr)
|
|
}
|
|
bn := &SHostnetwork{}
|
|
bn.BaremetalId = self.Id
|
|
bn.NetworkId = net.Id
|
|
bn.IpAddr = freeIp
|
|
bn.MacAddr = netif.Mac
|
|
HostnetworkManager.TableSpec().Insert(bn)
|
|
db.OpsLog.LogAttachEvent(self, net, userCred, jsonutils.NewString(freeIp))
|
|
self.UpdateDnsRecord(netif, true)
|
|
net.UpdateBaremetalNetmap(bn, self.GetNetifName(netif))
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformRemoveNetif(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "remove-netif")
|
|
}
|
|
|
|
func (self *SHost) PerformRemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
mac, _ := data.GetString("mac")
|
|
mac = netutils.FormatMacAddr(mac)
|
|
if len(mac) == 0 {
|
|
return nil, httperrors.NewBadRequestError("Invalid mac address")
|
|
}
|
|
netif, err := NetInterfaceManager.FetchByMac(mac)
|
|
if err != nil {
|
|
return nil, httperrors.NewBadRequestError("Fetch netif error %s", err)
|
|
}
|
|
return nil, self.RemoveNetif(ctx, userCred, netif, jsonutils.QueryBoolean(data, "reserve", false))
|
|
}
|
|
|
|
func (self *SHost) RemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error {
|
|
wire := netif.GetWire()
|
|
self.DisableNetif(ctx, userCred, netif, reserve)
|
|
log.Infof("Remove wire")
|
|
err := netif.Remove(ctx, userCred)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if wire != nil {
|
|
log.Infof("Remove wire")
|
|
others := self.GetNetifsOnWire(wire)
|
|
if len(others) == 0 {
|
|
hw, _ := HostwireManager.FetchByIds(self.Id, wire.Id)
|
|
if hw != nil {
|
|
db.OpsLog.LogDetachEvent(self, wire, userCred, jsonutils.NewString(fmt.Sprintf("disable netif %s", self.AccessMac)))
|
|
log.Infof("Detach host wire because of remove netif %s", netif.Mac)
|
|
return hw.Delete(ctx, userCred)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SHost) GetNetifsOnWire(wire *SWire) []SNetInterface {
|
|
dest := make([]SNetInterface, 0)
|
|
q := NetInterfaceManager.Query()
|
|
err := q.Filter(sqlchemy.Equals(q.Field("baremetal_id"), self.Id)).Filter(sqlchemy.Equals(q.Field("wire_id"), wire.Id)).Desc(q.Field("index")).All(&dest)
|
|
if err != nil {
|
|
log.Errorln(err)
|
|
return nil
|
|
}
|
|
return dest
|
|
}
|
|
|
|
func (self *SHost) AllowPerformSyncstatus(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "syncstatus")
|
|
}
|
|
|
|
func (self *SHost) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
self.SetStatus(userCred, BAREMETAL_SYNCING_STATUS, "")
|
|
return nil, self.StartSyncstatus(ctx, userCred, "")
|
|
}
|
|
|
|
func (self *SHost) AllowPerformReset(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "reset")
|
|
}
|
|
|
|
func (self *SHost) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if !self.IsBaremetal {
|
|
return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host")
|
|
}
|
|
if self.Status != BAREMETAL_RUNNING {
|
|
return nil, httperrors.NewBadRequestError("Cannot reset baremetal in status %s", self.Status)
|
|
}
|
|
guest := self.GetBaremetalServer()
|
|
if guest != nil {
|
|
if self.HostType == HOST_TYPE_BAREMETAL {
|
|
if guest.Status != VM_ADMIN {
|
|
return nil, httperrors.NewBadRequestError("Cannot reset baremetal with active guest")
|
|
}
|
|
} else {
|
|
return guest.PerformReset(ctx, userCred, query, data)
|
|
}
|
|
}
|
|
kwargs := jsonutils.NewDict()
|
|
kwargs.Set("force_reboot", jsonutils.JSONTrue)
|
|
kwargs.Set("action", jsonutils.NewString("reset"))
|
|
return self.PerformMaintenance(ctx, userCred, query, kwargs)
|
|
}
|
|
|
|
func (self *SHost) AllowPerformRemoveAllNetifs(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "remove-all-netifs")
|
|
}
|
|
|
|
func (self *SHost) PerformRemoveAllNetifs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
netifs := self.GetNetInterfaces()
|
|
for i := 0; i < len(netifs); i++ {
|
|
if !utils.IsInStringArray(netifs[i].NicType, NIC_TYPES) {
|
|
self.RemoveNetif(ctx, userCred, &netifs[i], false)
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformDisable(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "disable")
|
|
}
|
|
|
|
func (self *SHost) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if self.Enabled {
|
|
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
|
|
self.Enabled = false
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
db.OpsLog.LogEvent(self, db.ACT_DISABLE, "", userCred)
|
|
logclient.AddActionLog(self, logclient.ACT_DISABLE, nil, userCred, true)
|
|
self.SyncAttachedStorageStatus()
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformCacheImage(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "cache-image")
|
|
}
|
|
|
|
func (self *SHost) PerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if self.HostStatus != HOST_ONLINE {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot perform cache image in status %s", self.Status)
|
|
}
|
|
imageId, _ := data.GetString("image")
|
|
img, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
|
|
if err != nil {
|
|
log.Errorf(err.Error())
|
|
return nil, httperrors.NewNotFoundError("image %s not found", imageId)
|
|
}
|
|
if len(img.Checksum) != 0 && regutils.MatchUUID(img.Checksum) {
|
|
return nil, httperrors.NewInvalidStatusError("Cannot cache image with no checksum")
|
|
}
|
|
isForce := jsonutils.QueryBoolean(data, "is_force", false)
|
|
return nil, self.StartImageCacheTask(ctx, userCred, img.Id, isForce)
|
|
}
|
|
|
|
func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, isForce bool) error {
|
|
sc := self.GetLocalStoragecache()
|
|
if sc == nil {
|
|
return fmt.Errorf("No local storage cache found")
|
|
}
|
|
return sc.StartImageCacheTask(ctx, userCred, imageId, isForce, "")
|
|
}
|
|
|
|
func (self *SHost) AllowPerformConvertHypervisor(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "convert-hypervisor")
|
|
}
|
|
|
|
func (self *SHost) isAlterNameUnique(name string) bool {
|
|
if self.GetModelManager().Query().Equals("name", name).NotEquals("id", self.Id).Equals("zone_id", self.ZoneId).Count() == 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (self *SHost) PerformConvertHypervisor(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
hostType, err := data.GetString("host_type")
|
|
if err != nil {
|
|
return nil, httperrors.NewNotAcceptableError("host_type must be specified")
|
|
}
|
|
if self.HostType != HOST_TYPE_BAREMETAL {
|
|
return nil, httperrors.NewNotAcceptableError("Must be a baremetal host")
|
|
}
|
|
if self.GetBaremetalServer() != nil {
|
|
return nil, httperrors.NewNotAcceptableError("Baremetal host is aleady occupied")
|
|
}
|
|
if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) {
|
|
return nil, httperrors.NewNotAcceptableError("Connot convert hypervisor in status %s", self.Status)
|
|
}
|
|
driver := GetHostDriver(hostType)
|
|
if driver == nil {
|
|
return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", hostType)
|
|
}
|
|
// err := driver.CheckConvertConfig() do nothing
|
|
// if err != nil {
|
|
// return nil, httperrors.NewNotAcceptableError("Need more configuration: %s", err.Error())
|
|
// }
|
|
if data.Contains("name") {
|
|
name, _ := data.GetString("name")
|
|
err := self.GetModelManager().ValidateName(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !self.isAlterNameUnique(name) {
|
|
return nil, httperrors.NewInputParameterError("Invalid name %s", name)
|
|
}
|
|
}
|
|
image, _ := data.GetString("image")
|
|
raid, _ := data.GetString("raid")
|
|
params, err := driver.PrepareConvert(self, image, raid, data)
|
|
if err != nil {
|
|
return nil, httperrors.NewNotAcceptableError("Convert error: %s", err.Error())
|
|
}
|
|
guest, err := GuestManager.DoCreate(ctx, userCred, data, params, GuestManager)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Infof("Host convert to %s", guest.GetName())
|
|
db.OpsLog.LogEvent(self, db.ACT_CONVERT_START, "", userCred)
|
|
db.OpsLog.LogEvent(guest, db.ACT_CREATE, "Convert hypervisor", userCred)
|
|
params.Set("__task__", jsonutils.NewString(taskman.CONVERT_TASK))
|
|
params.Set("__convert_host_type__", jsonutils.NewString(hostType))
|
|
GuestManager.OnCreateComplete(ctx, []db.IModel{guest}, userCred, nil, params)
|
|
self.SetStatus(userCred, BAREMETAL_START_CONVERT, "")
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) AllowPerformUndoConvert(ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
data jsonutils.JSONObject) bool {
|
|
return userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "undo-convert")
|
|
}
|
|
|
|
func (self *SHost) PerformUndoConvert(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
if !self.IsBaremetal {
|
|
return nil, httperrors.NewNotAcceptableError("Not a baremetal")
|
|
}
|
|
if self.HostType == HOST_TYPE_BAREMETAL {
|
|
return nil, httperrors.NewNotAcceptableError("Not being convert to hypervisor")
|
|
}
|
|
if self.Enabled {
|
|
return nil, httperrors.NewNotAcceptableError("Host should be disabled")
|
|
}
|
|
if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) {
|
|
return nil, httperrors.NewNotAcceptableError("Cannot unconvert in status %s", self.Status)
|
|
}
|
|
driver := self.GetDriverWithDefault()
|
|
if driver == nil {
|
|
return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", self.HostType)
|
|
}
|
|
err := driver.PrepareUnconvert(self)
|
|
if err != nil {
|
|
return nil, httperrors.NewNotAcceptableError(err.Error())
|
|
}
|
|
guests := self.GetGuests()
|
|
if len(guests) > 1 {
|
|
return nil, httperrors.NewNotAcceptableError("Not an empty host")
|
|
} else if len(guests) == 1 {
|
|
guest := guests[0]
|
|
if guest.Hypervisor != HYPERVISOR_BAREMETAL {
|
|
return nil, httperrors.NewNotAcceptableError("Not an converted hypervisor")
|
|
}
|
|
_, err := guest.GetModelManager().TableSpec().Update(&guest, func() error {
|
|
guest.DisableDelete = tristate.False
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
db.OpsLog.LogEvent(&guest, db.ACT_DELETE, "Unconvert baremetal", userCred)
|
|
}
|
|
db.OpsLog.LogEvent(self, db.ACT_UNCONVERT_START, "", userCred)
|
|
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnconvertHypervisorTask", self, userCred, nil, "", "", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil, nil
|
|
}
|
|
|
|
func (self *SHost) GetDriverWithDefault() IHostDriver {
|
|
hostType := self.HostType
|
|
if len(hostType) == 0 {
|
|
hostType = HOST_TYPE_DEFAULT
|
|
}
|
|
return GetHostDriver(hostType)
|
|
}
|
|
|
|
func (self *SHost) UpdateDiskConfig(layouts []baremetal.Layout) error {
|
|
bs := self.GetBaremetalstorage()
|
|
if bs != nil {
|
|
_, err := bs.GetModelManager().TableSpec().Update(bs, func() error {
|
|
if len(layouts) != 0 {
|
|
bs.Config = jsonutils.Marshal(layouts).(*jsonutils.JSONArray)
|
|
var size int64
|
|
for i := 0; i < len(layouts); i++ {
|
|
size += layouts[i].Size
|
|
}
|
|
bs.RealCapacity = int(size)
|
|
} else {
|
|
bs.Config = jsonutils.NewArray()
|
|
bs.RealCapacity = bs.GetStorage().Capacity
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Errorln(err)
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|