// Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package models import ( "context" "database/sql" "fmt" "net/http" "net/url" "sort" "strconv" "strings" "time" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "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/apis" billing_api "yunion.io/x/onecloud/pkg/apis/billing" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/types" "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" "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) type SHostManager struct { db.SEnabledStatusInfrasResourceBaseManager db.SExternalizedResourceBaseManager db.SDnsNameValidatorManager SZoneResourceBaseManager SManagedResourceBaseManager } var HostManager *SHostManager func init() { HostManager = &SHostManager{ SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SHost{}, "hosts_tbl", "host", "hosts", ), } HostManager.SetVirtualObject(HostManager) HostManager.SetAlias("baremetal", "baremetals") } type SHost struct { db.SEnabledStatusInfrasResourceBase db.SExternalizedResourceBase SZoneResourceBase SManagedResourceBase SBillingResourceBase // 机架 Rack string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // 机位 Slots string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // 管理口MAC AccessMac string `width:"32" charset:"ascii" nullable:"false" index:"true" list:"admin" update:"admin"` // 管理口Ip地址 AccessIp string `width:"16" charset:"ascii" nullable:"true" list:"admin"` // 管理地址 ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 系统信息 SysInfo jsonutils.JSONObject `nullable:"true" search:"admin" list:"admin" update:"admin" create:"admin_optional"` // 物理机序列号信息 SN string `width:"128" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // CPU核数 CpuCount int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 物理CPU颗数 NodeCount int8 `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // CPU描述信息 CpuDesc string `width:"64" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // CPU频率 CpuMhz int `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // CPU缓存大小,单位KB CpuCache int `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // 预留CPU大小 CpuReserved int `nullable:"true" default:"0" list:"admin" update:"admin" create:"admin_optional"` // CPU超分比 CpuCmtbound float32 `nullable:"true" default:"8" list:"admin" update:"admin" create:"admin_optional"` // CPUMicrocode CpuMicrocode string `width:"64" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // CPU架构 CpuArchitecture string `width:"16" charset:"ascii" nullable:"true" get:"user" update:"admin" create:"admin_optional"` // 内存大小,单位Mb MemSize int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 预留内存大小 MemReserved int `nullable:"true" default:"0" list:"admin" update:"admin" create:"admin_optional"` // 内存超分比 MemCmtbound float32 `nullable:"true" default:"1" list:"admin" update:"admin" create:"admin_optional"` // 存储大小,单位Mb StorageSize int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 存储类型 StorageType string `width:"20" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 存储驱动类型 StorageDriver string `width:"20" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // 存储详情 StorageInfo jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // IPMI地址 IpmiIp string `width:"16" charset:"ascii" nullable:"true" list:"admin"` // IPMI详情 IpmiInfo jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // 宿主机状态 // example: online HostStatus string `width:"16" charset:"ascii" nullable:"false" default:"offline" list:"admin"` // 宿主机类型 HostType string `width:"36" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` // host服务软件版本 Version string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"` // OVN软件版本 OvnVersion string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"` IsBaremetal bool `nullable:"true" default:"false" list:"admin" update:"admin" create:"admin_optional"` // 是否处于维护状态 IsMaintenance bool `nullable:"true" default:"false" list:"admin"` LastPingAt time.Time `` ResourceType string `width:"36" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_optional" default:"shared"` RealExternalId string `width:"256" charset:"utf8" get:"admin"` // 是否为导入的宿主机 IsImport bool `nullable:"true" default:"false" list:"admin" create:"admin_optional"` // 是否允许PXE启动 EnablePxeBoot tristate.TriState `nullable:"false" default:"true" list:"admin" create:"admin_optional" update:"admin"` // 主机UUID Uuid string `width:"64" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 主机启动模式, 可能值位PXE和ISO BootMode string `width:"8" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` } func (manager *SHostManager) GetContextManagers() [][]db.IModelManager { return [][]db.IModelManager{ {ZoneManager}, } } // 宿主机/物理机列表 func (manager *SHostManager) ListItemFilter( ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.HostListInput, ) (*sqlchemy.SQuery, error) { var err error q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput) if err != nil { return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter") } q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter") } q, err = manager.SZoneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ZonalFilterListInput) if err != nil { return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemFilter") } resType := query.ResourceType if len(resType) > 0 { switch resType { case api.HostResourceTypeShared: q = q.Filter( sqlchemy.OR( sqlchemy.IsNullOrEmpty(q.Field("resource_type")), sqlchemy.Equals(q.Field("resource_type"), api.HostResourceTypeShared), ), ) default: q = q.Equals("resource_type", resType) } } q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter") } anyMac := query.AnyMac 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 := query.Schedtag 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 := query.Wire 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 := query.Storage 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() notAttached := (query.StorageNotAttached != nil && *query.StorageNotAttached) if !notAttached { q = q.In("id", scopeQuery) } else { q = q.NotIn("id", scopeQuery) } } hypervisorStr := query.Hypervisor if len(hypervisorStr) > 0 { hostType, ok := api.HYPERVISOR_HOSTTYPE[hypervisorStr] if !ok { return nil, httperrors.NewInputParameterError("not supported hypervisor %s", hypervisorStr) } q = q.Filter(sqlchemy.Equals(q.Field("host_type"), hostType)) } usable := (query.Usable != nil && *query.Usable) if usable { hosts := HostManager.Query().SubQuery() hostwires := HostwireManager.Query().SubQuery() networks := NetworkManager.Query().SubQuery() providers := CloudproviderManager.Query().SubQuery() hostQ1 := hosts.Query(hosts.Field("id")) hostQ1 = hostQ1.Join(providers, sqlchemy.Equals(hosts.Field("manager_id"), providers.Field("id"))) hostQ1 = hostQ1.Join(hostwires, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id"))) hostQ1 = hostQ1.Join(networks, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id"))) hostQ1 = hostQ1.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) hostQ1 = hostQ1.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) hostQ1 = hostQ1.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) hostQ1 = hostQ1.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) hostQ1 = hostQ1.Filter(sqlchemy.IsTrue(hosts.Field("enabled"))) hostQ2 := hosts.Query(hosts.Field("id")) hostQ2 = hostQ2.Join(hostwires, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id"))) hostQ2 = hostQ2.Join(networks, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id"))) hostQ2 = hostQ2.Filter(sqlchemy.IsNullOrEmpty(hosts.Field("manager_id"))) hostQ2 = hostQ2.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) hostQ2 = hostQ2.Filter(sqlchemy.IsTrue(hosts.Field("enabled"))) q = q.Filter(sqlchemy.OR( sqlchemy.In(q.Field("id"), hostQ1.SubQuery()), sqlchemy.In(q.Field("id"), hostQ2.SubQuery()), )) zones := ZoneManager.Query().SubQuery() q = q.Join(zones, sqlchemy.Equals(q.Field("zone_id"), zones.Field("id"))). Filter(sqlchemy.Equals(zones.Field("status"), api.ZONE_ENABLE)) q = q.In("status", []string{api.HOST_STATUS_RUNNING, api.HOST_STATUS_READY}) q = q.Filter( sqlchemy.OR( sqlchemy.AND( sqlchemy.NotEquals(q.Field("host_type"), api.HOST_TYPE_BAREMETAL), sqlchemy.Equals(q.Field("host_status"), api.HOST_ONLINE), ), sqlchemy.Equals(q.Field("host_type"), api.HOST_TYPE_BAREMETAL), ), ) } if query.IsEmpty != nil { isEmpty := *query.IsEmpty sq := GuestManager.Query("host_id").IsNotEmpty("host_id").GroupBy("host_id").SubQuery() if isEmpty { q = q.NotIn("id", sq) } else { q = q.In("id", sq) } } if query.Baremetal != nil { isBaremetal := *query.Baremetal if isBaremetal { q = q.Equals("host_type", api.HOST_TYPE_BAREMETAL) } else { q = q.NotEquals("host_type", api.HOST_TYPE_BAREMETAL) } } if len(query.Rack) > 0 { q = q.In("rack", query.Rack) } if len(query.Slots) > 0 { q = q.In("slots", query.Slots) } if len(query.AccessMac) > 0 { q = q.In("access_mac", query.AccessMac) } if len(query.AccessIp) > 0 { q = q.In("access_ip", query.AccessIp) } if len(query.SN) > 0 { q = q.In("sn", query.SN) } if len(query.CpuCount) > 0 { q = q.In("cpu_count", query.CpuCount) } if len(query.MemSize) > 0 { q = q.In("mem_size", query.MemSize) } if len(query.StorageType) > 0 { q = q.In("storage_type", query.StorageType) } if len(query.IpmiIp) > 0 { q = q.In("ipmi_ip", query.IpmiIp) } if len(query.HostStatus) > 0 { q = q.In("host_status", query.HostStatus) } if len(query.HostType) > 0 { q = q.In("host_type", query.HostType) } if len(query.Version) > 0 { q = q.In("version", query.Version) } if len(query.OvnVersion) > 0 { q = q.In("ovn_version", query.OvnVersion) } if query.IsMaintenance != nil { if *query.IsMaintenance { q = q.IsTrue("is_maintenance") } else { q = q.IsFalse("is_maintenance") } } if query.IsImport != nil { if *query.IsImport { q = q.IsTrue("is_import") } else { q = q.IsFalse("is_import") } } if query.EnablePxeBoot != nil { if *query.EnablePxeBoot { q = q.IsTrue("enable_pxe_boot") } else { q = q.IsFalse("enable_pxe_boot") } } if len(query.Uuid) > 0 { q = q.In("uuid", query.Uuid) } if len(query.BootMode) > 0 { q = q.In("boot_mode", query.BootMode) } return q, nil } func (manager *SHostManager) OrderByExtraFields( ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.HostListInput, ) (*sqlchemy.SQuery, error) { var err error q, err = manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") } q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput) if err != nil { return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields") } q, err = manager.SZoneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ZonalFilterListInput) if err != nil { return nil, errors.Wrap(err, "SZoneResourceBaseManager.OrderByExtraFields") } return q, nil } func (manager *SHostManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } q, err = manager.SZoneResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } return q, httperrors.ErrNotFound } func (manager *SHostManager) CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*db.CustomizeListFilters, error) { filters := db.NewCustomizeListFilters() if query.Contains("cdrom_boot") { cdromBoot := jsonutils.QueryBoolean(query, "cdrom_boot", false) cdromBootF := func(obj jsonutils.JSONObject) (bool, error) { id, err := obj.GetString("id") if err != nil { return false, err } host := manager.FetchHostById(id) ipmiInfo, err := host.GetIpmiInfo() if err != nil { return false, err } if cdromBoot && ipmiInfo.CdromBoot { return true, nil } if !cdromBoot && !ipmiInfo.CdromBoot { return true, nil } return false, nil } filters.Append(cdromBootF) } return filters, 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 { return self.validateDeleteCondition(ctx, false) } func (self *SHost) ValidatePurgeCondition(ctx context.Context) error { return self.validateDeleteCondition(ctx, true) } func (self *SHost) validateDeleteCondition(ctx context.Context, purge bool) error { if !purge && self.IsBaremetal && self.HostType != api.HOST_TYPE_BAREMETAL { return httperrors.NewInvalidStatusError("Host is a converted baremetal, should be unconverted before delete") } if self.GetEnabled() { return httperrors.NewInvalidStatusError("Host is not disabled") } cnt, err := self.GetGuestCount() if err != nil { return httperrors.NewInternalServerError("getGuestCount fail %s", err) } if cnt > 0 { return httperrors.NewNotEmptyError("Not an empty host") } for _, hoststorage := range self.GetHoststorages() { storage := hoststorage.GetStorage() if storage != nil && storage.IsLocal() { cnt, err := storage.GetDiskCount() if err != nil { return httperrors.NewInternalServerError("GetDiskCount fail %s", err) } if cnt > 0 { return httperrors.NewNotEmptyError("Local host storage is not empty???") } } } return self.SEnabledStatusInfrasResourceBase.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 { DeleteResourceJointSchedtags(self, ctx, userCred) IsolatedDeviceManager.DeleteDevicesByHost(ctx, userCred, self) for _, hoststorage := range self.GetHoststorages() { storage := hoststorage.GetStorage() if storage != nil && storage.IsLocal() { cnt, err := storage.GetDiskCount() if err != nil { return err } if cnt > 0 { return httperrors.NewNotEmptyError("Inconsistent: local storage is not empty???") } } } for _, hoststorage := range self.GetHoststorages() { storage := hoststorage.GetStorage() hoststorage.Delete(ctx, userCred) if storage != nil && storage.IsLocal() { storage.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.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) } func (self *SHost) GetHoststoragesQuery() *sqlchemy.SQuery { return HoststorageManager.Query().Equals("host_id", self.Id) } func (self *SHost) GetStorageCount() (int, error) { return self.GetHoststoragesQuery().CountWithError() } 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, &hoststorage) 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) GetHoststorageByExternalId(extId string) *SHoststorage { hoststorage := SHoststorage{} hoststorage.SetModelManager(HoststorageManager, &hoststorage) hoststorages := HoststorageManager.Query().SubQuery() storages := StorageManager.Query().SubQuery() q := hoststorages.Query() q = q.Join(storages, sqlchemy.Equals(hoststorages.Field("storage_id"), storages.Field("id"))) q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id)) q = q.Filter(sqlchemy.Equals(storages.Field("external_id"), extId)) err := q.First(&hoststorage) if err != nil { log.Errorf("GetHoststorageByExternalId fail %s", err) return nil } return &hoststorage } func (self *SHost) GetStorageByFilePath(path string) *SStorage { hoststorages := self.GetHoststorages() if hoststorages == nil { return nil } for i := 0; i < len(hoststorages); i += 1 { if len(hoststorages[i].MountPoint) > 0 && strings.HasPrefix(path, hoststorages[i].MountPoint) { return hoststorages[i].GetStorage() } } return nil } 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"), api.STORAGE_BAREMETAL)) q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id)) cnt, err := q.CountWithError() if err != nil { return nil } if cnt == 1 { hs := SHoststorage{} hs.SetModelManager(HoststorageManager, &hs) err := q.First(&hs) if err != nil { log.Errorf("error %s", err) return nil } return &hs } // log.Errorf("Cannot find baremetalstorage??") return nil } func (self *SHost) SaveCleanUpdates(doUpdate func() error) (map[string]sqlchemy.SUpdateDiff, error) { return self.saveUpdates(doUpdate, true) } func (self *SHost) SaveUpdates(doUpdate func() error) (map[string]sqlchemy.SUpdateDiff, error) { return self.saveUpdates(doUpdate, false) } func (self *SHost) saveUpdates(doUpdate func() error, doSchedClean bool) (map[string]sqlchemy.SUpdateDiff, error) { diff, err := db.Update(self, doUpdate) if err != nil { return nil, err } if doSchedClean { self.ClearSchedDescCache() } return diff, nil } func (self *SHost) AllowPerformUpdateStorage( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) bool { return db.IsAdminAllowPerform(userCred, self, "update-storage") } func (self *SHost) PerformUpdateStorage( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) (jsonutils.JSONObject, error) { bs := self.GetBaremetalstorage() capacity, _ := data.Int("capacity") zoneId, _ := data.GetString("zone_id") storageCacheId, _ := data.GetString("storagecache_id") if bs == nil { // 1. create storage storage := SStorage{} storage.Name = fmt.Sprintf("storage%s", self.GetName()) storage.Capacity = capacity storage.StorageType = api.STORAGE_BAREMETAL storage.MediumType = self.StorageType storage.Cmtbound = 1.0 storage.Status = api.STORAGE_ONLINE storage.Enabled = tristate.True storage.ZoneId = zoneId storage.StoragecacheId = storageCacheId err := StorageManager.TableSpec().Insert(&storage) if err != nil { return nil, fmt.Errorf("Create baremetal storage error: %v", err) } storage.SetModelManager(StorageManager, &storage) db.OpsLog.LogEvent(&storage, db.ACT_CREATE, storage.GetShortDesc(ctx), userCred) // 2. create host storage bmStorage := SHoststorage{} bmStorage.HostId = self.Id bmStorage.StorageId = storage.Id bmStorage.RealCapacity = capacity bmStorage.MountPoint = "" err = HoststorageManager.TableSpec().Insert(&bmStorage) if err != nil { return nil, fmt.Errorf("Create baremetal hostStorage error: %v", err) } bmStorage.SetModelManager(HoststorageManager, &bmStorage) db.OpsLog.LogAttachEvent(ctx, self, &storage, userCred, bmStorage.GetShortDesc(ctx)) return nil, nil } storage := bs.GetStorage() //if capacity != int64(storage.Capacity) { diff, err := db.Update(storage, func() error { storage.Capacity = capacity storage.StoragecacheId = storageCacheId storage.Enabled = tristate.True return nil }) if err != nil { return nil, fmt.Errorf("Update baremetal storage error: %v", err) } db.OpsLog.LogEvent(storage, db.ACT_UPDATE, diff, userCred) //} return nil, nil } func (self *SHost) GetFetchUrl(disableHttps bool) string { managerUrl, err := url.Parse(self.ManagerUri) if err != nil { log.Errorf("GetFetchUrl fail to parse url: %s", err) } if disableHttps { managerUrl.Scheme = "http" } 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, strings.Split(managerUrl.Host, ":")[0], 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", api.STORAGE_BAREMETAL) } else if isBaremetal.IsFalse() { q = q.NotEquals("storage_type", api.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) IsNewNameUnique(name string, userCred mcclient.TokenCredential, kwargs *jsonutils.JSONDict) (bool, error) { q := self.Query().Equals("name", name) if kwargs != nil && kwargs.Contains("zone_id") { zoneId, _ := kwargs.GetString("zone_id") q.Equals("zone_id", zoneId) } cnt, err := q.CountWithError() if err != nil { return false, err } return cnt == 0, nil } 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.GetPublicServiceURL("compute_v2", options.Options.Region, "") if err != nil { return nil, err } var script string script += fmt.Sprintf("curl -k -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 (self *SHostManager) AllowGetPropertyNodeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) bool { return userCred.HasSystemAdminPrivilege() } func (self *SHostManager) GetPropertyNodeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) (jsonutils.JSONObject, error) { hosts := self.Query().SubQuery() q := hosts.Query(hosts.Field("host_type"), sqlchemy.SUM("node_count_total", hosts.Field("node_count"))) q, err := self.ListItemFilter(ctx, q, userCred, query) if err != nil { return nil, err } rows, err := q.GroupBy("host_type").Rows() if err != nil { return nil, err } ret := jsonutils.NewDict() defer rows.Close() for rows.Next() { var hostType string var count int64 err = rows.Scan(&hostType, &count) if err != nil { log.Errorf("Get host node count scan err: %v", err) return ret, nil } ret.Add(jsonutils.NewInt(count), hostType) } return ret, nil } func (self *SHostManager) ClearAllSchedDescCache() error { return self.ClearSchedDescSessionCache("", "") } func (self *SHostManager) ClearSchedDescCache(hostId string) error { return self.ClearSchedDescSessionCache(hostId, "") } func (self *SHostManager) ClearSchedDescSessionCache(hostId, sessionId string) error { s := auth.GetAdminSession(context.Background(), options.Options.Region, "") return modules.SchedManager.CleanCache(s, hostId, sessionId) } func (self *SHost) ClearSchedDescCache() error { return self.ClearSchedDescSessionCache("") } func (self *SHost) ClearSchedDescSessionCache(sessionId string) error { return HostManager.ClearSchedDescSessionCache(self.Id, sessionId) } func (self *SHost) AllowGetDetailsSpec(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { return db.IsAdminAllowGetSpec(userCred, self, "spec") } func (self *SHost) GetDetailsSpec(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { return GetModelSpec(HostManager, self) } func (man *SHostManager) GetSpecShouldCheckStatus(query *jsonutils.JSONDict) (bool, error) { statusCheck := true if query.Contains("is_empty") { isEmpty, err := query.Bool("is_empty") if err != nil { return statusCheck, err } if !isEmpty { statusCheck = false } } return statusCheck, nil } func (self *SHost) GetSpec(statusCheck bool) *jsonutils.JSONDict { if statusCheck { if !self.GetEnabled() { return nil } if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_PREPARE_FAIL, api.BAREMETAL_PREPARE}) || self.GetBaremetalServer() != nil { return nil } if self.MemSize == 0 || self.CpuCount == 0 { return nil } if self.ResourceType == api.HostResourceTypePrepaidRecycle { cnt, err := self.GetGuestCount() if err != nil { return nil } if cnt > 0 { // occupied return nil } } if len(self.ManagerId) > 0 { providerObj, _ := CloudproviderManager.FetchById(self.ManagerId) if providerObj == nil { return nil } provider := providerObj.(*SCloudprovider) if !provider.IsAvailable() { return nil } } } spec := self.GetHardwareSpecification() specInfo := new(api.HostSpec) if err := spec.Unmarshal(specInfo); err != nil { return spec } nifs := self.GetNetInterfaces() var nicCount int for _, nif := range nifs { if nif.NicType != api.NIC_TYPE_IPMI { nicCount++ } } specInfo.NicCount = nicCount var manufacture string var model string if self.SysInfo != nil { manufacture, _ = self.SysInfo.GetString("manufacture") model, _ = self.SysInfo.GetString("model") } if manufacture == "" { manufacture = "Unknown" } if model == "" { model = "Unknown" } specInfo.Manufacture = manufacture specInfo.Model = model return specInfo.JSON(specInfo) } func (manager *SHostManager) GetSpecIdent(input *jsonutils.JSONDict) []string { spec := new(api.HostSpec) input.Unmarshal(spec) specKeys := []string{ fmt.Sprintf("cpu:%d", spec.Cpu), fmt.Sprintf("mem:%dM", spec.Mem), fmt.Sprintf("nic:%d", spec.NicCount), fmt.Sprintf("manufacture:%s", spec.Manufacture), fmt.Sprintf("model:%s", spec.Model), } diskDriverSpec := spec.Disk if diskDriverSpec != nil { for driver, driverSpec := range diskDriverSpec { specKeys = append(specKeys, parseDiskDriverSpec(driver, driverSpec)...) } } sort.Strings(specKeys) return specKeys } func parseDiskDriverSpec(driver string, adapterSpecs api.DiskAdapterSpec) []string { ret := make([]string, 0) for adapterKey, adapterSpec := range adapterSpecs { for _, diskSpec := range adapterSpec { sizeGB, _ := utils.GetSizeGB(fmt.Sprintf("%d", diskSpec.Size), "M") diskKey := fmt.Sprintf("disk:%s_%s_%s_%dGx%d", driver, adapterKey, diskSpec.Type, sizeGB, diskSpec.Count) ret = append(ret, diskKey) } } return ret } func ConvertStorageInfo2BaremetalStorages(storageInfo jsonutils.JSONObject) []*baremetal.BaremetalStorage { if storageInfo == nil { return nil } 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) api.DiskDriverSpec { refStorages := ConvertStorageInfo2BaremetalStorages(storageInfo) if refStorages == nil { return nil } return baremetal.GetDiskSpecV2(refStorages) } func (self *SHost) GetHardwareSpecification() *jsonutils.JSONDict { spec := &api.HostSpec{ Cpu: int(self.CpuCount), Mem: self.MemSize, } if self.StorageInfo != nil { spec.Disk = GetDiskSpecV2(self.StorageInfo) spec.Driver = self.StorageDriver } ret := spec.JSON(spec) if self.StorageInfo != nil { ret.Set("storage_info", self.StorageInfo) } return ret } type SStorageCapacity struct { Capacity int64 `json:"capacity,omitzero"` Used int64 `json:"used_capacity,omitzero"` Wasted int64 `json:"waste_capacity,omitzero"` VCapacity int64 `json:"virtual_capacity,omitzero"` } func (cap *SStorageCapacity) GetFree() int64 { return cap.VCapacity - cap.Used - cap.Wasted } func (cap *SStorageCapacity) GetCommitRate() float64 { if cap.Capacity > 0 { return float64(int(float64(cap.Used)*100.0/float64(cap.Capacity)+0.5)) / 100.0 } else { return 0.0 } } func (cap *SStorageCapacity) Add(cap2 SStorageCapacity) { cap.Capacity += cap2.Capacity cap.Used += cap2.Used cap.Wasted += cap2.Wasted cap.VCapacity += cap2.VCapacity } func (cap *SStorageCapacity) toCapacityInfo() api.SStorageCapacityInfo { info := api.SStorageCapacityInfo{} info.Capacity = cap.Capacity info.UsedCapacity = cap.Used info.WasteCapacity = cap.Wasted info.VirtualCapacity = cap.VCapacity info.CommitRate = cap.GetCommitRate() info.FreeCapacity = cap.GetFree() return info } func (self *SHost) GetAttachedLocalStorageCapacity() SStorageCapacity { ret := SStorageCapacity{} storages := self.GetAttachedStorages("") for _, s := range storages { if !utils.IsInStringArray(s.StorageType, api.HOST_STORAGE_LOCAL_TYPES) { continue } ret.Add(s.getStorageCapacity()) } return ret } func (self *SHost) GetAttachedLocalStorages() []SStorage { ret := make([]SStorage, 0) storages := self.GetAttachedStorages("") for _, s := range storages { if !utils.IsInStringArray(s.StorageType, api.HOST_STORAGE_LOCAL_TYPES) { continue } ret = append(ret, s) } return ret } func _getLeastUsedStorage(storages []SStorage, backends []string) *SStorage { var best *SStorage var bestCap int64 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 == api.STORAGE_LOCAL { backends = []string{api.STORAGE_NAS, api.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, error) { return self.GetWiresQuery().CountWithError() } 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, &hw) 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, &wire) err := q.First(&wire) if err != nil { log.Errorf("GetMasterWire fail %s", err) return nil } return &wire } func (self *SHost) getHostwiresOfId(wireId string) []SHostwire { hostwires := make([]SHostwire, 0) q := self.GetWiresQuery().Equals("wire_id", wireId) err := db.FetchModelObjects(HostwireManager, q, &hostwires) if err != nil { log.Errorf("getHostwiresOfId fail %s", err) return nil } return hostwires } func (self *SHost) getHostwireOfIdAndMac(wireId string, mac string) *SHostwire { hostwire := SHostwire{} hostwire.SetModelManager(HostwireManager, &hostwire) q := self.GetWiresQuery().Equals("wire_id", wireId) q = q.Equals("mac_addr", mac) err := q.First(&hostwire) if err != nil { log.Errorf("getHostwireOfIdAndMac 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, error) { q := self.GetGuestsQuery() return q.CountWithError() } func (self *SHost) GetContainerCount(status []string) (int, error) { q := self.GetGuestsQuery() q = q.Filter(sqlchemy.Equals(q.Field("hypervisor"), api.HYPERVISOR_CONTAINER)) if len(status) > 0 { q = q.In("status", status) } return q.CountWithError() } func (self *SHost) GetNonsystemGuestCount() (int, error) { q := self.GetGuestsQuery() q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("is_system")), sqlchemy.IsFalse(q.Field("is_system")))) return q.CountWithError() } func (self *SHost) GetRunningGuestCount() (int, error) { q := self.GetGuestsQuery() q = q.In("status", api.VM_RUNNING_STATUS) return q.CountWithError() } func (self *SHost) GetRunningGuestMemorySize() int { res := self.getGuestsResource(api.VM_RUNNING) if res != nil { return res.GuestVmemSize } return -1 } 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(netId string) *SHostnetwork { q := self.GetBaremetalnetworksQuery() netifs := NetInterfaceManager.Query().Equals("baremetal_id", self.Id) netifs = netifs.Filter(sqlchemy.OR( sqlchemy.IsNullOrEmpty(netifs.Field("nic_type")), sqlchemy.NotEquals(netifs.Field("nic_type"), api.NIC_TYPE_IPMI), )) netifsSub := netifs.SubQuery() q = q.Join(netifsSub, sqlchemy.Equals(q.Field("mac_addr"), netifsSub.Field("mac"))) q = q.Equals("network_id", netId) hn := SHostnetwork{} hn.SetModelManager(HostnetworkManager, &hn) err := q.First(&hn) if err != nil { log.Errorf("GetAttach2Network fail %s", err) return nil } return &hn } 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, &netif) q := NetInterfaceManager.Query().Equals("baremetal_id", self.Id).Equals("nic_type", api.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(ctx, 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, api.HOST_TYPES) { log.Fatalf("Unsupported host type %s", self.HostType) } return GetHostDriver(self.HostType) } func (manager *SHostManager) getHostsByZoneProvider(zone *SZone, provider *SCloudprovider) ([]SHost, error) { hosts := make([]SHost, 0) q := manager.Query() if zone != nil { q = q.Equals("zone_id", zone.Id) } if provider != nil { q = q.Equals("manager_id", provider.Id) } // exclude prepaid_recycle fake hosts q = q.NotEquals("resource_type", api.HostResourceTypePrepaidRecycle) 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) ([]SHost, []cloudprovider.ICloudHost, compare.SyncResult) { lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, userCred)) defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, userCred)) localHosts := make([]SHost, 0) remoteHosts := make([]cloudprovider.ICloudHost, 0) syncResult := compare.SyncResult{} dbHosts, err := manager.getHostsByZoneProvider(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 { if removed[i].IsPrepaidRecycleResource() { continue } err = removed[i].syncRemoveCloudHost(ctx, userCred) if err != nil { syncResult.DeleteError(err) } else { syncResult.Delete() } } for i := 0; i < len(commondb); i += 1 { err = commondb[i].syncWithCloudHost(ctx, userCred, commonext[i], provider) 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(ctx, userCred, added[i], provider, 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) syncRemoveCloudHost(ctx context.Context, userCred mcclient.TokenCredential) error { lockman.LockObject(ctx, self) defer lockman.ReleaseObject(ctx, self) err := self.ValidatePurgeCondition(ctx) if err != nil { err = self.SetStatus(userCred, api.HOST_OFFLINE, "sync to delete") if err == nil { _, err = self.PerformDisable(ctx, userCred, nil, apis.PerformDisableInput{}) } guests := self.GetGuests() for _, guest := range guests { err = guest.SetStatus(userCred, api.VM_UNKNOWN, "sync to delete") if err != nil { return err } } } else { err = self.RealDelete(ctx, userCred) } return err } func (self *SHost) syncWithCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost, provider *SCloudprovider) error { diff, err := db.UpdateWithLock(ctx, 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() if cpuCmt := extHost.GetCpuCmtbound(); cpuCmt > 0 { self.CpuCmtbound = cpuCmt } if memCmt := extHost.GetMemCmtbound(); memCmt > 0 { self.MemCmtbound = memCmt } if reservedMem := extHost.GetReservedMemoryMb(); reservedMem > 0 { self.MemReserved = reservedMem } self.IsEmulated = extHost.IsEmulated() self.SetEnabled(extHost.GetEnabled()) self.IsMaintenance = extHost.GetIsMaintenance() self.Version = extHost.GetVersion() return nil }) if err != nil { log.Errorf("syncWithCloudZone error %s", err) return err } db.OpsLog.LogSyncUpdate(self, diff, userCred) if provider != nil { SyncCloudDomain(userCred, self, provider.GetOwnerId()) self.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) } if err := HostManager.ClearSchedDescCache(self.Id); err != nil { log.Errorf("ClearSchedDescCache for host %s error %v", self.Name, err) } return nil } func (self *SHost) syncWithCloudPrepaidVM(extVM cloudprovider.ICloudVM, host *SHost) error { _, err := self.SaveUpdates(func() error { self.CpuCount = extVM.GetVcpuCount() self.MemSize = extVM.GetVmemSizeMB() self.BillingType = extVM.GetBillingType() self.ExpiredAt = extVM.GetExpiredAt() self.ExternalId = host.ExternalId return nil }) if err != nil { log.Errorf("syncWithCloudZone error %s", err) } 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(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost, provider *SCloudprovider, izone *SZone) (*SHost, error) { host := SHost{} host.SetModelManager(manager, &host) if izone == nil { // onpremise host accessIp := extHost.GetAccessIp() if len(accessIp) == 0 { msg := fmt.Sprintf("fail to find wire for host %s: empty host access ip", extHost.GetName()) log.Errorf(msg) return nil, fmt.Errorf(msg) } wire, err := WireManager.GetOnPremiseWireOfIp(accessIp) if err != nil { msg := fmt.Sprintf("fail to find wire for host %s %s: %s", extHost.GetName(), accessIp, err) log.Errorf(msg) return nil, fmt.Errorf(msg) } izone = wire.GetZone() } newName, err := db.GenerateName(manager, userCred, extHost.GetName()) if err != nil { return nil, fmt.Errorf("generate name fail %s", err) } host.Name = newName host.ExternalId = extHost.GetGlobalId() host.ZoneId = izone.Id host.HostType = extHost.GetHostType() host.Status = extHost.GetStatus() host.HostStatus = extHost.GetHostStatus() host.SetEnabled(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.CpuCmtbound = 8.0 if cpuCmt := extHost.GetCpuCmtbound(); cpuCmt > 0 { host.CpuCmtbound = cpuCmt } host.MemCmtbound = 1.0 if memCmt := extHost.GetMemCmtbound(); memCmt > 0 { host.MemCmtbound = memCmt } if reservedMem := extHost.GetReservedMemoryMb(); reservedMem > 0 { host.MemReserved = reservedMem } host.ManagerId = provider.Id host.IsEmulated = extHost.IsEmulated() host.IsMaintenance = extHost.GetIsMaintenance() host.Version = extHost.GetVersion() host.IsPublic = false host.PublicScope = string(rbacutils.ScopeNone) err = manager.TableSpec().Insert(&host) if err != nil { log.Errorf("newFromCloudHost fail %s", err) return nil, err } db.OpsLog.LogEvent(&host, db.ACT_CREATE, host.GetShortDesc(ctx), userCred) SyncCloudDomain(userCred, &host, provider.GetOwnerId()) if provider != nil { host.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) } 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, provider *SCloudprovider) ([]SStorage, []cloudprovider.ICloudStorage, compare.SyncResult) { lockman.LockClass(ctx, StorageManager, db.GetLockClassKey(StorageManager, userCred)) defer lockman.ReleaseClass(ctx, StorageManager, db.GetLockClassKey(StorageManager, userCred)) localStorages := make([]SStorage, 0) remoteStorages := make([]cloudprovider.ICloudStorage, 0) 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 nil, nil, 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) err := self.syncRemoveCloudHostStorage(ctx, userCred, &removed[i]) if errors.Cause(err) == ErrStorageInUse && removed[i].StorageType == api.STORAGE_LOCAL { removed[i].SetStatus(userCred, api.STORAGE_OFFLINE, "the only host used this local storage has detached") // prevent generating a delete error for syncResult continue } 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(ctx, userCred, &commondb[i], commonext[i]) if err != nil { syncResult.UpdateError(err) } else { localStorages = append(localStorages, commondb[i]) remoteStorages = append(remoteStorages, commonext[i]) 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()) local, err := self.newCloudHostStorage(ctx, userCred, added[i], provider) if err != nil { syncResult.AddError(err) } else { localStorages = append(localStorages, *local) remoteStorages = append(remoteStorages, added[i]) syncResult.Add() } } return localStorages, remoteStorages, syncResult } func (self *SHost) syncRemoveCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, localStorage *SStorage) error { hs := self.GetHoststorageOfId(localStorage.Id) err := hs.ValidateDeleteCondition(ctx) if err == nil { log.Errorf("sync remove hoststorage fail: %s", err) err = hs.Detach(ctx, userCred) } else { } return err } func (self *SHost) syncWithCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, localStorage *SStorage, extStorage cloudprovider.ICloudStorage) error { // do nothing hs := self.GetHoststorageOfId(localStorage.Id) err := hs.syncWithCloudHostStorage(userCred, extStorage) if err != nil { return err } s := hs.GetStorage() err = s.syncWithCloudStorage(ctx, userCred, extStorage, nil) return err } func (self *SHost) isAttach2Storage(storage *SStorage) bool { hs := self.GetHoststorageOfId(storage.Id) return hs != nil } func (self *SHost) Attach2Storage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, mountPoint string) error { if self.isAttach2Storage(storage) { return nil } hs := SHoststorage{} hs.SetModelManager(HoststorageManager, &hs) hs.StorageId = storage.Id hs.HostId = self.Id hs.MountPoint = mountPoint err := HoststorageManager.TableSpec().Insert(&hs) if err != nil { return err } db.OpsLog.LogAttachEvent(ctx, self, storage, userCred, nil) return nil } func (self *SHost) newCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage, provider *SCloudprovider) (*SStorage, error) { storageObj, err := db.FetchByExternalId(StorageManager, extStorage.GetGlobalId()) if err != nil { if err == sql.ErrNoRows { // no cloud storage found, this may happen for on-premise host // create the storage right now storageObj, err = StorageManager.newFromCloudStorage(ctx, userCred, extStorage, provider, self.GetZone()) if err != nil { log.Errorf("create by cloud storage fail %s", err) return nil, err } } else { log.Errorf("%s", err) return nil, err } } storage := storageObj.(*SStorage) err = self.Attach2Storage(ctx, userCred, storage, extStorage.GetMountPoint()) return storage, err } func (self *SHost) SyncHostWires(ctx context.Context, userCred mcclient.TokenCredential, wires []cloudprovider.ICloudWire) compare.SyncResult { lockman.LockObject(ctx, self) defer lockman.ReleaseObject(ctx, self) 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) err := self.syncRemoveCloudHostWire(ctx, userCred, &removed[i]) 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) syncRemoveCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, localwire *SWire) error { hws := self.getHostwiresOfId(localwire.Id) for i := range hws { err := hws[i].Detach(ctx, userCred) if err != nil { return err } } return nil } 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) hs.WireId = wire.Id hs.HostId = self.Id err := HostwireManager.TableSpec().Insert(&hs) if err != nil { return err } db.OpsLog.LogAttachEvent(ctx, self, wire, userCred, nil) return nil } func (self *SHost) newCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire) error { wireObj, err := db.FetchByExternalId(WireManager, extWire.GetGlobalId()) if err != nil { log.Errorf("%s", err) return nil } wire := wireObj.(*SWire) err = self.Attach2Wire(ctx, userCred, wire) return err } type SGuestSyncResult struct { Local *SGuest Remote cloudprovider.ICloudVM IsNew bool } func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCredential, iprovider cloudprovider.ICloudProvider, vms []cloudprovider.ICloudVM, syncOwnerId mcclient.IIdentityProvider) ([]SGuestSyncResult, compare.SyncResult) { lockman.LockClass(ctx, GuestManager, db.GetLockClassKey(GuestManager, syncOwnerId)) defer lockman.ReleaseClass(ctx, GuestManager, db.GetLockClassKey(GuestManager, syncOwnerId)) syncVMPairs := make([]SGuestSyncResult, 0) syncResult := compare.SyncResult{} dbVMs := self.GetGuests() for i := range dbVMs { if taskman.TaskManager.IsInTask(&dbVMs[i]) { syncResult.Error(fmt.Errorf("server %s(%s)in task", dbVMs[i].Name, dbVMs[i].Id)) return nil, syncResult } } 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, syncResult } for i := 0; i < len(removed); i += 1 { err := removed[i].syncRemoveCloudVM(ctx, userCred) if err != nil { syncResult.DeleteError(err) } else { syncResult.Delete() } } for i := 0; i < len(commondb); i += 1 { err := commondb[i].syncWithCloudVM(ctx, userCred, iprovider, self, commonext[i], syncOwnerId) if err != nil { syncResult.UpdateError(err) } else { syncVMPair := SGuestSyncResult{ Local: &commondb[i], Remote: commonext[i], IsNew: false, } syncVMPairs = append(syncVMPairs, syncVMPair) syncResult.Update() } } for i := 0; i < len(added); i += 1 { vm, err := db.FetchByExternalId(GuestManager, added[i].GetGlobalId()) if err != nil && err != sql.ErrNoRows { log.Errorf("failed to found guest by externalId %s error: %v", added[i].GetGlobalId(), err) continue } if vm != nil { guest := vm.(*SGuest) ihost := added[i].GetIHost() if ihost == nil { log.Errorf("failed to found ihost from vm %s", added[i].GetGlobalId()) continue } _host, err := db.FetchByExternalId(HostManager, ihost.GetGlobalId()) if err != nil { log.Errorf("failed to found host by externalId %s", ihost.GetGlobalId()) continue } host := _host.(*SHost) err = guest.syncWithCloudVM(ctx, userCred, iprovider, host, added[i], syncOwnerId) if err != nil { syncResult.UpdateError(err) } else { syncResult.Update() } continue } if added[i].GetBillingType() == billing_api.BILLING_TYPE_PREPAID { vhost := HostManager.GetHostByRealExternalId(added[i].GetGlobalId()) if vhost != nil { // this recycle vm is not build yet, skip synchronize err = vhost.SyncWithRealPrepaidVM(ctx, userCred, added[i]) if err != nil { syncResult.AddError(err) } continue } } new, err := GuestManager.newCloudVM(ctx, userCred, iprovider, self, added[i], syncOwnerId) if err != nil { syncResult.AddError(err) } else { syncVMPair := SGuestSyncResult{ Local: new, Remote: added[i], IsNew: true, } syncVMPairs = append(syncVMPairs, syncVMPair) syncResult.Add() } } return syncVMPairs, syncResult } func (self *SHost) getNetworkOfIPOnHost(ipAddr string) (*SNetwork, error) { netInterfaces := self.GetNetInterfaces() for _, netInterface := range netInterfaces { network, err := netInterface.GetCandidateNetworkForIp(auth.AdminCredential(), ipAddr) if err == nil && network != nil { return network, nil } } return nil, fmt.Errorf("IP %s not reachable on this host", ipAddr) } func (self *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) ([]SNetInterface, *SNetwork) { netObj, err := NetworkManager.FetchById(netId) if err != nil { return nil, nil } net := netObj.(*SNetwork) used, err := net.getFreeAddressCount() if err != nil { return nil, nil } if used == 0 && !reserved { return nil, nil } matchNetIfs := make([]SNetInterface, 0) netifs := self.GetNetInterfaces() for i := 0; i < len(netifs); i++ { if !netifs[i].IsUsableServernic() { continue } if netifs[i].WireId == net.WireId { matchNetIfs = append(matchNetIfs, netifs[i]) // return &netifs[i], net } } if len(matchNetIfs) > 0 { return matchNetIfs, net } return nil, nil } func (self *SHost) GetNetworkWithId(netId string, reserved bool) (*SNetwork, error) { var q1, q2 *sqlchemy.SQuery { networks := NetworkManager.Query() hostwires := HostwireManager.Query().SubQuery() hosts := HostManager.Query().SubQuery() q1 = networks q1 = q1.Join(hostwires, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id"))) q1 = q1.Join(hosts, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id"))) q1 = q1.Filter(sqlchemy.Equals(networks.Field("id"), netId)) q1 = q1.Filter(sqlchemy.Equals(hosts.Field("id"), self.Id)) } { networks := NetworkManager.Query() wires := WireManager.Query().SubQuery() vpcs := VpcManager.Query().SubQuery() regions := CloudregionManager.Query().SubQuery() q2 = networks q2 = q2.Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))) q2 = q2.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id"))) q2 = q2.Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id"))) q2 = q2.Filter(sqlchemy.Equals(networks.Field("id"), netId)) q2 = q2.Filter(sqlchemy.AND( sqlchemy.Equals(regions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD), sqlchemy.NOT(sqlchemy.Equals(vpcs.Field("id"), api.DEFAULT_VPC_ID)), )) } q := sqlchemy.Union(q1, q2).Query() net := SNetwork{} net.SetModelManager(NetworkManager, &net) err := q.First(&net) if err != nil { return nil, err } if reserved { return &net, nil } freeCnt, err := net.getFreeAddressCount() if err != nil { return nil, err } if freeCnt > 0 { return &net, nil } return nil, fmt.Errorf("No IP address") } func (manager *SHostManager) FetchHostById(hostId string) *SHost { host := SHost{} host.SetModelManager(manager, &host) 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.IIdentityProvider, scope rbacutils.TRbacScope, rangeObjs []db.IStandaloneModel, hostStatus, status string, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, enabled, isBaremetal tristate.TriState, ) *sqlchemy.SQuery { hosts := manager.Query().SubQuery() q := hosts.Query( hosts.Field("mem_size"), hosts.Field("mem_reserved"), hosts.Field("mem_cmtbound"), hosts.Field("cpu_count"), hosts.Field("cpu_reserved"), hosts.Field("cpu_cmtbound"), hosts.Field("storage_size"), ) if scope != rbacutils.ScopeSystem && userCred != nil { q = q.Filter(sqlchemy.Equals(hosts.Field("domain_id"), userCred.GetProjectDomainId())) } 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, hostTypes, resourceTypes, providers, brands, cloudEnv, rangeObjs) return q } type HostStat struct { MemSize int MemReserved int MemCmtbound float32 CpuCount int CpuReserved int 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.IIdentityProvider, scope rbacutils.TRbacScope, rangeObjs []db.IStandaloneModel, hostStatus, status string, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, enabled, isBaremetal tristate.TriState, ) HostsCountStat { return manager.calculateCount( manager.totalCountQ( userCred, scope, rangeObjs, hostStatus, status, hostTypes, resourceTypes, providers, brands, cloudEnv, enabled, isBaremetal, ), ) } /* func (self *SHost) GetIZone() (cloudprovider.ICloudZone, error) { provider, err := self.GetCloudProvider() if err != nil { return nil, fmt.Errorf("No cloudprovider 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) { host, _, err := self.GetIHostAndProvider() return host, err } func (self *SHost) GetIHostAndProvider() (cloudprovider.ICloudHost, cloudprovider.ICloudProvider, error) { provider, err := self.GetDriver() if err != nil { return nil, nil, fmt.Errorf("No cloudprovider for host: %s", err) } var iregion cloudprovider.ICloudRegion if provider.GetFactory().IsOnPremise() { iregion, err = provider.GetOnPremiseIRegion() } else { region := self.GetRegion() if region == nil { msg := "fail to find region of host???" log.Errorf(msg) return nil, nil, fmt.Errorf(msg) } iregion, err = provider.GetIRegionById(region.ExternalId) } if err != nil { log.Errorf("fail to find iregion: %s", err) return nil, nil, err } ihost, err := iregion.GetIHostById(self.ExternalId) if err != nil { if err == cloudprovider.ErrNotFound { return nil, nil, cloudprovider.ErrNotFound } log.Errorf("fail to find ihost by id %s %s", self.ExternalId, err) return nil, nil, fmt.Errorf("fail to find ihost by id %s", err) } return ihost, provider, nil } func (self *SHost) GetIRegion() (cloudprovider.ICloudRegion, error) { provider, err := self.GetDriver() if err != nil { return nil, fmt.Errorf("No cloudprovider 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 } guest := SGuest{} guest.SetModelManager(GuestManager, &guest) q := GuestManager.Query().Equals("host_id", self.Id).Equals("hypervisor", api.HOST_TYPE_BAREMETAL) err := q.First(&guest) if err != nil { if err != sql.ErrNoRows { log.Errorf("query fail %s", err) } return nil } return &guest } func (self *SHost) GetSchedtags() []SSchedtag { return GetSchedtags(HostschedtagManager, self.Id) } 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"))) cond := sqlchemy.OR(sqlchemy.Equals(q.Field("host_id"), self.Id), sqlchemy.Equals(q.Field("backup_host_id"), self.Id)) q = q.Filter(cond) 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, out api.HostDetails, showReason bool) api.HostDetails { server := self.GetBaremetalServer() if server != nil { out.ServerId = server.Id out.Server = server.Name if self.HostType == api.HOST_TYPE_BAREMETAL { out.ServerIps = strings.Join(server.GetRealIPs(), ",") } } 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) } out.NicCount = len(nicInfos) out.NicInfo = nicInfos } out.Schedtags = GetSchedtagsDetailsToResourceV2(self, ctx) var usage *SHostGuestResourceUsage if options.Options.IgnoreNonrunningGuests { usage = self.getGuestsResource(api.VM_RUNNING) } else { usage = self.getGuestsResource("") } if usage != nil { out.CpuCommit = usage.GuestVcpuCount out.MemCommit = usage.GuestVmemSize } containerCount, _ := self.GetContainerCount(nil) runningContainerCount, _ := self.GetContainerCount(api.VM_RUNNING_STATUS) guestCount, _ := self.GetGuestCount() nonesysGuestCnt, _ := self.GetNonsystemGuestCount() runningGuestCnt, _ := self.GetRunningGuestCount() out.Guests = guestCount - containerCount out.NonsystemGuests = nonesysGuestCnt - containerCount out.RunningGuests = runningGuestCnt - runningContainerCount totalCpu := self.GetCpuCount() cpuCommitRate := 0.0 if totalCpu > 0 && usage.GuestVcpuCount > 0 { cpuCommitRate = float64(usage.GuestVcpuCount) * 1.0 / float64(totalCpu) } out.CpuCommitRate = cpuCommitRate totalMem := self.GetMemSize() memCommitRate := 0.0 if totalMem > 0 && usage.GuestVmemSize > 0 { memCommitRate = float64(usage.GuestVmemSize) * 1.0 / float64(totalMem) } out.MemCommitRate = memCommitRate capa := self.GetAttachedLocalStorageCapacity() out.Storage = capa.Capacity out.StorageUsed = capa.Used out.StorageWaste = capa.Wasted out.StorageVirtual = capa.VCapacity out.StorageFree = capa.GetFree() out.StorageCommitRate = capa.GetCommitRate() out.Spec = self.GetHardwareSpecification() // extra = self.SManagedResourceBase.getExtraDetails(ctx, extra) out.IsPrepaidRecycle = false if self.IsPrepaidRecycle() { out.IsPrepaidRecycle = true } if self.IsBaremetal { out.CanPrepare = true err := self.canPrepare() if err != nil { out.CanPrepare = false if showReason { out.PrepareFailReason = err.Error() } } } return out } func (self *SHost) GetMetadataHiddenKeys() []string { return []string{} } func (self *SHost) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (api.HostDetails, error) { return api.HostDetails{}, nil } func (manager *SHostManager) FetchCustomizeColumns( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool, ) []api.HostDetails { rows := make([]api.HostDetails, len(objs)) stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) showReason := false if query.Contains("show_fail_reason") { showReason = true } for i := range rows { rows[i] = api.HostDetails{ EnabledStatusInfrasResourceBaseDetails: stdRows[i], ManagedResourceInfo: managerRows[i], ZoneResourceInfo: zoneRows[i], } rows[i] = objs[i].(*SHost).getMoreDetails(ctx, rows[i], showReason) } return rows } func (self *SHost) AllowGetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { return db.IsAdminAllowGetSpec(userCred, self, "vnc") } func (self *SHost) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.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 db.IsAdminAllowGetSpec(userCred, self, "ipmi") } func (self *SHost) GetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { ret, ok := self.IpmiInfo.(*jsonutils.JSONDict) if !ok { return nil, httperrors.NewNotFoundError("No ipmi information was found for host %s", self.Name) } password, err := ret.GetString("password") if err != nil { return nil, httperrors.NewNotFoundError("IPMI has no password information") } 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 { zones := ZoneManager.Query().Equals("cloudregion_id", regionId).SubQuery() hosts := HostManager.Query() q := hosts.Equals("manager_id", managerId) q = q.Join(zones, sqlchemy.Equals(zones.Field("id"), hosts.Field("zone_id"))) 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) Request(ctx context.Context, userCred mcclient.TokenCredential, method httputils.THttpMethod, url string, headers http.Header, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { s := auth.GetSession(ctx, userCred, "", "") _, ret, err := s.JSONRequest(self.ManagerUri, "", method, url, headers, body) return ret, err } func (self *SHost) GetLocalStoragecache() *SStoragecache { localStorages := self.GetAttachedStorages(api.STORAGE_LOCAL) for i := 0; i < len(localStorages); i += 1 { sc := localStorages[i].GetStoragecache() if sc != nil { return sc } } return nil } func (self *SHost) GetStoragecache() *SStoragecache { localStorages := self.GetAttachedStorages("") for i := 0; i < len(localStorages); i += 1 { sc := localStorages[i].GetStoragecache() if sc != nil { return sc } } return nil } func (self *SHost) PostCreate( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject, ) { self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data) input := api.HostCreateInput{} err := data.Unmarshal(&input) if err != nil { log.Errorf("data.Unmarshal fail %s", err) return } kwargs := data.(*jsonutils.JSONDict) ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, self.Id) if err != nil { log.Errorf("fetchIpmiInfo fail %s", err) return } ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) if ipmiInfoJson.Length() > 0 { _, err := self.SaveUpdates(func() error { self.IpmiInfo = ipmiInfoJson return nil }) if err != nil { log.Errorln(err.Error()) } else if len(ipmiInfo.IpAddr) > 0 { self.setIpmiIp(userCred, ipmiInfo.IpAddr) } } if len(input.AccessIp) > 0 { self.setAccessIp(userCred, input.AccessIp) } if len(input.AccessMac) > 0 { self.setAccessMac(userCred, input.AccessMac) } noProbe := false if input.NoProbe != nil { noProbe = *input.NoProbe } if len(self.ZoneId) > 0 && self.HostType == api.HOST_TYPE_BAREMETAL && !noProbe { // ipmiInfo, _ := self.GetIpmiInfo() if len(ipmiInfo.IpAddr) > 0 { self.StartBaremetalCreateTask(ctx, userCred, kwargs, "") } } keys := GetHostQuotaKeysFromCreateInput(input) quota := SInfrasQuota{Host: 1} quota.SetKeys(keys) err = quotas.CancelPendingUsage(ctx, userCred, "a, "a, true) if err != nil { log.Errorf("CancelPendingUsage fail %s", err) } } func (self *SHost) StartBaremetalCreateTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error { if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCreateTask", self, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (manager *SHostManager) ValidateSizeParams(input api.HostSizeAttributes) (api.HostSizeAttributes, error) { memStr := input.MemSize if len(memStr) > 0 { if !regutils.MatchSize(memStr) { return input, errors.Wrap(httperrors.ErrInputParameter, "Memory size must be number[+unit], like 256M, 1G or 256") } memSize, err := fileutils.GetSizeMb(memStr, 'M', 1024) if err != nil { return input, errors.Wrap(err, "fileutils.GetSizeMb") } input.MemSize = strconv.FormatInt(int64(memSize), 10) // data.Set("mem_size", jsonutils.NewInt(int64(memSize))) } memReservedStr := input.MemReserved if len(memReservedStr) > 0 { if !regutils.MatchSize(memReservedStr) { return input, errors.Wrap(httperrors.ErrInputParameter, "Memory size must be number[+unit], like 256M, 1G or 256") } memSize, err := fileutils.GetSizeMb(memReservedStr, 'M', 1024) if err != nil { return input, errors.Wrap(err, "fileutils.GetSizeMb") } input.MemReserved = strconv.FormatInt(int64(memSize), 10) // data.Set("mem_reserved", jsonutils.NewInt(int64(memSize))) } cpuCacheStr := input.CpuCache if len(cpuCacheStr) > 0 { if !regutils.MatchSize(cpuCacheStr) { return input, errors.Wrapf(httperrors.ErrInputParameter, "Illegal cpu cache size %s", cpuCacheStr) } cpuCache, err := fileutils.GetSizeKb(cpuCacheStr, 'K', 1024) if err != nil { return input, errors.Wrap(err, "fileutils.GetSizeKb") } input.CpuCache = strconv.FormatInt(int64(cpuCache), 10) // data.Set("cpu_cache", jsonutils.NewInt(int64(cpuCache))) } return input, nil } func (manager *SHostManager) inputUniquenessCheck(input api.HostAccessAttributes, zoneId string, hostId string) (api.HostAccessAttributes, error) { for key, val := range map[string]string{ "manager_uri": input.ManagerUri, "access_ip": input.AccessIp, } { if len(val) > 0 { q := manager.Query().Equals(key, val) if len(zoneId) > 0 { q = q.Equals("zone_id", zoneId) } else { q = q.IsNullOrEmpty("zone_id") } if len(hostId) > 0 { q = q.NotEquals("id", hostId) } cnt, err := q.CountWithError() if err != nil { return input, httperrors.NewInternalServerError("check %s duplication fail %s", key, err) } if cnt > 0 { return input, httperrors.NewConflictError("duplicate %s %s", key, val) } } } accessMac := input.AccessMac if len(accessMac) > 0 { accessMac2 := netutils.FormatMacAddr(accessMac) if len(accessMac2) == 0 { return input, httperrors.NewInputParameterError("invalid macAddr %s", accessMac) } if accessMac2 != api.ACCESS_MAC_ANY { q := manager.Query().Equals("access_mac", accessMac2) if len(hostId) > 0 { q = q.NotEquals("id", hostId) } cnt, err := q.CountWithError() if err != nil { return input, httperrors.NewInternalServerError("check access_mac duplication fail %s", err) } if cnt > 0 { return input, httperrors.NewConflictError("duplicate access_mac %s", accessMac) } input.AccessMac = accessMac2 } } return input, nil } func (manager *SHostManager) ValidateCreateData( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.HostCreateInput, ) (api.HostCreateInput, error) { var err error if len(input.Zone) > 0 { _, input.ZoneResourceInput, err = ValidateZoneResourceInput(userCred, input.ZoneResourceInput) if err != nil { return input, errors.Wrap(err, "ValidateZoneResourceInput") } } noProbe := false if input.NoProbe != nil { noProbe = *input.NoProbe } input.HostAccessAttributes, err = manager.inputUniquenessCheck(input.HostAccessAttributes, input.Zone, "") if err != nil { return input, errors.Wrap(err, "manager.inputUniquenessCheck") } input.HostSizeAttributes, err = manager.ValidateSizeParams(input.HostSizeAttributes) if err != nil { return input, errors.Wrap(err, "manager.ValidateSizeParams") } if len(input.MemReserved) == 0 { if input.HostType != api.HOST_TYPE_BAREMETAL { memSize, _ := strconv.ParseInt(input.MemSize, 10, 64) memReserved := memSize / 8 if memReserved > 4096 { memReserved = 4096 } input.MemReserved = strconv.FormatInt(memReserved, 10) // data.Set("mem_reserved", jsonutils.NewInt(memReserved)) } else { input.MemReserved = "0" // data.Set("mem_reserved", jsonutils.NewInt(0)) } } ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, "") if err != nil { return input, errors.Wrap(err, "fetchIpmiInfo") } ipmiIpAddr := ipmiInfo.IpAddr if len(ipmiIpAddr) == 0 { noProbe = true } if len(ipmiIpAddr) > 0 && !noProbe { net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None) if net == nil { return input, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr) } // check ip has been reserved rip := ReservedipManager.GetReservedIP(net, ipmiIpAddr) if rip == nil { // if not, reserve this IP temporarily err := net.reserveIpWithDuration(ctx, userCred, ipmiIpAddr, "reserve for baremetal ipmi IP", 30*time.Minute) if err != nil { return input, errors.Wrap(err, "net.reserveIpWithDuration") } } zoneObj := net.GetZone() if zoneObj == nil { return input, httperrors.NewInputParameterError("IPMI network has no zone???") } originZoneId := input.Zone if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() { return input, httperrors.NewInputParameterError("IPMI address located in different zone than specified") } input.Zone = zoneObj.GetId() // data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) } if !noProbe { var accessNet *SNetwork accessIpAddr := input.AccessIp // tString("access_ip") if len(accessIpAddr) > 0 { net, _ := NetworkManager.GetOnPremiseNetworkOfIP(accessIpAddr, "", tristate.None) if net == nil { return input, httperrors.NewInputParameterError("%s is out of network IP ranges", accessIpAddr) } accessNet = net } else { accessNetStr := input.AccessNet // data.GetString("access_net") if len(accessNetStr) > 0 { netObj, err := NetworkManager.FetchByIdOrName(userCred, accessNetStr) if err != nil { if errors.Cause(err) == sql.ErrNoRows { return input, httperrors.NewResourceNotFoundError2("network", accessNetStr) } else { return input, httperrors.NewGeneralError(err) } } accessNet = netObj.(*SNetwork) } else { accessWireStr := input.AccessWire // data.GetString("access_wire") if len(accessWireStr) > 0 { wireObj, err := WireManager.FetchByIdOrName(userCred, accessWireStr) if err != nil { if errors.Cause(err) == sql.ErrNoRows { return input, httperrors.NewResourceNotFoundError2("wire", accessWireStr) } else { return input, httperrors.NewGeneralError(err) } } wire := wireObj.(*SWire) lockman.LockObject(ctx, wire) defer lockman.ReleaseObject(ctx, wire) net, err := wire.GetCandidatePrivateNetwork(userCred, false, []string{api.NETWORK_TYPE_PXE, api.NETWORK_TYPE_BAREMETAL, api.NETWORK_TYPE_GUEST}) if err != nil { return input, httperrors.NewGeneralError(err) } accessNet = net } } } if accessNet != nil { lockman.LockObject(ctx, accessNet) defer lockman.ReleaseObject(ctx, accessNet) accessIp, err := accessNet.GetFreeIP(ctx, userCred, nil, nil, accessIpAddr, api.IPAllocationNone, true) if err != nil { return input, httperrors.NewGeneralError(err) } if len(accessIpAddr) > 0 && accessIpAddr != accessIp { return input, httperrors.NewConflictError("Access ip %s has been used", accessIpAddr) } zoneObj := accessNet.GetZone() if zoneObj == nil { return input, httperrors.NewInputParameterError("Access network has no zone???") } originZoneId := input.Zone // data.GetString("zone_id") if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() { return input, httperrors.NewInputParameterError("Access address located in different zone than specified") } // check ip has been reserved rip := ReservedipManager.GetReservedIP(accessNet, accessIp) if rip == nil { // if not reserved, reserve this IP temporarily err = accessNet.reserveIpWithDuration(ctx, userCred, accessIp, "reserve for baremetal access IP", 30*time.Minute) if err != nil { return input, err } } input.AccessIp = accessIp input.Zone = zoneObj.GetId() // data.Set("access_ip", jsonutils.NewString(accessIp)) // data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) } } // only baremetal can be created hostType := input.HostType // .GetString("host_type") if len(hostType) == 0 { hostType = api.HOST_TYPE_BAREMETAL input.HostType = hostType // data.Set("host_type", jsonutils.NewString(hostType)) } if hostType == api.HOST_TYPE_BAREMETAL { isBaremetal := true input.IsBaremetal = &isBaremetal // data.Set("is_baremetal", jsonutils.JSONTrue) } if noProbe { // accessMac := input.AccessMac // data.GetString("access_mac") // uuid := input.Uuid // data.GetString("uuid") if len(input.AccessMac) == 0 && len(input.Uuid) == 0 { return input, httperrors.NewInputParameterError("missing access_mac and uuid in no_probe mode") } } input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) if err != nil { return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData") } keys := GetHostQuotaKeysFromCreateInput(input) quota := SInfrasQuota{Host: 1} quota.SetKeys(keys) err = quotas.CheckSetPendingQuota(ctx, userCred, "a) if err != nil { return input, errors.Wrapf(err, "CheckSetPendingQuota") } return input, nil } func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostUpdateInput) (api.HostUpdateInput, error) { var err error input.HostAccessAttributes, err = HostManager.inputUniquenessCheck(input.HostAccessAttributes, self.ZoneId, self.Id) if err != nil { return input, errors.Wrap(err, "inputUniquenessCheck") } input.HostSizeAttributes, err = HostManager.ValidateSizeParams(input.HostSizeAttributes) if err != nil { return input, errors.Wrap(err, "ValidateSizeParams") } ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, self.Id) if err != nil { return input, errors.Wrap(err, "fetchIpmiInfo") } ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) if ipmiInfoJson.Length() > 0 { ipmiIpAddr := ipmiInfo.IpAddr if len(ipmiIpAddr) > 0 { net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None) if net == nil { return input, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr) } zoneObj := net.GetZone() if zoneObj == nil { return input, httperrors.NewInputParameterError("IPMI network has not zone???") } if zoneObj.GetId() != self.ZoneId { return input, httperrors.NewInputParameterError("New IPMI address located in another zone!") } } val := jsonutils.NewDict() val.Update(self.IpmiInfo) val.Update(ipmiInfoJson) input.IpmiInfo = val } input.EnabledStatusInfrasResourceBaseUpdateInput, err = self.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput) if err != nil { return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.ValidateUpdateData") } if len(input.Name) > 0 { self.UpdateDnsRecords(false) } return input, nil } func (self *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { self.SEnabledStatusInfrasResourceBase.PostUpdate(ctx, userCred, query, data) if data.Contains("cpu_cmtbound") || data.Contains("mem_cmtbound") { self.ClearSchedDescCache() } } 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 == api.NIC_TYPE_IPMI { return self.GetName() } else if netif.NicType == api.NIC_TYPE_ADMIN { return self.GetName() + "-admin" } return "" } func fetchIpmiInfo(data api.HostIpmiAttributes, hostId string) (types.SIPMIInfo, error) { info := types.SIPMIInfo{} info.Username = data.IpmiUsername if len(data.IpmiPassword) > 0 { if len(hostId) > 0 { value, err := utils.EncryptAESBase64(hostId, data.IpmiPassword) if err != nil { log.Errorf("encrypt password failed %s", err) return info, errors.Wrap(err, "utils.EncryptAESBase64") } info.Password = value } else { info.Password = data.IpmiPassword } } if len(data.IpmiIpAddr) > 0 && !regutils.MatchIP4Addr(data.IpmiIpAddr) { msg := fmt.Sprintf("ipmi_ip_addr: %s not valid ipv4 address", data.IpmiIpAddr) log.Errorf(msg) return info, errors.Wrap(httperrors.ErrInvalidFormat, msg) } info.IpAddr = data.IpmiIpAddr if data.IpmiPresent != nil { info.Present = *data.IpmiPresent } if data.IpmiLanChannel != nil { info.LanChannel = *data.IpmiLanChannel } if data.IpmiVerified != nil { info.Verified = *data.IpmiVerified } if data.IpmiRedfishApi != nil { info.RedfishApi = *data.IpmiRedfishApi } if data.IpmiCdromBoot != nil { info.CdromBoot = *data.IpmiCdromBoot } if data.IpmiPxeBoot != nil { info.PxeBoot = *data.IpmiPxeBoot } return info, nil } func (self *SHost) AllowPerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "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{api.BAREMETAL_READY}) { return nil, httperrors.NewInvalidStatusError("Cannot start baremetal with active guest") } guest := self.GetBaremetalServer() if guest != nil { if self.HostType == api.HOST_TYPE_BAREMETAL && utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) { return nil, self.InitializedGuestStart(ctx, userCred, guest) } // if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) { // return nil, httperrors.NewBadRequestError("Cannot start baremetal with active guest") // } self.SetStatus(userCred, api.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 db.IsAdminAllowPerform(userCred, self, "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{api.BAREMETAL_RUNNING}) { return nil, httperrors.NewInvalidStatusError("Cannot stop baremetal with non-active guest") } guest := self.GetBaremetalServer() if guest != nil { if self.HostType != api.HOST_TYPE_BAREMETAL { if !utils.IsInStringArray(guest.Status, []string{api.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, api.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 db.IsAdminAllowPerform(userCred, self, "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{api.BAREMETAL_READY, api.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{api.VM_READY, api.VM_RUNNING, api.VM_ADMIN}) { return nil, httperrors.NewInvalidStatusError("Cannot do maintenance while guest status %s", guest.Status) } params := jsonutils.NewDict() if guest != nil { if guest.Status == api.VM_RUNNING { params.Set("guest_running", jsonutils.NewBool(true)) } guest.SetStatus(userCred, api.VM_ADMIN, "") } if self.Status == api.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, api.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 db.IsAdminAllowPerform(userCred, self, "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{api.BAREMETAL_RUNNING, api.BAREMETAL_READY}) { return nil, httperrors.NewInvalidStatusError("Cannot do unmaintenance in status %s", self.Status) } guest := self.GetBaremetalServer() if guest != nil && guest.Status != api.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, api.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) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { guest := self.GetBaremetalServer() if guest != nil { return guest.StartSyncstatus(ctx, userCred, parentTaskId) } 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 db.IsAdminAllowPerform(userCred, self, "offline") } func (self *SHost) PerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.HostStatus != api.HOST_OFFLINE { _, err := self.SaveUpdates(func() error { self.HostStatus = api.HOST_OFFLINE return nil }) if err != nil { return nil, err } db.OpsLog.LogEvent(self, db.ACT_OFFLINE, "", userCred) logclient.AddActionLogWithContext(ctx, self, logclient.ACT_OFFLINE, nil, userCred, true) self.SyncAttachedStorageStatus() } return nil, nil } func (self *SHost) AllowPerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "online") } func (self *SHost) PerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.HostStatus != api.HOST_ONLINE { _, err := self.SaveUpdates(func() error { self.LastPingAt = time.Now() self.HostStatus = api.HOST_ONLINE if !self.IsMaintaining() { self.Status = api.BAREMETAL_RUNNING } return nil }) if err != nil { return nil, err } db.OpsLog.LogEvent(self, db.ACT_ONLINE, "", userCred) logclient.AddActionLogWithContext(ctx, 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.Errorln(err) 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 db.IsAdminAllowPerform(userCred, self, "ping") } func (self *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.HostStatus != api.HOST_ONLINE { self.PerformOnline(ctx, userCred, query, data) } else { self.SaveUpdates(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) appParams := appsrv.AppContextGetParams(ctx) if appParams != nil { // skip log&trace, when everything is normal appParams.SkipTrace = true appParams.SkipLog = true } return result, nil } func (self *SHost) AllowPerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "prepare") } func (self *SHost) isRedfishCapable() bool { ipmiInfo, _ := self.GetIpmiInfo() if ipmiInfo.Verified && ipmiInfo.RedfishApi { return true } return false } func (self *SHost) canPrepare() error { if !self.IsBaremetal { return httperrors.NewInvalidStatusError("not a baremetal") } if !self.isRedfishCapable() && len(self.AccessMac) == 0 && len(self.Uuid) == 0 { return httperrors.NewInvalidStatusError("need valid access_mac and uuid to do prepare") } if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING, api.BAREMETAL_PREPARE_FAIL}) { return httperrors.NewInvalidStatusError("Cannot prepare baremetal in status %s", self.Status) } server := self.GetBaremetalServer() if server != nil && server.Status != api.VM_ADMIN { return httperrors.NewInvalidStatusError("Cannot prepare baremetal in server status %s", server.Status) } return nil } func (self *SHost) PerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { err := self.canPrepare() if err != nil { return nil, err } var onfinish string server := self.GetBaremetalServer() if server != nil && self.Status == api.BAREMETAL_READY { onfinish = "shutdown" } return nil, self.StartPrepareTask(ctx, userCred, onfinish, "") } 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)) } self.SetStatus(userCred, api.BAREMETAL_PREPARE, "start prepare task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalPrepareTask", self, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (self *SHost) AllowPerformIpmiProbe(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "ipmi-probe") } func (self *SHost) PerformIpmiProbe(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { return nil, self.StartIpmiProbeTask(ctx, userCred, "") } return nil, httperrors.NewInvalidStatusError("Cannot do Ipmi-probe in status %s", self.Status) } func (self *SHost) StartIpmiProbeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { data := jsonutils.NewDict() self.SetStatus(userCred, api.BAREMETAL_START_PROBE, "start ipmi-probe task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalIpmiProbeTask", self, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (self *SHost) AllowPerformInitialize( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) bool { return db.IsAdminAllowPerform(userCred, self, "initialize") } func (self *SHost) PerformInitialize( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) (jsonutils.JSONObject, error) { if !utils.IsInStringArray( self.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_PREPARE_FAIL}) { return nil, httperrors.NewBadRequestError( "Cannot do initialization in status %s", self.Status) } name, err := data.GetString("name") if err != nil || self.GetBaremetalServer() != nil { return nil, nil } err = db.NewNameValidator(GuestManager, userCred, name, "") if err != nil { return nil, err } if self.IpmiInfo == nil || !self.IpmiInfo.Contains("ip_addr") || !self.IpmiInfo.Contains("password") { return nil, httperrors.NewBadRequestError("IPMI infomation not configured") } guest := &SGuest{} guest.Name = name guest.VmemSize = self.MemSize guest.VcpuCount = self.CpuCount guest.DisableDelete = tristate.True guest.Hypervisor = api.HYPERVISOR_BAREMETAL guest.HostId = self.Id guest.ProjectId = userCred.GetProjectId() guest.DomainId = userCred.GetProjectDomainId() guest.Status = api.VM_RUNNING guest.OsType = "Linux" guest.SetModelManager(GuestManager, guest) err = GuestManager.TableSpec().Insert(guest) if err != nil { return nil, httperrors.NewInternalServerError("Guest Insert error: %s", err) } guest.SetAllMetadata(ctx, map[string]interface{}{ "is_fake_baremetal_server": true, "host_ip": self.AccessIp}, userCred) caps := self.GetAttachedLocalStorageCapacity() diskConfig := &api.DiskConfig{SizeMb: int(caps.GetFree())} err = guest.CreateDisksOnHost(ctx, userCred, self, []*api.DiskConfig{diskConfig}, nil, true, true, nil, nil, true) if err != nil { log.Errorf("Host perform initialize failed on create disk %s", err) } return nil, nil } func (self *SHost) AllowPerformAddNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "add-netif") } func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { log.Debugf("add_netif %s", data) 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") rate, _ := data.Int("rate") nicType, _ := data.GetString("nic_type") index, _ := data.Int("index") linkUp, _ := data.GetString("link_up") mtu, _ := data.Int("mtu") reset := jsonutils.QueryBoolean(data, "reset", false) strInterface, _ := data.GetString("interface") bridge, _ := data.GetString("bridge") reserve := jsonutils.QueryBoolean(data, "reserve", false) requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false) isLinkUp := tristate.None if linkUp != "" { if utils.ToBool(linkUp) { isLinkUp = tristate.True } else { isLinkUp = tristate.False } } err := self.addNetif(ctx, userCred, mac, wire, ipAddr, int(rate), nicType, int8(index), isLinkUp, int16(mtu), reset, strInterface, bridge, reserve, requireDesignatedIp) return nil, err } func (self *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential, mac string, wire string, ipAddr string, rate int, nicType string, index int8, linkUp tristate.TriState, mtu int16, reset bool, strInterface string, bridge string, reserve bool, requireDesignatedIp bool, ) error { var sw *SWire if len(wire) > 0 { iWire, err := WireManager.FetchByIdOrName(userCred, wire) if err != nil { if err == sql.ErrNoRows { return httperrors.NewResourceNotFoundError2(WireManager.Keyword(), wire) } else { return httperrors.NewInternalServerError("find Wire %s error: %s", wire, err) } } sw = iWire.(*SWire) if len(ipAddr) > 0 { iIpAddr, err := netutils.NewIPV4Addr(ipAddr) if err != nil { return httperrors.NewInputParameterError("invalid ipaddr %s", ipAddr) } findAddr := false swNets, err := sw.getNetworks() if err != nil { return httperrors.NewInputParameterError("no networks on wire %s", wire) } for i := range swNets { if swNets[i].IsAddressInRange(iIpAddr) { findAddr = true break } } if !findAddr { return httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire) } } } else if len(ipAddr) > 0 && len(wire) == 0 { ipWire, err := WireManager.GetOnPremiseWireOfIp(ipAddr) if err != nil { return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) } sw = ipWire } netif, err := NetInterfaceManager.FetchByMac(mac) if err != nil { if err != sql.ErrNoRows { return httperrors.NewInternalServerError("fail to fetch netif by mac %s: %s", mac, err) } // else not found netif = &SNetInterface{} netif.Mac = mac netif.BaremetalId = self.Id if sw != nil { netif.WireId = sw.Id } netif.Rate = rate netif.NicType = nicType if index >= 0 { netif.Index = index } if !linkUp.IsNone() { netif.LinkUp = linkUp.Bool() } netif.Mtu = mtu err = NetInterfaceManager.TableSpec().Insert(netif) if err != nil { return err } } else { var changed = false _, err := db.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 > 0 && rate != netif.Rate { netif.Rate = rate } if nicType != "" && nicType != netif.NicType { netif.NicType = nicType } if index >= 0 && index != netif.Index { netif.Index = index } if !linkUp.IsNone() && linkUp.Bool() != netif.LinkUp { netif.LinkUp = linkUp.Bool() } if mtu > 0 && mtu != netif.Mtu { netif.Mtu = mtu } return nil }) if err != nil { return errors.Wrap(err, "db.Update") } if changed || reset { self.DisableNetif(ctx, userCred, netif, false) } } sw = netif.GetWire() if sw != nil { if len(strInterface) == 0 { strInterface = fmt.Sprintf("eth%d", netif.Index) } if len(strInterface) > 0 { if len(bridge) == 0 { bridge = fmt.Sprintf("br%s", sw.GetName()) } var isMaster = netif.NicType == api.NIC_TYPE_ADMIN hw, err := HostwireManager.FetchByHostIdAndMac(self.Id, mac) if err != nil { if err != sql.ErrNoRows { return httperrors.NewInternalServerError("fail to fetch hostwire by mac %s: %s", mac, err) } 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 err } } else { db.Update(hw, func() error { hw.Bridge = bridge hw.Interface = strInterface // hw.MacAddr = mac hw.WireId = sw.Id hw.IsMaster = isMaster return nil }) } } } if netif.NicType == api.NIC_TYPE_ADMIN { err := self.setAccessMac(userCred, netif.Mac) if err != nil { return httperrors.NewBadRequestError(err.Error()) } } if len(ipAddr) > 0 { err = self.EnableNetif(ctx, userCred, netif, "", ipAddr, "", "", reserve, requireDesignatedIp) if err != nil { return httperrors.NewBadRequestError(err.Error()) } } return nil } func (self *SHost) AllowPerformEnableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "enable-netif") } func (self *SHost) PerformEnableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { log.Debugf("enable_netif %s", data) 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, api.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") netType, _ := data.GetString("net_type") reserve := jsonutils.QueryBoolean(data, "reserve", false) requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false) err := self.EnableNetif(ctx, userCred, netif, network, ipAddr, allocDir, netType, 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, netType string, reserve, requireDesignatedIp bool) error { bn := netif.GetBaremetalNetwork() if bn != nil { log.Debugf("Netif has been attach2network? %s", jsonutils.Marshal(bn)) 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) } else { // the ipaddr is not usable, should be reset to empty ipAddr = "" } } wire := netif.GetWire() if wire == nil { return fmt.Errorf("No wire attached") } hw, err := HostwireManager.FetchByHostIdAndMac(self.Id, netif.Mac) if err != nil { return err } if hw.WireId != wire.Id { 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 { var netTypes []string if len(netType) > 0 && netType != api.NETWORK_TYPE_BAREMETAL { netTypes = []string{netType, api.NETWORK_TYPE_BAREMETAL} } else { netTypes = []string{api.NETWORK_TYPE_BAREMETAL} } net, err = wire.GetCandidatePrivateNetwork(userCred, false, netTypes) if err != nil { return fmt.Errorf("fail to find network %s", err) } if net == nil { return fmt.Errorf("No network found") } } } else if net.WireId != wire.Id { return fmt.Errorf("conflict??? candiate net is not on wire") } bn, err = self.Attach2Network(ctx, userCred, netif, net, ipAddr, allocDir, reserve, requireDesignatedIp) if err != nil { return errors.Wrap(err, "self.Attach2Network") } switch netif.NicType { case api.NIC_TYPE_IPMI: err = self.setIpmiIp(userCred, bn.IpAddr) case api.NIC_TYPE_ADMIN: err = self.setAccessIp(userCred, bn.IpAddr) } return err } func (self *SHost) AllowPerformDisableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "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() var ipAddr string if bn != nil { ipAddr = bn.IpAddr self.UpdateDnsRecord(netif, false) self.DeleteBaremetalnetwork(ctx, userCred, bn, reserve) } var err error switch netif.NicType { case api.NIC_TYPE_IPMI: if ipAddr == self.IpmiIp { err = self.setIpmiIp(userCred, "") } case api.NIC_TYPE_ADMIN: if ipAddr == self.AccessIp { err = self.setAccessIp(userCred, "") } } return err } func (self *SHost) Attach2Network(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, net *SNetwork, ipAddr, allocDir string, reserved, requireDesignatedIp bool) (*SHostnetwork, error) { lockman.LockObject(ctx, net) defer lockman.ReleaseObject(ctx, net) freeIp, err := net.GetFreeIP(ctx, userCred, nil, nil, ipAddr, api.IPAllocationDirection(allocDir), reserved) if err != nil { return nil, errors.Wrap(err, "net.GetFreeIP") } if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp { return nil, fmt.Errorf("IP address %s is occupied, get %s instead", ipAddr, freeIp) } bn := &SHostnetwork{} bn.BaremetalId = self.Id bn.NetworkId = net.Id bn.IpAddr = freeIp bn.MacAddr = netif.Mac err = HostnetworkManager.TableSpec().Insert(bn) if err != nil { return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert") } db.OpsLog.LogAttachEvent(ctx, self, net, userCred, jsonutils.NewString(freeIp)) self.UpdateDnsRecord(netif, true) net.UpdateBaremetalNetmap(bn, self.GetNetifName(netif)) return bn, nil } func (self *SHost) AllowPerformRemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "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) nicType := netif.NicType mac := netif.Mac err := netif.Remove(ctx, userCred) if err != nil { return errors.Wrap(err, "netif.Remove") } if nicType == api.NIC_TYPE_ADMIN && self.AccessMac == mac { err := self.setAccessMac(userCred, "") if err != nil { return errors.Wrap(err, "self.setAccessMac") } } if wire != nil { others := self.GetNetifsOnWire(wire) if len(others) == 0 { hw, _ := HostwireManager.FetchByHostIdAndMac(self.Id, netif.Mac) if hw != nil { db.OpsLog.LogDetachEvent(ctx, self, wire, userCred, jsonutils.NewString(fmt.Sprintf("disable netif %s", self.AccessMac))) log.Debugf("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 db.IsAdminAllowPerform(userCred, self, "syncstatus") } func (self *SHost) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.HostType != api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewBadRequestError("Cannot sync status a non-baremetal host") } self.SetStatus(userCred, api.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 db.IsAdminAllowPerform(userCred, self, "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 != api.BAREMETAL_RUNNING { return nil, httperrors.NewBadRequestError("Cannot reset baremetal in status %s", self.Status) } guest := self.GetBaremetalServer() if guest != nil { if self.HostType == api.HOST_TYPE_BAREMETAL { if guest.Status != api.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 db.IsAdminAllowPerform(userCred, self, "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, api.NIC_TYPES) { self.RemoveNetif(ctx, userCred, &netifs[i], false) } } return nil, nil } func (self *SHost) AllowPerformEnable( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput, ) bool { return self.SEnabledStatusInfrasResourceBase.AllowPerformEnable(ctx, userCred, query, input) } func (self *SHost) PerformEnable( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput, ) (jsonutils.JSONObject, error) { if !self.GetEnabled() { _, err := self.SEnabledStatusInfrasResourceBase.PerformEnable(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformEnable") } self.SyncAttachedStorageStatus() } return nil, nil } func (self *SHost) AllowPerformDisable( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput, ) bool { return self.SEnabledStatusInfrasResourceBase.AllowPerformDisable(ctx, userCred, query, input) } func (self *SHost) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) { if self.GetEnabled() { _, err := self.SEnabledStatusInfrasResourceBase.PerformDisable(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformDisable") } self.SyncAttachedStorageStatus() } return nil, nil } func (self *SHost) AllowPerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "cache-image") } func (self *SHost) PerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.HostType == api.HOST_TYPE_BAREMETAL || self.HostStatus != api.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.Errorln(err) 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) format, _ := data.GetString("format") return nil, self.StartImageCacheTask(ctx, userCred, img.Id, format, isForce) } func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, format string, isForce bool) error { var sc *SStoragecache switch self.HostType { case api.HOST_TYPE_BAREMETAL: case api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_ESXI: sc = self.GetLocalStoragecache() default: sc = self.GetStoragecache() } if sc == nil { return errors.Wrap(errors.ErrNotSupported, "No associate storage cache found") } return sc.StartImageCacheTask(ctx, userCred, imageId, format, isForce, "") } func (self *SHost) AllowPerformConvertHypervisor(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "convert-hypervisor") } func (self *SHost) isAlterNameUnique(name string) (bool, error) { q := HostManager.Query().Equals("name", name).NotEquals("id", self.Id).Equals("zone_id", self.ZoneId) cnt, err := q.CountWithError() if err != nil { return false, err } return cnt == 0, nil } 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 != api.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{api.BAREMETAL_READY, api.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) } if data.Contains("name") { name, _ := data.GetString("name") err := self.GetModelManager().ValidateName(name) if err != nil { return nil, err } uniq, err := self.isAlterNameUnique(name) if err != nil { return nil, httperrors.NewInternalServerError("isAlterNameUnique fail %s", err) } if !uniq { return nil, httperrors.NewDuplicateNameError(name, self.Id) } } image, _ := data.GetString("image") raid, _ := data.GetString("raid") input, err := driver.PrepareConvert(self, image, raid, data) if err != nil { return nil, httperrors.NewNotAcceptableError("Convert error: %s", err.Error()) } params := input.JSON(input) // ownerId := userCred.GetProjectId() guest, err := db.DoCreate(GuestManager, ctx, userCred, nil, params, userCred) if err != nil { return nil, err } func() { lockman.LockObject(ctx, guest) defer lockman.ReleaseObject(ctx, guest) guest.PostCreate(ctx, userCred, userCred, nil, params) }() 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) opts := jsonutils.NewDict() opts.Set("server_params", params) opts.Set("server_id", jsonutils.NewString(guest.GetId())) opts.Set("convert_host_type", jsonutils.NewString(hostType)) task, err := taskman.TaskManager.NewTask(ctx, "BaremetalConvertHypervisorTask", self, userCred, opts, "", "", nil) if err != nil { return nil, err } task.ScheduleRun(nil) self.SetStatus(userCred, api.BAREMETAL_START_CONVERT, "") return nil, nil } func (self *SHost) AllowPerformUndoConvert(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "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 == api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewNotAcceptableError("Not being convert to hypervisor") } if self.GetEnabled() { return nil, httperrors.NewNotAcceptableError("Host should be disabled") } if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.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 != api.HYPERVISOR_BAREMETAL { return nil, httperrors.NewNotAcceptableError("Not an converted hypervisor") } err := guest.SetDisableDelete(userCred, false) 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 = api.HOST_TYPE_DEFAULT } return GetHostDriver(hostType) } func (self *SHost) UpdateDiskConfig(userCred mcclient.TokenCredential, layouts []baremetal.Layout) error { bs := self.GetBaremetalstorage() if bs != nil { diff, err := db.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 = size } else { bs.Config = jsonutils.NewArray() bs.RealCapacity = bs.GetStorage().Capacity } return nil }) if err != nil { log.Errorln(err) return err } db.OpsLog.LogEvent(bs, db.ACT_UPDATE, diff, userCred) } return nil } func (host *SHost) SyncHostExternalNics(ctx context.Context, userCred mcclient.TokenCredential, ihost cloudprovider.ICloudHost) compare.SyncResult { result := compare.SyncResult{} netIfs := host.GetNetInterfaces() extNics, err := ihost.GetIHostNics() if err != nil { result.Error(err) return result } disables := make([]*SNetInterface, 0) enables := make([]cloudprovider.ICloudHostNetInterface, 0) type sRemoveNetInterface struct { netif *SNetInterface reserveIp bool } type sAddNetInterface struct { netif cloudprovider.ICloudHostNetInterface reserveIp bool } removes := make([]sRemoveNetInterface, 0) adds := make([]sAddNetInterface, 0) nicMax := len(netIfs) if nicMax < len(extNics) { nicMax = len(extNics) } for i := 0; i < nicMax; i += 1 { if i < len(netIfs) && i < len(extNics) { obn := netIfs[i].GetBaremetalNetwork() var oip string if obn != nil { oip = obn.IpAddr } nip := extNics[i].GetIpAddr() if netIfs[i].Mac == extNics[i].GetMac() { if oip != nip { if obn != nil { disables = append(disables, &netIfs[i]) } if len(nip) > 0 { enables = append(enables, extNics[i]) } } else { // do nothing, in sync } } else { reserveIp := false if len(oip) > 0 && oip == nip { // # mac change case reserveIp = true } removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: reserveIp}) adds = append(adds, sAddNetInterface{netif: extNics[i], reserveIp: reserveIp}) } } else if i < len(netIfs) && i >= len(extNics) { removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: false}) } else if i >= len(netIfs) && i < len(extNics) { adds = append(adds, sAddNetInterface{netif: extNics[i], reserveIp: false}) } } for i := len(removes) - 1; i >= 0; i -= 1 { err = host.RemoveNetif(ctx, userCred, removes[i].netif, removes[i].reserveIp) if err != nil { result.DeleteError(err) } else { result.Delete() } } for i := len(disables) - 1; i >= 0; i -= 1 { err = host.DisableNetif(ctx, userCred, disables[i], false) if err != nil { result.DeleteError(err) } else { result.Delete() } } for i := 0; i < len(enables); i += 1 { netif := host.GetNetInterface(enables[i].GetMac()) // always true reserved address pool err = host.EnableNetif(ctx, userCred, netif, "", enables[i].GetIpAddr(), "", "", true, true) if err != nil { result.AddError(err) } else { result.Add() } } for i := 0; i < len(adds); i += 1 { // always try reserved pool extNic := adds[i].netif err = host.addNetif(ctx, userCred, extNic.GetMac(), "", extNic.GetIpAddr(), 0, extNic.GetNicType(), extNic.GetIndex(), extNic.IsLinkUp(), int16(extNic.GetMtu()), false, "", "", true, true) if err != nil { result.AddError(err) } else { result.Add() } } return result } // func (manager *SHostManager) GetEsxiAgentHostId(key string) (string, error) { // q := HostManager.Query("id") // q = q.Equals("host_status", HOST_ONLINE) // q = q.Equals("host_type", HOST_TYPE_HYPERVISOR) // q = q.IsTrue("enabled") // // rows, err := q.Rows() // if err != nil { // return "", err // } // defer rows.Close() // // var hostId string // hostIds := make([]string, 0) // for rows.Next() { // err = rows.Scan(&hostId) // if err != nil { // return "", err // } // hostIds = append(hostIds, hostId) // } // // ring := hashring.New(hostIds) // ret, _ := ring.GetNode(key) // return ret, nil // } // // func (manager *SHostManager) GetEsxiAgentHost(key string) (*SHost, error) { // hostId, err := manager.GetEsxiAgentHostId(key) // if err != nil { // return nil, err // } // return manager.FetchHostById(hostId), nil // } // // func (host *SHost) GetEsxiAgentHost() (*SHost, error) { // return HostManager.GetEsxiAgentHost(host.Id) // } func (self *SHost) IsBaremetalAgentReady() bool { return self.isAgentReady(api.AgentTypeBaremetal) } func (self *SHost) BaremetalSyncRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) { return self.doAgentRequest(api.AgentTypeBaremetal, ctx, method, url, headers, body) } func (self *SHost) IsEsxiAgentReady() bool { return self.isAgentReady(api.AgentTypeEsxi) } func (self *SHost) EsxiRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) { return self.doAgentRequest(api.AgentTypeEsxi, ctx, method, url, headers, body) } func (self *SHost) isAgentReady(agentType api.TAgentType) bool { agent := BaremetalagentManager.GetAgent(agentType, self.ZoneId) if agent == nil { log.Errorf("%s ready: false", agentType) return false } return true } func (self *SHost) doAgentRequest(agentType api.TAgentType, ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) { agent := BaremetalagentManager.GetAgent(agentType, self.ZoneId) if agent == nil { return nil, fmt.Errorf("no valid %s", agentType) } serviceUrl := agent.ManagerUri if url[0] != '/' && serviceUrl[len(serviceUrl)-1] != '/' { serviceUrl += "/" } url = serviceUrl + url _, data, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, method, url, headers, body, false) return data, err } func (manager *SHostManager) GetHostByIp(hostIp string) (*SHost, error) { q := manager.Query() q = q.Equals("access_ip", hostIp) host, err := db.NewModelObject(manager) if err != nil { return nil, err } err = q.First(host) if err != nil { return nil, err } return host.(*SHost), nil } func (self *SHost) getCloudProviderInfo() SCloudProviderInfo { var region *SCloudregion zone := self.GetZone() if zone != nil { region = zone.GetRegion() } provider := self.GetCloudprovider() return MakeCloudProviderInfo(region, zone, provider) } func (self *SHost) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { desc := self.SEnabledStatusInfrasResourceBase.GetShortDesc(ctx) info := self.getCloudProviderInfo() desc.Update(jsonutils.Marshal(&info)) return desc } func (self *SHost) MarkGuestUnknown(userCred mcclient.TokenCredential) { log.Errorln(self.GetGuests()) for _, guest := range self.GetGuests() { guest.SetStatus(userCred, api.VM_UNKNOWN, "host offline") } } func (manager *SHostManager) PingDetectionTask(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) { deadline := time.Now().Add(-1 * time.Duration(options.Options.HostOfflineMaxSeconds) * time.Second) q := manager.Query().Equals("host_status", api.HOST_ONLINE). Equals("host_type", api.HOST_TYPE_HYPERVISOR) q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("last_ping_at")), sqlchemy.LT(q.Field("last_ping_at"), deadline))) rows, err := q.Rows() if err != nil { log.Errorln(err) return } defer rows.Close() for rows.Next() { var host = new(SHost) q.Row2Struct(rows, host) host.SetModelManager(manager, host) lockman.LockObject(ctx, host) host.PerformOffline(ctx, userCred, nil, nil) host.MarkGuestUnknown(userCred) lockman.ReleaseObject(ctx, host) } } func (self *SHost) IsPrepaidRecycleResource() bool { return self.ResourceType == api.HostResourceTypePrepaidRecycle } func (host *SHost) AllowPerformSetSchedtag(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return AllowPerformSetResourceSchedtag(host, ctx, userCred, query, data) } func (host *SHost) PerformSetSchedtag(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { return PerformSetResourceSchedtag(host, ctx, userCred, query, data) } func (host *SHost) GetDynamicConditionInput() *jsonutils.JSONDict { return jsonutils.Marshal(host).(*jsonutils.JSONDict) } func (host *SHost) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) { ret, err := host.SEnabledStatusInfrasResourceBase.PerformStatus(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformStatus") } host.ClearSchedDescCache() return ret, nil } func (host *SHost) GetSchedtagJointManager() ISchedtagJointManager { return HostschedtagManager } func (host *SHost) AllowPerformHostExitMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, host, "host maintenance") } func (host *SHost) PerformHostExitMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !utils.IsInStringArray(host.Status, []string{api.BAREMETAL_MAINTAIN_FAIL, api.BAREMETAL_MAINTAINING}) { return nil, httperrors.NewInvalidStatusError("host status %s can't exit maintenance", host.Status) } err := host.SetStatus(userCred, api.HOST_STATUS_RUNNING, "exit maintenance") if err != nil { return nil, err } return nil, nil } func (host *SHost) AllowPerformHostMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, host, "host maintenance") } func (host *SHost) PerformHostMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if host.HostType != api.HOST_TYPE_HYPERVISOR { return nil, httperrors.NewBadRequestError("host type %s can't do host maintenance", host.HostType) } if host.HostStatus == api.BAREMETAL_START_MAINTAIN { return nil, httperrors.NewBadRequestError("unsupport on host status %s", host.HostStatus) } var preferHostId string preferHost, _ := data.GetString("prefer_host") if len(preferHost) > 0 { if !db.IsAdminAllowPerform(userCred, host, "assign-host") { return nil, httperrors.NewBadRequestError("Only system admin can assign host") } iHost, _ := HostManager.FetchByIdOrName(userCred, preferHost) if iHost == nil { return nil, httperrors.NewBadRequestError("Host %s not found", preferHost) } host := iHost.(*SHost) preferHostId = host.Id } guests := host.GetGuests() for i := 0; i < len(guests); i++ { lockman.LockObject(ctx, &guests[i]) defer lockman.ReleaseObject(ctx, &guests[i]) guest, err := guests[i].validateForBatchMigrate(ctx) if err != nil { return nil, err } guests[i] = *guest if host.HostStatus == api.HOST_OFFLINE && guests[i].Status != api.VM_UNKNOWN { return nil, httperrors.NewBadRequestError("Host %s can't migrate guests %s in status %s", host.HostStatus, guests[i].Name, guests[i].Status) } } var hostGuests = []*api.GuestBatchMigrateParams{} for i := 0; i < len(guests); i++ { bmp := &api.GuestBatchMigrateParams{ Id: guests[i].Id, LiveMigrate: guests[i].Status == api.VM_RUNNING, RescueMode: guests[i].Status == api.VM_UNKNOWN, OldStatus: guests[i].Status, } guests[i].SetStatus(userCred, api.VM_START_MIGRATE, "host maintainence") hostGuests = append(hostGuests, bmp) } kwargs := jsonutils.NewDict() kwargs.Set("guests", jsonutils.Marshal(hostGuests)) kwargs.Set("prefer_host_id", jsonutils.NewString(preferHostId)) return nil, host.StartMaintainTask(ctx, userCred, kwargs) } func (host *SHost) SetStatus(userCred mcclient.TokenCredential, status string, reason string) error { err := host.SEnabledStatusInfrasResourceBase.SetStatus(userCred, status, reason) if err != nil { return err } host.ClearSchedDescCache() return nil } func (host *SHost) StartMaintainTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error { host.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "start maintenance") if task, err := taskman.TaskManager.NewTask(ctx, "HostMaintainTask", host, userCred, data, "", "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) } return nil } func (host *SHost) IsMaintaining() bool { return utils.IsInStringArray(host.Status, []string{api.BAREMETAL_START_MAINTAIN, api.BAREMETAL_MAINTAINING, api.BAREMETAL_MAINTAIN_FAIL}) } // InstanceGroups returns the enabled group of guest in host and their frequency of occurrence func (host *SHost) InstanceGroups() ([]SGroup, map[string]int, error) { q := GuestManager.Query("id") guestQ := q.Filter(sqlchemy.OR(sqlchemy.Equals(q.Field("host_id"), host.Id), sqlchemy.Equals(q.Field("backup_host_id"), host.Id))).SubQuery() groupQ := GroupguestManager.Query().SubQuery() q = groupQ.Query().Join(guestQ, sqlchemy.Equals(guestQ.Field("id"), groupQ.Field("guest_id"))) groupguests := make([]SGroupguest, 0, 1) err := db.FetchModelObjects(GroupguestManager, q, &groupguests) if err != nil { return nil, nil, err } groupIds, groupSet := make([]string, 0, len(groupguests)), make(map[string]int) for i := range groupguests { id := groupguests[i].GroupId if _, ok := groupSet[id]; !ok { groupIds = append(groupIds, id) groupSet[id] = 1 continue } groupSet[id] += 1 } if len(groupIds) == 0 { return []SGroup{}, make(map[string]int), nil } groups := make([]SGroup, 0, len(groupIds)) q = GroupManager.Query().In("id", groupIds).IsTrue("enabled") err = db.FetchModelObjects(GroupManager, q, &groups) if err != nil { return nil, nil, err } retSet := make(map[string]int) for i := range groups { retSet[groups[i].GetId()] = groupSet[groups[i].GetId()] } return groups, retSet, nil } func (host *SHost) setIpmiIp(userCred mcclient.TokenCredential, ipAddr string) error { if host.IpmiIp == ipAddr { return nil } diff, err := db.Update(host, func() error { host.IpmiIp = ipAddr return nil }) if err != nil { return errors.Wrap(err, "db.Update") } db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred) return nil } func (host *SHost) setAccessIp(userCred mcclient.TokenCredential, ipAddr string) error { if host.AccessIp == ipAddr { return nil } diff, err := db.Update(host, func() error { host.AccessIp = ipAddr return nil }) if err != nil { return errors.Wrap(err, "db.Update") } db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred) return nil } func (host *SHost) setAccessMac(userCred mcclient.TokenCredential, mac string) error { mac = netutils.FormatMacAddr(mac) if host.AccessMac == mac { return nil } diff, err := db.Update(host, func() error { host.AccessMac = mac return nil }) if err != nil { return errors.Wrap(err, "db.Update") } db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred) return nil } func (host *SHost) GetIpmiInfo() (types.SIPMIInfo, error) { info := types.SIPMIInfo{} if host.IpmiInfo != nil { err := host.IpmiInfo.Unmarshal(&info) if err != nil { return info, errors.Wrap(err, "host.IpmiInfo.Unmarshal") } } return info, nil } func (self *SHost) AllowGetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { return db.IsAdminAllowGetSpec(userCred, self, "jnlp") } func (self *SHost) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { url := fmt.Sprintf("/baremetals/%s/jnlp", self.Id) header := mcclient.GetTokenHeaders(userCred) resp, err := self.BaremetalSyncRequest(ctx, "POST", url, header, nil) if err != nil { return nil, errors.Wrap(err, "BaremetalSyncRequest") } return resp, nil } func (self *SHost) AllowPerformInsertIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "insert-iso") } func (self *SHost) PerformInsertIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { imageStr, err := data.GetString("image") image, err := CachedimageManager.getImageInfo(ctx, userCred, imageStr, false) if err != nil { if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError2("image", imageStr) } else { return nil, httperrors.NewGeneralError(err) } } if image.Status != cloudprovider.IMAGE_STATUS_ACTIVE { return nil, httperrors.NewInvalidStatusError("Image status is not active") } boot := jsonutils.QueryBoolean(data, "boot", false) return nil, self.StartInsertIsoTask(ctx, userCred, image.Id, boot, "") } return nil, httperrors.NewInvalidStatusError("Cannot do insert-iso in status %s", self.Status) } func (self *SHost) StartInsertIsoTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, boot bool, parentTaskId string) error { data := jsonutils.NewDict() data.Add(jsonutils.NewString(imageId), "image_id") if boot { data.Add(jsonutils.JSONTrue, "boot") } data.Add(jsonutils.NewString(api.BAREMETAL_CDROM_ACTION_INSERT), "action") self.SetStatus(userCred, api.BAREMETAL_START_INSERT_ISO, "start insert iso task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCdromTask", self, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (self *SHost) AllowPerformEjectIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "eject-iso") } func (self *SHost) PerformEjectIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { return nil, self.StartEjectIsoTask(ctx, userCred, "") } return nil, httperrors.NewInvalidStatusError("Cannot do eject-iso in status %s", self.Status) } func (self *SHost) StartEjectIsoTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { data := jsonutils.NewDict() data.Add(jsonutils.NewString(api.BAREMETAL_CDROM_ACTION_EJECT), "action") self.SetStatus(userCred, api.BAREMETAL_START_EJECT_ISO, "start eject iso task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCdromTask", self, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (self *SHost) AllowPerformSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return db.IsAdminAllowPerform(userCred, self, "sync-config") } func (self *SHost) PerformSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if self.HostType != api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewBadRequestError("Cannot sync config a non-baremetal host") } self.SetStatus(userCred, api.BAREMETAL_SYNCING_STATUS, "") return nil, self.StartSyncConfig(ctx, userCred, "") } func (self *SHost) StartSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncConfigTask", self, userCred, nil, parentTaskId, "", nil) if err != nil { return err } task.ScheduleRun(nil) return nil } func (model *SHost) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { // make host default public return model.SEnabledStatusInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } func (host *SHost) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { localStorages := host.GetAttachedLocalStorages() for i := range localStorages { _, err := localStorages[i].PerformChangeOwner(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "local storage change owner") } } return host.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) } func GetHostQuotaKeysFromCreateInput(input api.HostCreateInput) quotas.SDomainRegionalCloudResourceKeys { ownerId := &db.SOwnerId{DomainId: input.ProjectDomain} var zone *SZone if len(input.Zone) > 0 { zone = ZoneManager.FetchZoneById(input.Zone) } zoneKeys := fetchZonalQuotaKeys(rbacutils.ScopeDomain, ownerId, zone, nil) keys := quotas.SDomainRegionalCloudResourceKeys{} keys.SBaseDomainQuotaKeys = zoneKeys.SBaseDomainQuotaKeys keys.SRegionalBaseKeys = zoneKeys.SRegionalBaseKeys return keys } func (model *SHost) GetQuotaKeys() quotas.SDomainRegionalCloudResourceKeys { zone := model.GetZone() manager := model.GetCloudprovider() ownerId := model.GetOwnerId() zoneKeys := fetchZonalQuotaKeys(rbacutils.ScopeDomain, ownerId, zone, manager) keys := quotas.SDomainRegionalCloudResourceKeys{} keys.SBaseDomainQuotaKeys = zoneKeys.SBaseDomainQuotaKeys keys.SRegionalBaseKeys = zoneKeys.SRegionalBaseKeys keys.SCloudResourceBaseKeys = zoneKeys.SCloudResourceBaseKeys return keys } func (host *SHost) GetUsages() []db.IUsage { if host.Deleted { return nil } usage := SInfrasQuota{Host: 1} keys := host.GetQuotaKeys() usage.SetKeys(keys) return []db.IUsage{ &usage, } } func (host *SHost) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicInput) (jsonutils.JSONObject, error) { // perform public for all connected local storage storages := host.GetAttachedLocalStorages() for i := range storages { _, err := storages[i].performPublicInternal(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "storage.PerformPublic") } } return host.SEnabledStatusInfrasResourceBase.PerformPublic(ctx, userCred, query, input) } func (host *SHost) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { // perform private for all connected local storage storages := host.GetAttachedLocalStorages() for i := range storages { _, err := storages[i].performPrivateInternal(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "storage.PerformPrivate") } } return host.SEnabledStatusInfrasResourceBase.PerformPrivate(ctx, userCred, query, input) }