// 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" "net/http" "net/url" "sort" "strconv" "strings" "sync" "time" "golang.org/x/sync/errgroup" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "yunion.io/x/cloudmux/pkg/apis/compute" "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/util/fileutils" "yunion.io/x/pkg/util/httputils" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/util/rbacscope" "yunion.io/x/pkg/util/regutils" "yunion.io/x/pkg/util/sets" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" baremetalapi "yunion.io/x/onecloud/pkg/apis/baremetal" billing_api "yunion.io/x/onecloud/pkg/apis/billing" api "yunion.io/x/onecloud/pkg/apis/compute" computeapi "yunion.io/x/onecloud/pkg/apis/compute" hostapi "yunion.io/x/onecloud/pkg/apis/host" napi "yunion.io/x/onecloud/pkg/apis/notify" "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/cloudcommon/types" "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/scheduler" "yunion.io/x/onecloud/pkg/util/cgrouputils/cpuset" "yunion.io/x/onecloud/pkg/util/k8s/tokens" "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 SZoneResourceBaseManager SManagedResourceBaseManager SHostnameResourceBaseManager SBackupstorageResourceBaseManager } var HostManager *SHostManager func init() { HostManager = &SHostManager{ SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( SHost{}, "hosts_tbl", "host", "hosts", ), } HostManager.SetVirtualObject(HostManager) HostManager.SetAlias("baremetal", "baremetals") notifyclient.AddNotifyDBHookResources(HostManager.KeywordPlural(), HostManager.AliasPlural()) GuestManager.NameRequireAscii = false } type SHost struct { db.SEnabledStatusInfrasResourceBase db.SExternalizedResourceBase SZoneResourceBase `update:""` SManagedResourceBase SBillingResourceBase SHostnameResourceBase // 机架 Rack string `width:"16" charset:"ascii" nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // 机位 Slots string `width:"16" charset:"ascii" nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // 管理口MAC AccessMac string `width:"32" charset:"ascii" nullable:"true" index:"true" list:"domain" update:"domain"` // 管理口Ip地址 AccessIp string `width:"64" charset:"ascii" nullable:"true" list:"domain" update:"domain"` // 管理地址 ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // 系统信息 SysInfo jsonutils.JSONObject `length:"long" nullable:"true" search:"domain" list:"domain" update:"domain" create:"domain_optional"` // 物理机序列号信息 SN string `width:"128" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // CPU核数 CpuCount int `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // 物理CPU颗数 NodeCount int8 `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // CPU描述信息 CpuDesc string `width:"128" charset:"ascii" nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // CPU频率 CpuMhz int `nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // CPU缓存大小,单位KB CpuCache int `nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // 预留CPU大小 CpuReserved int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"` // CPU超分比 CpuCmtbound float32 `nullable:"true" list:"domain" create:"domain_optional"` // CPUMicrocode CpuMicrocode string `width:"64" charset:"ascii" nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // CPU架构 CpuArchitecture string `width:"16" charset:"ascii" nullable:"true" get:"domain" list:"domain" update:"domain" create:"domain_optional"` // KVM CAP VCPU MAX KvmCapMaxVcpu int `nullable:"true" get:"domain" list:"domain" update:"domain" create:"domain_optional"` // 内存大小,单位Mb MemSize int `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // 预留内存大小 MemReserved int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"` // 内存超分比 MemCmtbound float32 `nullable:"true" list:"domain" create:"domain_optional"` // 页大小 PageSizeKB int `nullable:"false" default:"4" list:"domain" update:"domain" create:"domain_optional"` // scheduler cpu-node/numa allocate EnableNumaAllocate bool `nullable:"true" default:"false" list:"domain" update:"domain" create:"domain_optional"` // 存储大小,单位Mb StorageSize int64 `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // 存储类型 StorageType string `width:"20" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // 存储驱动类型 StorageDriver string `width:"20" charset:"ascii" nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // 存储详情 StorageInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"` RootPartitionUsedCapacityMb int `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` MemoryUsedMb int `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` CpuUsagePercent float64 `nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // IPMI地址 IpmiIp string `width:"16" charset:"ascii" nullable:"true" list:"domain"` // IPMI详情 IpmiInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // 宿主机状态 // example: online HostStatus string `width:"16" charset:"ascii" nullable:"false" default:"offline" list:"domain"` // 宿主机类型 HostType string `width:"36" charset:"ascii" nullable:"false" list:"domain" update:"domain" create:"domain_required"` // host服务软件版本 Version string `width:"128" charset:"ascii" list:"domain" update:"domain" create:"domain_optional"` // OVN软件版本 OvnVersion string `width:"64" charset:"ascii" list:"domain" update:"domain" create:"domain_optional"` IsBaremetal bool `nullable:"true" default:"false" list:"domain" update:"domain" create:"domain_optional"` // 是否处于维护状态 IsMaintenance bool `nullable:"true" default:"false" list:"domain"` LastPingAt time.Time `` // health check enabled by host agent online EnableHealthCheck bool `nullable:"true" default:"false"` ResourceType string `width:"36" charset:"ascii" nullable:"false" list:"domain" update:"domain" create:"domain_optional" default:"shared"` RealExternalId string `width:"256" charset:"utf8" get:"domain"` // 是否为导入的宿主机 IsImport bool `nullable:"true" default:"false" list:"domain" create:"domain_optional"` // 是否允许PXE启动 EnablePxeBoot tristate.TriState `default:"true" list:"domain" create:"domain_optional" update:"domain"` // 主机UUID Uuid string `width:"64" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // 主机启动模式, 可能值为PXE和ISO BootMode string `width:"8" nullable:"true" list:"domain" update:"domain" create:"domain_optional"` // IPv4地址,作为私有云vpc访问外网时的网关 OvnMappedIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"` // IPv6地址,作为私有云vpc访问外网时的网关 OvnMappedIp6Addr string `width:"64" charset:"ascii" nullable:"true" list:"user"` // UEFI详情 UefiInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"` // 公网Ip地址 PublicIp string `width:"128" charset:"ascii" nullable:"true" list:"domain" update:"domain"` } 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") } if len(query.AnyMac) > 0 { anyMac := netutils.FormatMacAddr(query.AnyMac) if len(anyMac) == 0 { return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid any_mac address %s", query.AnyMac) } netifs, _ := NetInterfaceManager.FetchByMac(anyMac) var bmIds []string for i := range netifs { if !utils.IsInArray(netifs[i].BaremetalId, bmIds) { bmIds = append(bmIds, netifs[i].BaremetalId) } } if len(bmIds) > 0 { q = q.In("id", bmIds) } else { q = q.Equals("access_mac", anyMac) } } if len(query.AnyIp) > 0 { cmpFunc := sqlchemy.Equals if len(query.AnyIp) == 1 { cmpFunc = func(f sqlchemy.IQueryField, v interface{}) sqlchemy.ICondition { return sqlchemy.Regexp(f, v.(string)) } } hnQ := HostnetworkManager.Query("baremetal_id") //.Contains("ip_addr", query.AnyIp).SubQuery() conditions := []sqlchemy.ICondition{} for _, ip := range query.AnyIp { conditions = append(conditions, cmpFunc(hnQ.Field("ip_addr"), ip)) } hn := hnQ.Filter( sqlchemy.OR(conditions...), ) conditions = []sqlchemy.ICondition{} for _, ip := range query.AnyIp { conditions = append(conditions, cmpFunc(q.Field("access_ip"), ip)) conditions = append(conditions, cmpFunc(q.Field("ipmi_ip"), ip)) } conditions = append(conditions, sqlchemy.In(q.Field("id"), hn)) q = q.Filter(sqlchemy.OR( conditions..., )) } schedTagStr := query.SchedtagId if len(schedTagStr) > 0 { schedTag, _ := SchedtagManager.FetchByIdOrName(ctx, 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.WireId if len(wireStr) > 0 { wire, _ := WireManager.FetchByIdOrName(ctx, nil, wireStr) if wire == nil { return nil, httperrors.NewResourceNotFoundError("Wire %s not found", wireStr) } hostwires := NetInterfaceManager.Query().SubQuery() scopeQuery := hostwires.Query(hostwires.Field("baremetal_id")).Equals("wire_id", wire.GetId()).SubQuery() q = q.In("id", scopeQuery) } storageStr := query.StorageId if len(storageStr) > 0 { storage, _ := StorageManager.FetchByIdOrName(ctx, 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) } } if len(query.BackupstorageId) > 0 { hbsQ := HostBackupstorageManager.Query("host_id", "backupstorage_id") hbsQ, err = manager.SBackupstorageResourceBaseManager.ListItemFilter(ctx, hbsQ, userCred, query.BackupstorageFilterListInput) if err != nil { return q, errors.Wrap(err, "SBackupStorageResouceBaseManager.ListItemFiled") } hbsSubQ := hbsQ.SubQuery() q = q.LeftJoin(hbsSubQ, sqlchemy.Equals(q.Field("id"), hbsSubQ.Field("host_id"))) notAttached := (query.StorageNotAttached != nil && *query.StorageNotAttached) if !notAttached { q = q.Filter(sqlchemy.IsNotNull(hbsSubQ.Field("backupstorage_id"))) } else { q = q.Filter(sqlchemy.IsNull(hbsSubQ.Field("backupstorage_id"))) } } hostStorageType := query.HostStorageType if len(hostStorageType) > 0 { hoststorages := HoststorageManager.Query() storages := StorageManager.Query().In("storage_type", hostStorageType).SubQuery() hq := hoststorages.Join(storages, sqlchemy.Equals(hoststorages.Field("storage_id"), storages.Field("id"))).SubQuery() scopeQuery := hq.Query(hq.Field("host_id")).SubQuery() q = q.In("id", scopeQuery) } hypervisorStr := query.Hypervisor if len(hypervisorStr) > 0 { q = q.Filter(sqlchemy.In(q.Field("host_type"), Hypervisors2HostTypes([]string{query.Hypervisor}))) } usable := (query.Usable != nil && *query.Usable) if usable { hosts := HostManager.Query().SubQuery() netifs := NetInterfaceManager.Query().SubQuery() networks := NetworkManager.Query().SubQuery() providers := usableCloudProviders().SubQuery() hostQ1 := hosts.Query(hosts.Field("id")) hostQ1 = hostQ1.Join(providers, sqlchemy.Equals(hosts.Field("manager_id"), providers.Field("id"))) hostQ1 = hostQ1.Join(netifs, sqlchemy.Equals(hosts.Field("id"), netifs.Field("baremetal_id"))) hostQ1 = hostQ1.Join(networks, sqlchemy.Equals(netifs.Field("wire_id"), networks.Field("wire_id"))) 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(netifs, sqlchemy.Equals(hosts.Field("id"), netifs.Field("baremetal_id"))) hostQ2 = hostQ2.Join(networks, sqlchemy.Equals(netifs.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) } } fieldQueryMap := map[string][]string{ "rack": query.Rack, "slots": query.Slots, "access_mac": query.AccessMac, "access_ip": query.AccessIp, "sn": query.SN, "storage_type": query.StorageType, "ipmi_ip": query.IpmiIp, "public_ip": query.PublicIp, "host_status": query.HostStatus, "host_type": query.HostType, "version": query.Version, "ovn_version": query.OvnVersion, "uuid": query.Uuid, "boot_mode": query.BootMode, "cpu_architecture": query.CpuArchitecture, } for f, vars := range fieldQueryMap { vars = stringutils2.FilterEmpty(vars) if len(vars) > 1 { q = q.In(f, vars) } else if len(vars) == 1 { q = q.Regexp(f, vars[0]) } } 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 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.OsArch) > 0 { q = db.ListQueryByArchitecture(q, "cpu_architecture", query.OsArch) } // for provider onecloud if len(query.ServerIdForNetwork) > 0 { guest := GuestManager.FetchGuestById(query.ServerIdForNetwork) if guest != nil && guest.GetHypervisor() == api.HYPERVISOR_KVM { nets, _ := guest.GetNetworks("") if len(nets) > 0 { wires := []string{} for i := 0; i < len(nets); i++ { net, _ := nets[i].GetNetwork() if net == nil { continue } vpc, _ := net.GetVpc() if vpc.Id != api.DEFAULT_VPC_ID { q = q.IsNotEmpty("ovn_version") } else { if !utils.IsInStringArray(net.WireId, wires) { wires = append(wires, net.WireId) netifs := NetInterfaceManager.Query().SubQuery() scopeQuery := netifs.Query(netifs.Field("baremetal_id")).Equals("wire_id", net.WireId).SubQuery() q = q.In("id", scopeQuery) } } } } } } 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") } if db.NeedOrderQuery([]string{query.OrderByServerCount}) { guests := GuestManager.Query().SubQuery() guestCounts := guests.Query( guests.Field("host_id"), sqlchemy.COUNT("id").Label("guest_count"), ).GroupBy("host_id").SubQuery() q = q.LeftJoin(guestCounts, sqlchemy.Equals(q.Field("id"), guestCounts.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByServerCount}, []sqlchemy.IQueryField{guestCounts.Field("guest_count")}) } if db.NeedOrderQuery([]string{query.OrderByCpuCommitRate}) { guestsQ := GuestManager.Query() if options.Options.IgnoreNonrunningGuests { guestsQ = guestsQ.Equals("status", api.VM_RUNNING) } guests := guestsQ.SubQuery() hosts := HostManager.Query().SubQuery() hostQ := hosts.Query( hosts.Field("id"), hosts.Field("cpu_count"), hosts.Field("cpu_reserved"), sqlchemy.SUM("guest_vcpu_count", guests.Field("vcpu_count")), ).LeftJoin(guests, sqlchemy.Equals(hosts.Field("id"), guests.Field("host_id"))) hostSQ := hostQ.GroupBy(hostQ.Field("host_id")).SubQuery() divSQ := hostSQ.Query( hostSQ.Field("id"), sqlchemy.SUB("vcpu_count", hostSQ.Field("cpu_count"), hostSQ.Field("cpu_reserved")), hostSQ.Field("guest_vcpu_count"), ).SubQuery() sq := divSQ.Query( divSQ.Field("id").Label("host_id"), sqlchemy.DIV("cpu_commit_rate", divSQ.Field("guest_vcpu_count"), divSQ.Field("vcpu_count")), ).SubQuery() q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("id"), sq.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByCpuCommitRate}, []sqlchemy.IQueryField{sq.Field("cpu_commit_rate")}) } if db.NeedOrderQuery([]string{query.OrderByCpuCommit}) { guestsQ := GuestManager.Query() if options.Options.IgnoreNonrunningGuests { guestsQ = guestsQ.Equals("status", api.VM_RUNNING) } guests := guestsQ.SubQuery() guestQ := guests.Query( guests.Field("host_id"), sqlchemy.SUM("guest_vcpu_count", guests.Field("vcpu_count")), ) guestSQ := guestQ.GroupBy(guestQ.Field("host_id")).SubQuery() q = q.LeftJoin(guestSQ, sqlchemy.Equals(q.Field("id"), guestSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByCpuCommit}, []sqlchemy.IQueryField{q.Field("guest_vcpu_count")}) } if db.NeedOrderQuery([]string{query.OrderByStorage}) { hoststorages := HoststorageManager.Query().SubQuery() storages := StorageManager.Query().IsTrue("enabled").In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery() hoststoragesQ := hoststorages.Query( hoststorages.Field("host_id"), sqlchemy.SUM("storage_capacity", storages.Field("capacity")), ) hoststoragesQ = hoststoragesQ.LeftJoin(storages, sqlchemy.Equals(hoststoragesQ.Field("storage_id"), storages.Field("id"))) hoststoragesSQ := hoststoragesQ.GroupBy(hoststoragesQ.Field("host_id")).SubQuery() q = q.LeftJoin(hoststoragesSQ, sqlchemy.Equals(q.Field("id"), hoststoragesSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByStorage}, []sqlchemy.IQueryField{hoststoragesSQ.Field("storage_capacity")}) } if db.NeedOrderQuery([]string{query.OrderByStorageCommitRate}) { hoststorages := HoststorageManager.Query().SubQuery() disks := DiskManager.Query().Equals("status", api.DISK_READY).SubQuery() storages := StorageManager.Query().IsTrue("enabled").In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery() disksQ := disks.Query( disks.Field("storage_id"), sqlchemy.SUM("disk_size", disks.Field("disk_size")), storages.Field("capacity"), storages.Field("reserved"), ).LeftJoin(storages, sqlchemy.Equals(disks.Field("storage_id"), storages.Field("id"))) disksSQ := disksQ.GroupBy(disksQ.Field("storage_id")).SubQuery() divSQ := disksSQ.Query( disksSQ.Field("storage_id"), disksSQ.Field("disk_size"), sqlchemy.SUB("storage_capacity", disksSQ.Field("capacity"), disksSQ.Field("reserved")), ).SubQuery() hoststoragesQ := hoststorages.Query( hoststorages.Field("host_id"), sqlchemy.SUM("storage_used", divSQ.Field("disk_size")), sqlchemy.SUM("storage_capacity", divSQ.Field("storage_capacity")), ) hoststoragesQ = hoststoragesQ.LeftJoin(divSQ, sqlchemy.Equals(hoststoragesQ.Field("storage_id"), divSQ.Field("storage_id"))) hoststoragesSQ1 := hoststoragesQ.GroupBy(hoststoragesQ.Field("host_id")).SubQuery() hoststoragesSQ := hoststoragesSQ1.Query( hoststoragesSQ1.Field("host_id"), sqlchemy.DIV("storage_commit_rate", hoststoragesSQ1.Field("storage_used"), hoststoragesSQ1.Field("storage_capacity"), ), ).SubQuery() q = q.LeftJoin(hoststoragesSQ, sqlchemy.Equals(q.Field("id"), hoststoragesSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByStorageCommitRate}, []sqlchemy.IQueryField{hoststoragesSQ.Field("storage_commit_rate")}) } if db.NeedOrderQuery([]string{query.OrderByMemCommitRate}) { guestsQ := GuestManager.Query() if options.Options.IgnoreNonrunningGuests { guestsQ = guestsQ.Equals("status", api.VM_RUNNING) } guests := guestsQ.SubQuery() hosts := HostManager.Query().SubQuery() hostQ := hosts.Query( hosts.Field("id"), hosts.Field("mem_size"), hosts.Field("mem_reserved"), sqlchemy.SUM("guest_vmem_size", guests.Field("vmem_size")), ).LeftJoin(guests, sqlchemy.Equals(hosts.Field("id"), guests.Field("host_id"))) hostSQ := hostQ.GroupBy(hostQ.Field("host_id")).SubQuery() divSQ := hostSQ.Query( hostSQ.Field("id"), sqlchemy.SUB("vmem_size", hostSQ.Field("mem_size"), hostSQ.Field("mem_reserved")), hostSQ.Field("guest_vmem_size"), ).SubQuery() sq := divSQ.Query( divSQ.Field("id").Label("host_id"), sqlchemy.DIV("mem_commit_rate", divSQ.Field("guest_vmem_size"), divSQ.Field("vmem_size")), ).SubQuery() q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("id"), sq.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByMemCommitRate}, []sqlchemy.IQueryField{sq.Field("mem_commit_rate")}) } if db.NeedOrderQuery([]string{query.OrderByMemCommit}) { guestsQ := GuestManager.Query() if options.Options.IgnoreNonrunningGuests { guestsQ = guestsQ.Equals("status", api.VM_RUNNING) } guests := guestsQ.SubQuery() guestQ := guests.Query( guests.Field("host_id"), sqlchemy.SUM("guest_vmem_size", guests.Field("vmem_size")), ) guestSQ := guestQ.GroupBy(guestQ.Field("host_id")).SubQuery() q = q.LeftJoin(guestSQ, sqlchemy.Equals(q.Field("id"), guestSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByMemCommit}, []sqlchemy.IQueryField{q.Field("guest_vmem_size")}) } if db.NeedOrderQuery([]string{query.OrderByStorageUsed}) { disks := DiskManager.Query().Equals("status", api.DISK_READY).SubQuery() storages := StorageManager.Query().IsTrue("enabled").In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery() hs := HoststorageManager.Query() hoststorages := hs.Join(storages, sqlchemy.Equals(storages.Field("id"), hs.Field("storage_id"))).SubQuery() disksQ := disks.Query( disks.Field("storage_id"), sqlchemy.SUM("total_disk_size", disks.Field("disk_size")), ) disksQ = disksQ.LeftJoin(storages, sqlchemy.Equals(disksQ.Field("storage_id"), storages.Field("id"))) disksSQ := disksQ.GroupBy(disksQ.Field("storage_id")).SubQuery() hoststoragesQ := hoststorages.Query( hoststorages.Field("host_id"), sqlchemy.SUM("storage_used", disksSQ.Field("total_disk_size")), ) hoststoragesQ = hoststoragesQ.Join(disksSQ, sqlchemy.Equals(hoststoragesQ.Field("storage_id"), disksSQ.Field("storage_id"))) storageSQ := hoststoragesQ.GroupBy(hoststoragesQ.Field("host_id")).SubQuery() q = q.LeftJoin(storageSQ, sqlchemy.Equals(q.Field("id"), storageSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByStorageUsed}, []sqlchemy.IQueryField{q.Field("storage_used")}) } if db.NeedOrderQuery([]string{query.OrderByCpuUsage}) { db.OrderByFields(q, []string{query.OrderByCpuUsage}, []sqlchemy.IQueryField{q.Field("cpu_usage_percent")}) } if db.NeedOrderQuery([]string{query.OrderByMemUsage}) { hosts := HostManager.Query().SubQuery() sq := hosts.Query( hosts.Field("id").Label("host_id"), sqlchemy.DIV("mem_usage", hosts.Field("memory_used_mb"), hosts.Field("mem_size")), ).SubQuery() q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("id"), sq.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByMemUsage}, []sqlchemy.IQueryField{sq.Field("mem_usage")}) } if db.NeedOrderQuery([]string{query.OrderByStorageUsage}) { hs := HoststorageManager.Query().SubQuery() storages := StorageManager.Query().IsTrue("enabled").NotEquals("storage_type", api.STORAGE_BAREMETAL).In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery() host := HostManager.Query().SubQuery() hsSQ := hs.Query( hs.Field("host_id"), sqlchemy.SUM("actual_storage_used", storages.Field("actual_capacity_used")), ).LeftJoin(storages, sqlchemy.Equals(hs.Field("storage_id"), storages.Field("id"))).GroupBy(hs.Field("host_id")).SubQuery() hsQ := hsSQ.Query( hsSQ.Field("host_id"), sqlchemy.DIV("storage_usage", hsSQ.Field("actual_storage_used"), host.Field("storage_size")), ).LeftJoin(host, sqlchemy.Equals(hsSQ.Field("host_id"), host.Field("id"))) hsSSQ := hsQ.GroupBy(hsQ.Field("host_id")).SubQuery() q = q.LeftJoin(hsSSQ, sqlchemy.Equals(q.Field("id"), hsSSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByStorageUsage}, []sqlchemy.IQueryField{hsSSQ.Field("storage_usage")}) } if db.NeedOrderQuery([]string{query.OrderByVirtualMemUsage}) { guests := GuestManager.Query() if options.Options.IgnoreNonrunningGuests { guests = guests.Equals("status", api.VM_RUNNING) } sq := guests.SubQuery() guestSQ := sq.Query( sq.Field("host_id"), sqlchemy.SUM("mem_commit", sq.Field("vmem_size")), ).GroupBy(sq.Field("host_id")).SubQuery() host := HostManager.Query().SubQuery() vq := guestSQ.Query( guestSQ.Field("host_id"), guestSQ.Field("mem_commit"), sqlchemy.NewFunction( sqlchemy.NewCase().When( sqlchemy.GT(host.Field("mem_cmtbound"), 0), host.Field("mem_cmtbound"), ).Else(sqlchemy.NewConstField(options.Options.DefaultMemoryOvercommitBound)), "mem_cmtbound", true, ), sqlchemy.SUB("host_mem_size", host.Field("mem_size"), host.Field("mem_reserved")), ).LeftJoin(host, sqlchemy.Equals(guestSQ.Field("host_id"), host.Field("id"))).GroupBy(guestSQ.Field("host_id")).SubQuery() vsq := vq.Query( vq.Field("host_id"), sqlchemy.DIV("virtual_mem_usage", vq.Field("mem_commit"), sqlchemy.DIV("cmt_mem_size", vq.Field("mem_cmtbound"), vq.Field("host_mem_size"))), ) vqq := vsq.GroupBy(vsq.Field("host_id")).SubQuery() q = q.LeftJoin(vqq, sqlchemy.Equals(q.Field("id"), vqq.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByVirtualMemUsage}, []sqlchemy.IQueryField{vqq.Field("virtual_mem_usage")}) } if db.NeedOrderQuery([]string{query.OrderByVirtualCpuUsage}) { guests := GuestManager.Query() if options.Options.IgnoreNonrunningGuests { guests = guests.Equals("status", api.VM_RUNNING) } sq := guests.SubQuery() guestSQ := sq.Query( sq.Field("host_id"), sqlchemy.SUM("cpu_commit", sq.Field("vcpu_count")), ).GroupBy(sq.Field("host_id")).SubQuery() host := HostManager.Query().SubQuery() vq := guestSQ.Query( guestSQ.Field("host_id"), guestSQ.Field("cpu_commit"), sqlchemy.NewFunction( sqlchemy.NewCase().When( sqlchemy.GT(host.Field("cpu_cmtbound"), 0), host.Field("cpu_cmtbound"), ).Else(sqlchemy.NewConstField(options.Options.DefaultCPUOvercommitBound)), "cpu_cmtbound", true, ), sqlchemy.SUB("host_cpu_size", host.Field("cpu_count"), host.Field("cpu_reserved")), ).LeftJoin(host, sqlchemy.Equals(guestSQ.Field("host_id"), host.Field("id"))).GroupBy(guestSQ.Field("host_id")).SubQuery() vsq := vq.Query( vq.Field("host_id"), sqlchemy.DIV("virtual_cpu_usage", vq.Field("cpu_commit"), sqlchemy.DIV("cmt_cpu_size", vq.Field("cpu_cmtbound"), vq.Field("host_cpu_size"))), ) vqq := vsq.GroupBy(vsq.Field("host_id")).SubQuery() q = q.LeftJoin(vqq, sqlchemy.Equals(q.Field("id"), vqq.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByVirtualCpuUsage}, []sqlchemy.IQueryField{vqq.Field("virtual_cpu_usage")}) } if db.NeedOrderQuery([]string{query.OrderByVirtualStorageUsage}) { hoststorages := HoststorageManager.Query().SubQuery() storageQ := StorageManager.Query().IsTrue("enabled").NotEquals("storage_type", api.STORAGE_BAREMETAL).In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery() diskReadySQ := DiskManager.Query().Equals("status", api.DISK_READY).SubQuery() diskReadyQ := diskReadySQ.Query(sqlchemy.SUM("sum", diskReadySQ.Field("disk_size")).Label("used")).GroupBy(diskReadySQ.Field("storage_id")) readySQ := diskReadyQ.SubQuery() storageSQ := storageQ.Query( storageQ.Field("id"), sqlchemy.SUB("storage_capacity", storageQ.Field("capacity"), storageQ.Field("reserved")), hoststorages.Field("host_id"), sqlchemy.NewFunction( sqlchemy.NewCase().When( sqlchemy.GT(storageQ.Field("cmtbound"), 0), storageQ.Field("cmtbound"), ).Else(sqlchemy.NewConstField(1)), "cmtbound", true, ), readySQ.Field("used"), ) storageSQ = storageSQ.Join(hoststorages, sqlchemy.Equals(storageSQ.Field("id"), hoststorages.Field("storage_id"))) storageSQ = storageSQ.LeftJoin(readySQ, sqlchemy.Equals(readySQ.Field("storage_id"), storageSQ.Field("id"))) sq := storageSQ.SubQuery() sqMul := sq.Query( sq.Field("host_id"), sq.Field("id"), sq.Field("used"), sqlchemy.MUL("virtual_storage_size", sq.Field("storage_capacity"), sq.Field("cmtbound")), ).SubQuery() sqSum := sqMul.Query( sqMul.Field("host_id"), sqMul.Field("id"), sqlchemy.SUM("total_used", sqMul.Field("used")), sqlchemy.SUM("total_virtual_storage_szie", sqMul.Field("virtual_storage_size")), ).GroupBy(sqMul.Field("host_id")).SubQuery() sqDiv := sqSum.Query( sqSum.Field("host_id"), sqlchemy.DIV("virtual_storage_usage", sqSum.Field("total_used"), sqSum.Field("total_virtual_storage_szie")), ) usageSQ := sqDiv.GroupBy(sqDiv.Field("host_id")).SubQuery() q = q.LeftJoin(usageSQ, sqlchemy.Equals(q.Field("id"), usageSQ.Field("host_id"))) db.OrderByFields(q, []string{query.OrderByVirtualStorageUsage}, []sqlchemy.IQueryField{usageSQ.Field("virtual_storage_usage")}) } 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) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) { var err error q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields) 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 (hh *SHost) IsArmHost() bool { return hh.CpuArchitecture == apis.OS_ARCH_AARCH64 } func (hh *SHost) IsRISCVHost() bool { return hh.CpuArchitecture == apis.OS_ARCH_RISCV64 } func (hh *SHost) GetZone() (*SZone, error) { zone, err := ZoneManager.FetchById(hh.ZoneId) if err != nil { return nil, err } return zone.(*SZone), nil } func (hh *SHost) GetRegion() (*SCloudregion, error) { zones := ZoneManager.Query("cloudregion_id").Equals("id", hh.ZoneId).SubQuery() q := CloudregionManager.Query().In("id", zones) ret := &SCloudregion{} ret.SetModelManager(CloudregionManager, ret) err := q.First(ret) if err != nil { return nil, err } return ret, nil } func (hh *SHost) GetCpuCount() int { if hh.CpuReserved > 0 && hh.CpuReserved < hh.CpuCount { return int(hh.CpuCount - hh.CpuReserved) } else { return int(hh.CpuCount) } } func (hh *SHost) GetMemSize() int { if hh.MemReserved > 0 && hh.MemReserved < hh.MemSize { return hh.MemSize - hh.MemReserved } else { return hh.MemSize } } func (hh *SHost) IsHugePage() bool { return isHugePage(hh.PageSizeKB) } func isHugePage(pageSizeKb int) bool { return pageSizeKb > 4 } func (hh *SHost) GetMemoryOvercommitBound() float32 { if hh.IsHugePage() { return 1.0 } if hh.MemCmtbound > 0 { return hh.MemCmtbound } return options.Options.DefaultMemoryOvercommitBound } func (hh *SHost) GetVirtualMemorySize() float32 { return float32(hh.GetMemSize()) * hh.GetMemoryOvercommitBound() } func (hh *SHost) GetCPUOvercommitBound() float32 { if hh.CpuCmtbound > 0 { return hh.CpuCmtbound } return options.Options.DefaultCPUOvercommitBound } func (hh *SHost) GetVirtualCPUCount() float32 { return float32(hh.GetCpuCount()) * hh.GetCPUOvercommitBound() } func (hh *SHost) ValidateDeleteCondition(ctx context.Context, info api.HostDetails) error { if hh.IsBaremetal && hh.HostType != api.HOST_TYPE_BAREMETAL { return httperrors.NewInvalidStatusError("Host is a converted baremetal, should be unconverted before delete") } if hh.GetEnabled() { return httperrors.NewInvalidStatusError("Host is not disabled") } if info.Guests > 0 || info.BackupGuests > 0 { return httperrors.NewNotEmptyError("Not an empty host") } return hh.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx, nil) } func (hh *SHost) validateDeleteCondition(ctx context.Context, purge bool) error { if !purge && hh.IsBaremetal && hh.HostType != api.HOST_TYPE_BAREMETAL { return httperrors.NewInvalidStatusError("Host is a converted baremetal, should be unconverted before delete") } if hh.GetEnabled() { return httperrors.NewInvalidStatusError("Host is not disabled") } cnt, err := hh.GetGuestCount() if err != nil { return httperrors.NewInternalServerError("getGuestCount fail %s", err) } if cnt > 0 { return httperrors.NewNotEmptyError("Not an empty host") } cnt, err = hh.GetBackupGuestCount() if err != nil { return httperrors.NewInternalServerError("GetBackupGuestCount fail %s", err) } if cnt > 0 { return httperrors.NewNotEmptyError("Not an empty host") } for _, hoststorage := range hh.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 hh.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx, nil) } func (hh *SHost) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { log.Infof("Host delete do nothing") return nil } func (hh *SHost) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { if hh.IsBaremetal { return hh.StartDeleteBaremetalTask(ctx, userCred, "") } return hh.RealDelete(ctx, userCred) } func (hh *SHost) StartDeleteBaremetalTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { task, err := taskman.TaskManager.NewTask(ctx, "BaremetalDeleteTask", hh, userCred, nil, parentTaskId, "", nil) if err != nil { return err } return task.ScheduleRun(nil) } func (hh *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error { return hh.purge(ctx, userCred) } func (hh *SHost) GetLoadbalancerBackends() ([]SLoadbalancerBackend, error) { q := LoadbalancerBackendManager.Query().Equals("backend_id", hh.Id) ret := []SLoadbalancerBackend{} return ret, db.FetchModelObjects(LoadbalancerBackendManager, q, &ret) } func (hh *SHost) GetHoststoragesQuery() *sqlchemy.SQuery { return HoststorageManager.Query().Equals("host_id", hh.Id) } func (hh *SHost) GetStorageCount() (int, error) { return hh.GetHoststoragesQuery().CountWithError() } func (hh *SHost) GetHoststorages() []SHoststorage { hoststorages := make([]SHoststorage, 0) q := hh.GetHoststoragesQuery() err := db.FetchModelObjects(HoststorageManager, q, &hoststorages) if err != nil && errors.Cause(err) != sql.ErrNoRows { log.Errorf("GetHoststorages error %s", err) return nil } return hoststorages } func (hh *SHost) GetStorages() ([]SStorage, error) { sq := HoststorageManager.Query("storage_id").Equals("host_id", hh.Id).SubQuery() q := StorageManager.Query().In("id", sq) storages := []SStorage{} if err := db.FetchModelObjects(StorageManager, q, &storages); err != nil { return nil, err } return storages, nil } func (hh *SHost) GetHoststorageOfId(storageId string) *SHoststorage { hoststorage := SHoststorage{} hoststorage.SetModelManager(HoststorageManager, &hoststorage) err := hh.GetHoststoragesQuery().Equals("storage_id", storageId).First(&hoststorage) if err != nil { if errors.Cause(err) != sql.ErrNoRows { log.Errorf("GetHoststorageOfId fail %s", err) } return nil } return &hoststorage } func (hh *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"), hh.Id)) q = q.Filter(sqlchemy.Equals(storages.Field("external_id"), extId)) err := q.First(&hoststorage) if err != nil { if errors.Cause(err) != sql.ErrNoRows { log.Errorf("GetHoststorageByExternalId fail %s", err) } return nil } return &hoststorage } func (hh *SHost) GetStorageByFilePath(path string) *SStorage { hoststorages := hh.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 (hh *SHost) GetBaremetalstorage() *SHoststorage { if !hh.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"), hh.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 (hh *SHost) SaveCleanUpdates(doUpdate func() error) (map[string]sqlchemy.SUpdateDiff, error) { return hh.saveUpdates(doUpdate, true) } func (hh *SHost) SaveUpdates(doUpdate func() error) (map[string]sqlchemy.SUpdateDiff, error) { return hh.saveUpdates(doUpdate, false) } func (hh *SHost) saveUpdates(doUpdate func() error, doSchedClean bool) (map[string]sqlchemy.SUpdateDiff, error) { diff, err := db.Update(hh, doUpdate) if err != nil { return nil, err } if doSchedClean { hh.ClearSchedDescCache() } return diff, nil } func (hh *SHost) PerformSetCommitBound( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostSetCommitBoundInput, ) (jsonutils.JSONObject, error) { _, err := db.Update(hh, func() error { if input.CpuCmtbound != nil { hh.CpuCmtbound = *input.CpuCmtbound } if input.MemCmtbound != nil { hh.MemCmtbound = *input.MemCmtbound } return nil }) if err != nil { return nil, err } db.OpsLog.LogEvent(hh, db.ACT_SET_COMMIT_BOUND, input, userCred) logclient.AddActionLogWithContext(ctx, hh, logclient.ACT_SET_COMMIT_BOUND, input, userCred, true) return nil, nil } func (hh *SHost) PerformUpdateStorage( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) (jsonutils.JSONObject, error) { bs := hh.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", hh.GetName()) storage.Capacity = capacity storage.StorageType = api.STORAGE_BAREMETAL storage.MediumType = hh.StorageType storage.Cmtbound = 1.0 storage.Status = api.STORAGE_ONLINE storage.Enabled = tristate.True storage.ZoneId = zoneId storage.StoragecacheId = storageCacheId storage.DomainId = hh.DomainId storage.DomainSrc = string(apis.OWNER_SOURCE_LOCAL) err := StorageManager.TableSpec().Insert(ctx, &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 = hh.Id bmStorage.StorageId = storage.Id bmStorage.RealCapacity = capacity bmStorage.MountPoint = "" err = HoststorageManager.TableSpec().Insert(ctx, &bmStorage) if err != nil { return nil, fmt.Errorf("Create baremetal hostStorage error: %v", err) } bmStorage.SetModelManager(HoststorageManager, &bmStorage) db.OpsLog.LogAttachEvent(ctx, hh, &storage, userCred, bmStorage.GetShortDesc(ctx)) bmStorage.syncLocalStorageShare(ctx, userCred) 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 storage.DomainId = hh.DomainId storage.DomainSrc = string(apis.OWNER_SOURCE_LOCAL) return nil }) if err != nil { return nil, fmt.Errorf("Update baremetal storage error: %v", err) } db.OpsLog.LogEvent(storage, db.ACT_UPDATE, diff, userCred) bs.syncLocalStorageShare(ctx, userCred) //} return nil, nil } func (hh *SHost) GetFetchUrl(disableHttps bool) string { managerUrl, err := url.Parse(hh.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", managerUrl.Scheme, net.JoinHostPort(managerUrl.Hostname(), strconv.Itoa(port+40000))) } func (hh *SHost) GetAttachedEnabledHostStorages(storageType []string) []SStorage { return hh._getAttachedStorages(tristate.False, tristate.True, storageType) } func (hh *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.In("storage_type", storageType) } q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), hh.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 (hh *SHost) SyncAttachedStorageStatus(ctx context.Context) { storages := hh.GetAttachedEnabledHostStorages(nil) if storages != nil { for _, storage := range storages { storage.SyncStatusWithHosts(ctx) } hh.ClearSchedDescCache() } } func (hh *SHostManager) IsNewNameUnique(name string, userCred mcclient.TokenCredential, kwargs *jsonutils.JSONDict) (bool, error) { q := hh.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 (hh *SHostManager) GetPropertyK8sMasterNodeIps(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { cli, err := tokens.GetCoreClient() if err != nil { return nil, errors.Wrap(err, "get k8s client") } nodes, err := cli.Nodes().List(context.Background(), metav1.ListOptions{ LabelSelector: "node-role.kubernetes.io/master", }) if err != nil { return nil, errors.Wrap(err, "list master nodes") } ips := make([]string, 0) for i := range nodes.Items { for j := range nodes.Items[i].Status.Addresses { if nodes.Items[i].Status.Addresses[j].Type == v1.NodeInternalIP { ips = append(ips, nodes.Items[i].Status.Addresses[j].Address) } } } log.Infof("k8s master nodes ips %v", ips) res := jsonutils.NewDict() res.Set("ips", jsonutils.Marshal(ips)) return res, nil } func (hh *SHostManager) GetPropertyBmStartRegisterScript(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { regionUri, err := auth.GetPublicServiceURL(consts.GetServiceType(), options.Options.Region, "", httputils.POST) 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 (hh *SHostManager) GetPropertyNodeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) (jsonutils.JSONObject, error) { hosts := hh.Query().SubQuery() q := hosts.Query(hosts.Field("host_type"), sqlchemy.SUM("node_count_total", hosts.Field("node_count"))) return hh.getCount(ctx, userCred, q, query) } func (hh *SHostManager) getCount(ctx context.Context, userCred mcclient.TokenCredential, q *sqlchemy.SQuery, query api.HostListInput) (jsonutils.JSONObject, error) { filterAny := false if query.FilterAny != nil { filterAny = *query.FilterAny } q, err := db.ApplyListItemsGeneralFilters(hh, q, userCred, query.Filter, filterAny) if err != nil { return nil, err } q, err = hh.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("getCount scan err: %v", err) return ret, nil } ret.Add(jsonutils.NewInt(count), hostType) } return ret, nil } func (hh *SHostManager) GetPropertyHostTypeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) (jsonutils.JSONObject, error) { hosts := hh.Query().SubQuery() // select host_type, (case host_type when 'huaweicloudstack' then count(DISTINCT external_id) else count(id) end) as count from hosts_tbl group by host_type; cs := sqlchemy.NewCase() hcso := sqlchemy.Equals(hosts.Field("host_type"), api.HOST_TYPE_HCSO) cs.When(hcso, sqlchemy.COUNT("", sqlchemy.DISTINCT("", hosts.Field("external_id")))) cs.Else(sqlchemy.COUNT("", hosts.Field("id"))) q := hosts.Query(hosts.Field("host_type"), sqlchemy.NewFunction(cs, "count", true)) return hh.getCount(ctx, userCred, q, query) } func (hh *SHostManager) ClearAllSchedDescCache() error { return hh.ClearSchedDescSessionCache("", "") } func (hh *SHostManager) ClearSchedDescCache(hostId string) error { return hh.ClearSchedDescSessionCache(hostId, "") } func (hh *SHostManager) ClearSchedDescSessionCache(hostId, sessionId string) error { s := auth.GetAdminSession(context.Background(), options.Options.Region) return scheduler.SchedManager.CleanCache(s, hostId, sessionId, false) } func (hh *SHost) ClearSchedDescCache() error { return hh.ClearSchedDescSessionCache("") } func (hh *SHost) ClearSchedDescSessionCache(sessionId string) error { return HostManager.ClearSchedDescSessionCache(hh.Id, sessionId) } // sync clear sched desc on scheduler side func (hh *SHostManager) SyncClearSchedDescSessionCache(hostId, sessionId string) error { s := auth.GetAdminSession(context.Background(), options.Options.Region) return scheduler.SchedManager.CleanCache(s, hostId, sessionId, true) } func (hh *SHost) SyncCleanSchedDescCache() error { return hh.SyncClearSchedDescSessionCache("") } func (hh *SHost) SyncClearSchedDescSessionCache(sessionId string) error { return HostManager.SyncClearSchedDescSessionCache(hh.Id, sessionId) } func (hh *SHost) GetDetailsSpec(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { return GetModelSpec(HostManager, hh) } 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 (hh *SHost) GetSpec(statusCheck bool) *jsonutils.JSONDict { if statusCheck { if !hh.GetEnabled() { return nil } if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_RUNNING, api.BAREMETAL_READY}) || hh.GetBaremetalServer() != nil || hh.IsMaintenance { return nil } if hh.MemSize == 0 || hh.CpuCount == 0 { return nil } if hh.ResourceType == api.HostResourceTypePrepaidRecycle { cnt, err := hh.GetGuestCount() if err != nil { return nil } if cnt > 0 { // occupied return nil } } if len(hh.ManagerId) > 0 { providerObj, _ := CloudproviderManager.FetchById(hh.ManagerId) if providerObj == nil { return nil } provider := providerObj.(*SCloudprovider) if !provider.IsAvailable() { return nil } } } spec := hh.GetHardwareSpecification() specInfo := new(api.HostSpec) if err := spec.Unmarshal(specInfo); err != nil { return spec } nifs := hh.GetAllNetInterfaces() 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 hh.SysInfo != nil { manufacture, _ = hh.SysInfo.GetString("manufacture") model, _ = hh.SysInfo.GetString("model") } if manufacture == "" { manufacture = "Unknown" } if model == "" { model = "Unknown" } specInfo.Manufacture = strings.ReplaceAll(manufacture, " ", "_") specInfo.Model = strings.ReplaceAll(model, " ", "_") devices := IsolatedDeviceManager.FindByHost(hh.Id) if len(devices) > 0 { specInfo.IsolatedDevices = make([]api.IsolatedDeviceSpec, len(devices)) for i := 0; i < len(devices); i++ { specInfo.IsolatedDevices[i].DevType = devices[i].DevType specInfo.IsolatedDevices[i].Model = devices[i].Model specInfo.IsolatedDevices[i].PciId = devices[i].VendorDeviceId specInfo.IsolatedDevices[i].Vendor = devices[i].getVendor() } } 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 (hh *SHost) GetHardwareSpecification() *jsonutils.JSONDict { spec := &api.HostSpec{ Cpu: int(hh.CpuCount), Mem: hh.MemSize, } if hh.StorageInfo != nil { spec.Disk = GetDiskSpecV2(hh.StorageInfo) spec.Driver = hh.StorageDriver } ret := spec.JSON(spec) if hh.StorageInfo != nil { ret.Set("storage_info", hh.StorageInfo) } return ret } type SStorageCapacity struct { Capacity int64 `json:"capacity,omitzero"` Used int64 `json:"used_capacity,omitzero"` ActualUsed int64 `json:"real_time_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 cap.ActualUsed += cap2.ActualUsed } func (cap *SStorageCapacity) toCapacityInfo() api.SStorageCapacityInfo { info := api.SStorageCapacityInfo{} info.UsedCapacity = cap.Used info.WasteCapacity = cap.Wasted info.VirtualCapacity = cap.VCapacity info.CommitRate = cap.GetCommitRate() info.FreeCapacity = cap.GetFree() return info } func (hh *SHost) GetAttachedLocalStorageCapacity() SStorageCapacity { ret := SStorageCapacity{} storages := hh.GetAttachedEnabledHostStorages(api.HOST_STORAGE_LOCAL_TYPES) for _, s := range storages { ret.Add(s.getStorageCapacity()) } return ret } func (hh *SHost) GetAttachedLocalStorages() []SStorage { return hh.GetAttachedEnabledHostStorages(api.HOST_STORAGE_LOCAL_TYPES) } 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 ChooseLeastUsedStorage(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 (hh *SHost) GetLeastUsedStorage(backend string) *SStorage { storages := hh.GetAttachedEnabledHostStorages(nil) if storages != nil { return ChooseLeastUsedStorage(storages, backend) } return nil } /*func (hh *SHost) GetWiresQuery() *sqlchemy.SQuery { return Manager.Query().Equals("host_id", hh.Id) } func (hh *SHost) GetWireCount() (int, error) { return hh.GetWiresQuery().CountWithError() } func (hh *SHost) GetHostwires() []SHostwire { hw := make([]SHostwire, 0) q := hh.GetWiresQuery() err := db.FetchModelObjects(HostwireManager, q, &hw) if err != nil { log.Errorf("GetWires error %s", err) return nil } return hw }*/ func (hh *SHost) getAttachedWires() []SWire { wires := WireManager.Query().SubQuery() netifs := NetInterfaceManager.Query().SubQuery() q := wires.Query() q = q.Join(netifs, sqlchemy.Equals(netifs.Field("wire_id"), wires.Field("id"))) q = q.Filter(sqlchemy.Equals(netifs.Field("baremetal_id"), hh.Id)) q = q.Distinct() ret := make([]SWire, 0) err := db.FetchModelObjects(WireManager, q, &ret) if err != nil { log.Errorf("%s", err) return nil } return ret } func (hh *SHostManager) GetEnabledKvmHostForBackupStorage(bs *SBackupStorage) (*SHost, error) { hbs, err := HostBackupstorageManager.GetBackupStoragesByBackup(bs.Id) if err != nil { return nil, errors.Wrap(err, "GetBackupStoragesByBackup") } candidates := make([]string, 0) for i := range hbs { candidates = append(candidates, hbs[i].HostId) } host, err := HostManager.GetEnabledKvmHost(candidates) if err != nil { return nil, errors.Wrap(err, "GetEnabledKvmHost") } return host, nil } func (hh *SHostManager) GetEnabledKvmHostForDiskBackup(backup *SDiskBackup) (*SHost, error) { hbsCandidates := stringutils2.NewSortedStrings(nil) hssCandidates := stringutils2.NewSortedStrings(nil) var candidates []string { bs, err := backup.GetBackupStorage() if err != nil { return nil, errors.Wrap(err, "unable to get backupStorage") } hbs, err := HostBackupstorageManager.GetBackupStoragesByBackup(bs.Id) if err != nil { return nil, errors.Wrap(err, "HostBackupstorageManager.GetBackupStoragesByBackup") } for i := range hbs { hbsCandidates = hbsCandidates.Append(hbs[i].HostId) } } if len(backup.StorageId) > 0 { storage, err := backup.GetStorage() if err != nil { return nil, errors.Wrap(err, "unable to get storage of diskbackup") } hss, err := HoststorageManager.GetHostStoragesByStorageId(storage.Id) if err != nil { return nil, errors.Wrap(err, "HoststorageManager.GetStorages") } for i := range hss { hssCandidates = hssCandidates.Append(hss[i].HostId) } if len(hbsCandidates) == 0 { candidates = []string(hssCandidates) } else { candidates = []string(stringutils2.Intersect(hbsCandidates, hssCandidates)) } } else { candidates = []string(hbsCandidates) } host, err := HostManager.GetEnabledKvmHost(candidates) if err != nil { return nil, errors.Wrap(err, "GetEnabledKvmHost") } return host, nil } func (hh *SHostManager) GetEnabledKvmHost(candidates []string) (*SHost, error) { hostq := HostManager.Query().IsTrue("enabled") hostq = hostq.Equals("host_status", api.HOST_ONLINE) hostq = hostq.In("host_type", []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_KVM, api.HOST_TYPE_CONTAINER}) hostq = hostq.IsNullOrEmpty("manager_id") if len(candidates) > 0 { hostq = hostq.In("id", candidates) } host := SHost{} err := hostq.First(&host) if err != nil { return nil, err } host.SetModelManager(HostManager, &host) return &host, nil } func (hh *SHost) GetMasterWire() *SWire { wires := WireManager.Query().SubQuery() netifs := NetInterfaceManager.Query().SubQuery() q := wires.Query() q = q.Join(netifs, sqlchemy.Equals(netifs.Field("wire_id"), wires.Field("id"))) q = q.Filter(sqlchemy.Equals(netifs.Field("baremetal_id"), hh.Id)) q = q.Filter(sqlchemy.Equals(netifs.Field("nic_type"), api.NIC_TYPE_ADMIN)) wire := SWire{} wire.SetModelManager(WireManager, &wire) err := q.First(&wire) if err != nil { log.Errorf("GetMasterWire fail %s", err) return nil } return &wire } /*func (hh *SHost) getHostwires() ([]SHostwire, error) { hostwires := make([]SHostwire, 0) q := hh.GetWiresQuery() err := db.FetchModelObjects(HostwireManager, q, &hostwires) if err != nil { return nil, err } return hostwires, nil } func (hh *SHost) getHostwiresOfId(wireId string) []SHostwire { hostwires := make([]SHostwire, 0) q := hh.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 (hh *SHost) getHostwireOfIdAndMac(wireId string, mac string) *SHostwire { hostwire := SHostwire{} hostwire.SetModelManager(HostwireManager, &hostwire) q := hh.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 (hh *SHost) GetGuestsQuery() *sqlchemy.SQuery { return GuestManager.Query().Equals("host_id", hh.Id) } func (hh *SHost) GetGuests() ([]SGuest, error) { q := hh.GetGuestsQuery() guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return guests, nil } func (hh *SHost) GetKvmGuests() []SGuest { q := GuestManager.Query().Equals("host_id", hh.Id).Equals("hypervisor", api.HYPERVISOR_KVM) guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { log.Errorf("GetGuests %s", err) return nil } return guests } func (hh *SHost) GetGuestsMasterOnThisHost() []SGuest { q := hh.GetGuestsQuery().IsNotEmpty("backup_host_id") guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { log.Errorf("GetGuests %s", err) return nil } return guests } func (hh *SHost) GetGuestsBackupOnThisHost() []SGuest { q := GuestManager.Query().Equals("backup_host_id", hh.Id) guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { log.Errorf("GetGuests %s", err) return nil } return guests } func (hh *SHost) GetBackupGuestCount() (int, error) { q := GuestManager.Query().Equals("backup_host_id", hh.Id) return q.CountWithError() } func (hh *SHost) GetGuestCount() (int, error) { q := hh.GetGuestsQuery() return q.CountWithError() } func (hh *SHost) GetContainerCount(status []string) (int, error) { q := hh.GetGuestsQuery() q = q.Filter(sqlchemy.Equals(q.Field("hypervisor"), api.HYPERVISOR_POD)) if len(status) > 0 { q = q.In("status", status) } return q.CountWithError() } func (hh *SHost) GetNonsystemGuestCount() (int, error) { q := hh.GetGuestsQuery() q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("is_system")), sqlchemy.IsFalse(q.Field("is_system")))) return q.CountWithError() } func (hh *SHost) GetRunningGuestCount() (int, error) { q := hh.GetGuestsQuery() q = q.In("status", api.VM_RUNNING_STATUS) return q.CountWithError() } func (host *SHost) hasUnknownGuests() bool { q := host.GetGuestsQuery() q = q.Equals("status", api.VM_UNKNOWN) cnt, _ := q.CountWithError() return cnt > 0 } func (hh *SHost) GetNotReadyGuestsStat() (*SHostGuestResourceUsage, error) { 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"), hh.Id), sqlchemy.Equals(q.Field("backup_host_id"), hh.Id)) q = q.Filter(cond) q = q.NotEquals("status", api.VM_READY) stat := SHostGuestResourceUsage{} err := q.First(&stat) if err != nil { return nil, err } return &stat, nil } func (hh *SHost) GetRunningGuestResourceUsage() *SHostGuestResourceUsage { return hh.getGuestsResource(api.VM_RUNNING) } func (hh *SHost) GetBaremetalnetworksQuery() *sqlchemy.SQuery { return HostnetworkManager.Query().Equals("baremetal_id", hh.Id) } func (hh *SHost) GetBaremetalnetworks() []SHostnetwork { q := hh.GetBaremetalnetworksQuery() hns := make([]SHostnetwork, 0) err := db.FetchModelObjects(HostnetworkManager, q, &hns) if err != nil { log.Errorf("GetBaremetalnetworks error: %s", err) } return hns } func (hh *SHost) GetAttach2Network(netId string) *SHostnetwork { q := hh.GetBaremetalnetworksQuery() netifs := NetInterfaceManager.Query().Equals("baremetal_id", hh.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.AND( sqlchemy.Equals(q.Field("mac_addr"), netifsSub.Field("mac")), sqlchemy.Equals(q.Field("vlan_id"), netifsSub.Field("vlan_id")), )) 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 (h *SHost) getNetInterfacesInternal(wireId string, nicTypes []compute.TNicType) []SNetInterface { q := NetInterfaceManager.Query().Equals("baremetal_id", h.Id) if len(wireId) > 0 { q = q.Equals("wire_id", wireId) } if len(nicTypes) > 0 { //q.IsNullOrEmpty() if ok, _ := utils.InArray(compute.NIC_TYPE_NORMAL, nicTypes); ok { q = q.Filter(sqlchemy.OR(sqlchemy.In(q.Field("nic_type"), nicTypes), sqlchemy.IsNull(q.Field("nic_type")))) } else { q = q.In("nic_type", nicTypes) } } q = q.Asc("index") q = q.Asc("vlan_id") q = q.Asc("nic_type") netifs := make([]SNetInterface, 0) err := db.FetchModelObjects(NetInterfaceManager, q, &netifs) if err != nil { log.Errorf("GetNetInterfaces fail %s", err) return nil } return netifs } func (hh *SHost) GetAllNetInterfaces() []SNetInterface { return hh.getNetInterfacesInternal("", nil) } func (hh *SHost) GetHostNetInterfaces() []SNetInterface { return hh.getNetInterfacesInternal("", api.HOST_NIC_TYPES) } func (hh *SHost) GetAdminNetInterfaces() []SNetInterface { return hh.getNetInterfacesInternal("", []compute.TNicType{api.NIC_TYPE_ADMIN}) } func (hh *SHost) GetNetInterface(mac string, vlanId int) *SNetInterface { netif, _ := NetInterfaceManager.FetchByMacVlan(mac, vlanId) if netif != nil && netif.BaremetalId == hh.Id { return netif } return nil } func (hh *SHost) DeleteBaremetalnetwork(ctx context.Context, userCred mcclient.TokenCredential, bn *SHostnetwork, reserve bool) { net := bn.GetNetwork() bn.Delete(ctx, userCred) db.OpsLog.LogDetachEvent(ctx, hh, net, userCred, nil) if reserve && net != nil { if len(bn.IpAddr) > 0 && regutils.MatchIP4Addr(bn.IpAddr) { ReservedipManager.ReserveIP(ctx, userCred, net, bn.IpAddr, "Delete baremetalnetwork to reserve", api.AddressTypeIPv4) } if len(bn.Ip6Addr) > 0 && regutils.MatchIP6Addr(bn.Ip6Addr) { ReservedipManager.ReserveIP(ctx, userCred, net, bn.Ip6Addr, "Delete baremetalnetwork to reserve", api.AddressTypeIPv6) } } } func (hh *SHost) GetHostDriver() (IHostDriver, error) { if len(hh.HostType) == 0 { hh.HostType = api.HOST_TYPE_DEFAULT } region, err := hh.GetRegion() if err != nil { return nil, errors.Wrapf(err, "GetRegion") } return GetHostDriver(hh.HostType, region.Provider) } func (manager *SHostManager) getHostsByZoneProvider(zone *SZone, region *SCloudregion, provider *SCloudprovider) ([]SHost, error) { hosts := make([]SHost, 0) q := manager.Query() if zone != nil { q = q.Equals("zone_id", zone.Id) } if region != nil { zoneQ := ZoneManager.Query().Equals("cloudregion_id", region.Id).SubQuery() q = q.Join(zoneQ, sqlchemy.Equals(q.Field("zone_id"), zoneQ.Field("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, region *SCloudregion, hosts []cloudprovider.ICloudHost, xor bool) ([]SHost, []cloudprovider.ICloudHost, compare.SyncResult) { key := provider.Id if zone != nil { key = fmt.Sprintf("%s-%s", zone.Id, provider.Id) } lockman.LockRawObject(ctx, manager.Keyword(), key) defer lockman.ReleaseRawObject(ctx, manager.Keyword(), key) syncResult := compare.SyncResult{} dbHosts, err := manager.getHostsByZoneProvider(zone, region, provider) if err != nil { syncResult.Error(err) return nil, nil, syncResult } localHosts := make([]SHost, 0) remoteHosts := make([]cloudprovider.ICloudHost, 0) 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 { if !xor { err = commondb[i].SyncWithCloudHost(ctx, userCred, commonext[i]) if err != nil { syncResult.UpdateError(err) } } 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 (hh *SHost) syncRemoveCloudHost(ctx context.Context, userCred mcclient.TokenCredential) error { lockman.LockObject(ctx, hh) defer lockman.ReleaseObject(ctx, hh) err := hh.validateDeleteCondition(ctx, true) if err != nil { err = hh.purge(ctx, userCred) if err != nil { return errors.Wrap(err, "purge") } } else { err = hh.RealDelete(ctx, userCred) } return err } func (hh *SHost) SyncWithCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost) error { provider := hh.GetCloudprovider() diff, err := db.UpdateWithLock(ctx, hh, func() error { // hh.Name = extHost.GetName() hh.Status = extHost.GetStatus() hh.HostStatus = extHost.GetHostStatus() hh.AccessIp = extHost.GetAccessIp() hh.AccessMac = extHost.GetAccessMac() hh.SN = extHost.GetSN() hh.SysInfo = extHost.GetSysInfo() hh.StorageType = extHost.GetStorageType() hh.HostType = extHost.GetHostType() if hh.HostType == api.HOST_TYPE_BAREMETAL { hh.IsBaremetal = true } hh.StorageInfo = extHost.GetStorageInfo() if storageDriver := extHost.GetStorageDriver(); len(storageDriver) > 0 { hh.StorageDriver = storageDriver } hh.OvnVersion = extHost.GetOvnVersion() if ipmiInfo := extHost.GetIpmiInfo(); !gotypes.IsNil(ipmiInfo) { info := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) passwd, _ := info.GetString("password") if len(passwd) > 0 { passwd, _ = utils.EncryptAESBase64(hh.Id, passwd) info.Set("password", jsonutils.NewString(passwd)) } hh.IpmiInfo = info } if provider != nil && !utils.IsInStringArray(provider.Provider, strings.Split(options.Options.SkipSyncHostConfigInfoProviders, ",")) { hh.CpuCount = extHost.GetCpuCount() hh.NodeCount = extHost.GetNodeCount() cpuDesc := extHost.GetCpuDesc() if len(cpuDesc) > 128 { cpuDesc = cpuDesc[:128] } hh.CpuDesc = cpuDesc hh.CpuMhz = extHost.GetCpuMhz() hh.MemSize = extHost.GetMemSizeMB() hh.StorageSize = extHost.GetStorageSizeMB() if cpuCmt := extHost.GetCpuCmtbound(); cpuCmt > 0 { hh.CpuCmtbound = cpuCmt } if memCmt := extHost.GetMemCmtbound(); memCmt > 0 { hh.MemCmtbound = memCmt } if arch := extHost.GetCpuArchitecture(); len(arch) > 0 { hh.CpuArchitecture = arch } if reservedMem := extHost.GetReservedMemoryMb(); reservedMem > 0 { hh.MemReserved = reservedMem } } hh.IsEmulated = extHost.IsEmulated() hh.SetEnabled(extHost.GetEnabled()) hh.IsMaintenance = extHost.GetIsMaintenance() hh.Version = extHost.GetVersion() return nil }) if err != nil { return errors.Wrapf(err, "syncWithCloudZone") } db.OpsLog.LogSyncUpdate(hh, diff, userCred) if provider != nil { SyncCloudDomain(userCred, hh, provider.GetOwnerId()) hh.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) } if account := hh.GetCloudaccount(); account != nil { syncMetadata(ctx, userCred, hh, extHost, account.ReadOnly) } if !options.Options.DisableSyncSchedtags { err = hh.syncSchedtags(ctx, userCred, extHost) if err != nil && errors.Cause(err) != cloudprovider.ErrNotFound && errors.Cause(err) != errors.ErrNotImplemented { log.Errorf("syncSchedtags for %s fail: %v", hh.Name, err) } } if len(diff) > 0 { if err := HostManager.ClearSchedDescCache(hh.Id); err != nil { log.Errorf("ClearSchedDescCache for host %s error %v", hh.Name, err) } } return nil } func (hh *SHost) syncWithCloudPrepaidVM(extVM cloudprovider.ICloudVM, host *SHost) error { _, err := hh.SaveUpdates(func() error { hh.CpuCount = extVM.GetVcpuCount() hh.MemSize = extVM.GetVmemSizeMB() hh.BillingType = billing_api.ParseBillingType(extVM.GetBillingType()) hh.ExpiredAt = extVM.GetExpiredAt() hh.ExternalId = host.ExternalId return nil }) if err != nil { log.Errorf("syncWithCloudZone error %s", err) } if err := HostManager.ClearSchedDescCache(hh.Id); err != nil { log.Errorf("ClearSchedDescCache for host %s error %v", hh.Name, err) } return err } var ( METADATA_EXT_SCHEDTAG_KEY = "ext:schedtag" ) func (h *SHost) getAllSchedtagsWithExtSchedtagKey() (map[string]string, error) { sq := SchedtagManager.Query("id").SubQuery() q := db.Metadata.Query("obj_id", "value").Equals("obj_type", SchedtagManager.Keyword()).Equals("key", METADATA_EXT_SCHEDTAG_KEY).In("obj_id", sq) result := []struct { ObjId string Value string }{} err := q.All(&result) if err != nil { return nil, err } ret := make(map[string]string) for i := range result { ret[result[i].Value] = result[i].ObjId } return ret, nil } func (h *SHost) GetSchedtags() ([]SSchedtag, error) { sq := HostschedtagManager.Query("schedtag_id").Equals("host_id", h.Id).SubQuery() q := SchedtagManager.Query().In("id", sq) schedtags := make([]SSchedtag, 0) err := db.FetchModelObjects(SchedtagManager, q, &schedtags) if err != nil { return nil, errors.Wrap(err, "db.FetchModelObjects") } return schedtags, nil } func (h *SHost) syncSchedtags(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost) error { schedtags, err := h.GetSchedtags() if err != nil { return errors.Wrap(err, "GetSchedtags") } extSchedTags, err := extHost.GetSchedtags() if err != nil { return errors.Wrap(err, "extHost.GetSchedtags") } extTagMap := map[string]*cloudprovider.Schedtag{} extTagIdSet := sets.NewString() for i := range extSchedTags { extSchedtag := &extSchedTags[i] extTagIdSet.Insert(extSchedtag.Id) extTagMap[extSchedtag.Id] = &extSchedTags[i] } removed := make([]*SSchedtag, 0) removedIds := make([]string, 0) for i := range schedtags { stag := &schedtags[i] extTagId := stag.GetMetadata(ctx, METADATA_EXT_SCHEDTAG_KEY, userCred) if len(extTagId) == 0 { continue } if !extTagIdSet.Has(extTagId) { removed = append(removed, stag) removedIds = append(removedIds, stag.GetId()) } else { extTagIdSet.Delete(extTagId) } } added := extTagIdSet.UnsortedList() stagMap := make(map[string]string) if len(added) > 0 { stagMap, err = h.getAllSchedtagsWithExtSchedtagKey() if err != nil { return errors.Wrap(err, "getAllSchedtagsWithExtSchedtagKey") } } for _, extSchedId := range added { stagId, ok := stagMap[extSchedId] if !ok { st := &SSchedtag{ ResourceType: HostManager.KeywordPlural(), } st.DomainId = h.DomainId st.Name = extTagMap[extSchedId].Name st.Description = "Sync from cloud" st.SetModelManager(SchedtagManager, st) err := SchedtagManager.TableSpec().Insert(ctx, st) if err != nil { return errors.Wrapf(err, "unable to create schedtag %s", st.Name) } stagId = st.GetId() meta := make(map[string]interface{}) meta[METADATA_EXT_SCHEDTAG_KEY] = extSchedId for k, v := range extTagMap[extSchedId].Meta { meta[k] = v } st.SetAllMetadata(ctx, meta, userCred) } // attach hostschedtag := &SHostschedtag{ HostId: h.GetId(), } hostschedtag.SetModelManager(HostschedtagManager, hostschedtag) hostschedtag.SchedtagId = stagId err = HostschedtagManager.TableSpec().Insert(ctx, hostschedtag) if err != nil { return errors.Wrapf(err, "unable to create hostschedtag for tag %s host %s", stagId, h.GetId()) } } if len(removedIds) == 0 { return nil } q := HostschedtagManager.Query().Equals("host_id", h.GetId()).In("schedtag_id", removedIds) hostschedtags := make([]SHostschedtag, 0, len(removedIds)) err = db.FetchModelObjects(HostschedtagManager, q, &hostschedtags) if err != nil { return errors.Wrap(err, "db.FetchModelObject") } for i := range hostschedtags { err = hostschedtags[i].Detach(ctx, userCred) if err != nil { return errors.Wrapf(err, "unable to detach host %q and schedtag %q", hostschedtags[i].HostId, hostschedtags[i].SchedtagId) } } // try to clean for _, tag := range removed { cnt, err := tag.GetObjectCount() if err != nil { log.Errorf("unable to GetObjectCount for schedtag %q: %v", tag.GetName(), err) continue } if cnt > 0 { continue } err = tag.Delete(ctx, userCred) if err != nil { log.Errorf("unable to delete schedtag %q: %v", tag.GetName(), err) continue } } return nil } 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()) return nil, fmt.Errorf("%s", msg) } wire, err := WireManager.GetOnPremiseWireOfIp(accessIp) if err != nil { return nil, errors.Wrapf(err, "GetOnPremiseWireOfIp for host %s with ip %s", extHost.GetName(), accessIp) } izone, err = wire.GetZone() if err != nil { return nil, errors.Wrapf(err, "get zone for wire %s", wire.Name) } } host.ExternalId = extHost.GetGlobalId() host.ZoneId = izone.Id host.HostType = extHost.GetHostType() if host.HostType == api.HOST_TYPE_BAREMETAL { host.IsBaremetal = true } host.StorageInfo = extHost.GetStorageInfo() host.OvnVersion = extHost.GetOvnVersion() if ipmiInfo := extHost.GetIpmiInfo(); !gotypes.IsNil(ipmiInfo) { info := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) passwd, _ := info.GetString("password") if len(passwd) > 0 { passwd, _ = utils.EncryptAESBase64(host.Id, passwd) info.Set("password", jsonutils.NewString(passwd)) } host.IpmiInfo = info } 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() cpuDesc := extHost.GetCpuDesc() if len(cpuDesc) > 128 { cpuDesc = cpuDesc[:128] } host.CpuDesc = cpuDesc host.CpuMhz = extHost.GetCpuMhz() host.MemSize = extHost.GetMemSizeMB() host.StorageSize = extHost.GetStorageSizeMB() host.StorageType = extHost.GetStorageType() host.StorageDriver = extHost.GetStorageDriver() 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 arch := extHost.GetCpuArchitecture(); len(arch) > 0 { host.CpuArchitecture = arch } 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(rbacscope.ScopeNone) var err = func() error { lockman.LockRawObject(ctx, manager.Keyword(), "name") defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name") //newName, err := db.GenerateName(ctx, manager, userCred, extHost.GetName()) //if err != nil { // return errors.Wrapf(err, "db.GenerateName") //} host.Name = extHost.GetName() return manager.TableSpec().Insert(ctx, &host) }() if err != nil { return nil, errors.Wrapf(err, "Insert") } db.OpsLog.LogEvent(&host, db.ACT_CREATE, host.GetShortDesc(ctx), userCred) SyncCloudDomain(userCred, &host, provider.GetOwnerId()) err = host.syncSchedtags(ctx, userCred, extHost) if err != nil && errors.Cause(err) != cloudprovider.ErrNotFound && errors.Cause(err) != errors.ErrNotImplemented { log.Errorf("syncSchedtags %s fail %v", host.Name, err) } 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 (hh *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.TokenCredential, storages []cloudprovider.ICloudStorage, provider *SCloudprovider, xor bool) ([]SStorage, []cloudprovider.ICloudStorage, compare.SyncResult) { lockman.LockRawObject(ctx, "storages", hh.Id) defer lockman.ReleaseRawObject(ctx, "storages", hh.Id) localStorages := make([]SStorage, 0) remoteStorages := make([]cloudprovider.ICloudStorage, 0) syncResult := compare.SyncResult{} dbStorages := make([]SStorage, 0) hostStorages := hh.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 := hh._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...", hh.Id, removed[i].Id) err := hh.syncRemoveCloudHostStorage(ctx, userCred, &removed[i]) if errors.Cause(err) == ErrStorageInUse && removed[i].StorageType == api.STORAGE_LOCAL { removed[i].SetStatus(ctx, 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 { if !xor { log.Infof("host %s is still connected with %s, to update ...", hh.Id, commondb[i].Id) err := hh.syncWithCloudHostStorage(ctx, userCred, &commondb[i], commonext[i], provider) if err != nil { syncResult.UpdateError(err) } } 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 ...", hh.Id, added[i].GetId()) local, err := hh.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 (hh *SHost) syncRemoveCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, localStorage *SStorage) error { hs := hh.GetHoststorageOfId(localStorage.Id) err := hs.ValidateDeleteCondition(ctx, nil) if err == nil { log.Errorf("sync remove hoststorage fail: %s", err) err = hs.Detach(ctx, userCred) } else { } return err } func (hh *SHost) syncWithCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, localStorage *SStorage, extStorage cloudprovider.ICloudStorage, provider *SCloudprovider) error { // do nothing hs := hh.GetHoststorageOfId(localStorage.Id) err := hs.syncWithCloudHostStorage(userCred, extStorage) if err != nil { return err } s := hs.GetStorage() err = s.syncWithCloudStorage(ctx, userCred, extStorage, provider) return err } func (hh *SHost) isAttach2Storage(storage *SStorage) bool { hs := hh.GetHoststorageOfId(storage.Id) return hs != nil } func (hh *SHost) Attach2Storage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, mountPoint string) error { if hh.isAttach2Storage(storage) { return nil } hs := SHoststorage{} hs.SetModelManager(HoststorageManager, &hs) hs.StorageId = storage.Id hs.HostId = hh.Id hs.MountPoint = mountPoint err := HoststorageManager.TableSpec().Insert(ctx, &hs) if err != nil { return err } db.OpsLog.LogAttachEvent(ctx, hh, storage, userCred, nil) return nil } func (hh *SHost) newCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage, provider *SCloudprovider) (*SStorage, error) { storageObj, err := db.FetchByExternalIdAndManagerId(StorageManager, extStorage.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery { return q.Equals("manager_id", provider.Id) }) if err != nil { if errors.Cause(err) == sql.ErrNoRows { // no cloud storage found, this may happen for on-premise host // create the storage right now zone, _ := hh.GetZone() storageObj, err = StorageManager.newFromCloudStorage(ctx, userCred, extStorage, provider, zone) if err != nil { return nil, errors.Wrapf(err, "StorageManager.newFromCloudStorage") } } else { return nil, errors.Wrapf(err, "FetchByExternalIdAndManagerId(%s)", extStorage.GetGlobalId()) } } storage := storageObj.(*SStorage) err = hh.Attach2Storage(ctx, userCred, storage, extStorage.GetMountPoint()) return storage, err } func (hh *SHost) SyncHostWires(ctx context.Context, userCred mcclient.TokenCredential, wires []cloudprovider.ICloudWire) compare.SyncResult { lockman.LockRawObject(ctx, "wires", hh.Id) defer lockman.ReleaseRawObject(ctx, "wires", hh.Id) syncResult := compare.SyncResult{} dbWires := make([]SWire, 0) hostNetifs := hh.GetHostNetInterfaces() for i := 0; i < len(hostNetifs); i += 1 { wire := hostNetifs[i].GetWire() if wire != nil { // hostNetifs[i].Remove(ctx, userCred) } else { dbWires = append(dbWires, *wire) } } // dbWires := hh.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...", hh.Id, removed[i].Id) err := hh.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...", hh.Id, commondb[i].Id) err := hh.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...", hh.Id, added[i].GetId()) err := hh.newCloudHostWire(ctx, userCred, added[i]) if err != nil { syncResult.AddError(err) } else { syncResult.Add() } } return syncResult } func (hh *SHost) syncRemoveCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, localwire *SWire) error { netifs := hh.getNetifsOnWire(localwire.Id) for i := range netifs { err := netifs[i].Delete(ctx, userCred) if err != nil { return errors.Wrap(err, "delete") } } return nil } func (hh *SHost) syncWithCloudHostWire(extWire cloudprovider.ICloudWire) error { // do nothing return nil } func (hh *SHost) Attach2Wire(ctx context.Context, userCred mcclient.TokenCredential, wire *SWire) error { netif := SNetInterface{} netif.SetModelManager(NetInterfaceManager, &netif) netif.Mac = stringutils2.HashIdsMac(hh.Id, wire.Id) netif.VlanId = 1 netif.WireId = wire.Id netif.BaremetalId = hh.Id err := NetInterfaceManager.TableSpec().InsertOrUpdate(ctx, &netif) if err != nil { return errors.Wrap(err, "InsertOrUpdate") } return nil } func (hh *SHost) newCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire) error { wireObj, err := db.FetchByExternalIdAndManagerId(WireManager, extWire.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery { sq := VpcManager.Query().SubQuery() return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("vpc_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), hh.ManagerId)) }) if err != nil { log.Errorf("%s", err) return nil } wire := wireObj.(*SWire) err = hh.Attach2Wire(ctx, userCred, wire) return err } type SGuestSyncResult struct { Local *SGuest Remote cloudprovider.ICloudVM IsNew bool } func IsNeedSkipSync(ext cloudprovider.ICloudResource) (bool, string) { if len(options.Options.SkipServerBySysTagKeys) == 0 && len(options.Options.SkipServerByUserTagKeys) == 0 && len(options.Options.SkipServerByUserTagValues) == 0 && len(options.Options.RetentionServerByUserTagKeys) == 0 && len(options.Options.RetentionServerByUserTagValues) == 0 && len(options.Options.RetentionServerByUserTags) == 0 { return false, "" } tags, _ := ext.GetTags() if keys := strings.Split(options.Options.SkipServerBySysTagKeys, ","); len(keys) > 0 { for key := range ext.GetSysTags() { key = strings.Trim(key, "") if len(key) > 0 && utils.IsInStringArray(key, keys) { return true, key } } } if userKeys := strings.Split(options.Options.SkipServerByUserTagKeys, ","); len(userKeys) > 0 { for key := range tags { key = strings.Trim(key, "") if len(key) > 0 && utils.IsInStringArray(key, userKeys) { return true, key } } } if len(options.Options.SkipServerByUserTagValues) > 0 { for _, value := range tags { value = strings.Trim(value, "") if len(value) > 0 && utils.IsInStringArray(value, options.Options.SkipServerByUserTagValues) { return true, value } } } keys, values, pairs := []string{}, []string{}, []string{} for key, value := range tags { key = strings.Trim(key, "") keys = append(keys, key) values = append(values, value) pairs = append(pairs, fmt.Sprintf("%s:%s", key, value)) } if len(options.Options.RetentionServerByUserTagKeys) > 0 { skip, tagKey := true, "" for _, key := range options.Options.RetentionServerByUserTagKeys { key = strings.Trim(key, "") if len(key) > 0 && utils.IsInStringArray(key, keys) { skip, tagKey = false, key break } } return skip, tagKey } if len(options.Options.RetentionServerByUserTagValues) > 0 { skip, tagValue := true, "" for _, value := range options.Options.RetentionServerByUserTagValues { value = strings.Trim(value, "") if len(value) > 0 && utils.IsInStringArray(value, values) { skip, tagValue = false, value break } } return skip, tagValue } if len(options.Options.RetentionServerByUserTags) > 0 { skip, tagPair := true, "" for _, pair := range options.Options.RetentionServerByUserTags { pair = strings.Trim(pair, "") if len(pair) > 0 && utils.IsInStringArray(pair, pairs) { skip, tagPair = false, pair break } } return skip, tagPair } return false, "" } func (self *SGuest) Purge(ctx context.Context, userCred mcclient.TokenCredential) error { return self.purge(ctx, userCred) } func (hh *SHost) GetIsolateDevices() ([]SIsolatedDevice, error) { q := IsolatedDeviceManager.Query().Equals("host_id", hh.Id) ret := []SIsolatedDevice{} err := db.FetchModelObjects(IsolatedDeviceManager, q, &ret) if err != nil { return nil, err } return ret, nil } func (hh *SHost) SyncHostIsolateDevices(ctx context.Context, userCred mcclient.TokenCredential, iprovider cloudprovider.ICloudProvider, devs []cloudprovider.IsolateDevice, syncOwnerId mcclient.IIdentityProvider, xor bool) compare.SyncResult { lockman.LockRawObject(ctx, IsolatedDeviceManager.Keyword(), hh.Id) defer lockman.ReleaseRawObject(ctx, IsolatedDeviceManager.Keyword(), hh.Id) result := compare.SyncResult{} dbDevs, err := hh.GetIsolateDevices() if err != nil { result.Error(errors.Wrapf(err, "GetIsolateDevices")) return result } removed := make([]SIsolatedDevice, 0) commondb := make([]SIsolatedDevice, 0) commonext := make([]cloudprovider.IsolateDevice, 0) added := make([]cloudprovider.IsolateDevice, 0) duplicated := make(map[string][]cloudprovider.IsolateDevice) err = compare.CompareSets2(dbDevs, devs, &removed, &commondb, &commonext, &added, &duplicated) if err != nil { result.Error(err) return result } for i := 0; i < len(removed); i += 1 { err := removed[i].Delete(ctx, userCred) if err != nil { result.DeleteError(err) continue } result.Delete() } if !xor { for i := 0; i < len(commondb); i += 1 { err := commondb[i].syncWithCloudIsolateDevice(ctx, userCred, commonext[i]) if err != nil { result.UpdateError(err) continue } result.Update() } } for i := 0; i < len(added); i += 1 { err := hh.newIsolateDevice(ctx, userCred, added[i]) if err != nil { result.AddError(err) continue } result.Add() } if len(duplicated) > 0 { errs := make([]error, 0) for k, vms := range duplicated { errs = append(errs, errors.Wrapf(errors.ErrDuplicateId, "Duplicate Id %s (%d)", k, len(vms))) } result.AddError(errors.NewAggregate(errs)) } return result } func (hh *SHost) newIsolateDevice(ctx context.Context, userCred mcclient.TokenCredential, dev cloudprovider.IsolateDevice) error { ret := &SIsolatedDevice{} ret.SetModelManager(IsolatedDeviceManager, ret) ret.HostId = hh.Id ret.ExternalId = dev.GetGlobalId() ret.Name = dev.GetName() ret.Model = dev.GetModel() ret.Addr = dev.GetAddr() ret.DevType = dev.GetDevType() ret.NumaNode = dev.GetNumaNode() ret.VendorDeviceId = dev.GetVendorDeviceId() err := IsolatedDeviceManager.TableSpec().Insert(ctx, ret) if err != nil { return err } sharedProjectIds, err := dev.GetSharedProjectIds() if err != nil { if errors.Cause(err) == cloudprovider.ErrNotImplemented { return nil } return err } if len(sharedProjectIds) == 0 { return nil } if len(sharedProjectIds) > 0 { projectIds, err := db.FetchField(ExternalProjectManager, "tenant_id", func(q *sqlchemy.SQuery) *sqlchemy.SQuery { return q.Equals("manager_id", hh.ManagerId).In("external_id", sharedProjectIds) }) if err != nil { return err } input := apis.PerformPublicProjectInput{SharedProjectIds: projectIds} input.Scope = "project" db.SharablePerformPublic(ret, ctx, userCred, input) } return nil } func (hh *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCredential, iprovider cloudprovider.ICloudProvider, vms []cloudprovider.ICloudVM, syncOwnerId mcclient.IIdentityProvider, xor bool) ([]SGuestSyncResult, compare.SyncResult) { lockman.LockRawObject(ctx, GuestManager.Keyword(), hh.Id) defer lockman.ReleaseRawObject(ctx, GuestManager.Keyword(), hh.Id) syncVMPairs := make([]SGuestSyncResult, 0) syncResult := compare.SyncResult{} dbVMs, err := hh.GetGuests() if err != nil { syncResult.Error(errors.Wrapf(err, "GetGuests")) return nil, syncResult } 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) duplicated := make(map[string][]cloudprovider.ICloudVM) err = compare.CompareSets2(dbVMs, vms, &removed, &commondb, &commonext, &added, &duplicated) if err != nil { syncResult.Error(err) return nil, syncResult } for i := 0; i < len(removed); i += 1 { err := removed[i].SyncRemoveCloudVM(ctx, userCred, true) if err != nil { syncResult.DeleteError(err) } else { syncResult.Delete() } } if !xor { for i := 0; i < len(commondb); i += 1 { skip, key := IsNeedSkipSync(commonext[i]) if skip { log.Infof("delete server %s(%s) with tag key or value: %s", commonext[i].GetName(), commonext[i].GetGlobalId(), key) err := commondb[i].purge(ctx, userCred) if err != nil { syncResult.DeleteError(err) continue } syncResult.Delete() continue } err := commondb[i].syncWithCloudVM(ctx, userCred, iprovider, hh, commonext[i], syncOwnerId, true) if err != nil { syncResult.UpdateError(err) continue } syncVMPair := SGuestSyncResult{ Local: &commondb[i], Remote: commonext[i], IsNew: false, } syncVMPairs = append(syncVMPairs, syncVMPair) syncResult.Update() } } for i := 0; i < len(added); i += 1 { skip, key := IsNeedSkipSync(added[i]) if skip { log.Infof("skip server %s(%s) sync with tag key or value: %s", added[i].GetName(), added[i].GetGlobalId(), key) continue } vm, err := db.FetchByExternalIdAndManagerId(GuestManager, added[i].GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery { sq := HostManager.Query().SubQuery() return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("host_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), hh.ManagerId)) }) if err != nil && errors.Cause(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.FetchByExternalIdAndManagerId(HostManager, ihost.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery { return q.Equals("manager_id", hh.ManagerId) }) 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, true) if err != nil { syncResult.UpdateError(err) } else { syncResult.Update() } continue } if added[i].GetBillingType() == string(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, hh, 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() } } if len(duplicated) > 0 { errs := make([]error, 0) for k, vms := range duplicated { errs = append(errs, errors.Wrapf(errors.ErrDuplicateId, "Duplicate Id %s (%d)", k, len(vms))) } syncResult.AddError(errors.NewAggregate(errs)) } return syncVMPairs, syncResult } func (hh *SHost) getNetworkOfIPOnHost(ctx context.Context, ipAddr string) (*SNetwork, error) { netInterfaces := hh.GetHostNetInterfaces() for _, netInterface := range netInterfaces { network, err := netInterface.GetCandidateNetworkForIp(ctx, nil, nil, rbacscope.ScopeNone, ipAddr) if err == nil && network != nil { return network, nil } } return nil, fmt.Errorf("IP %s not reachable on this host", ipAddr) } func (hh *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) ([]SNetInterface, *SNetwork, error) { netObj, err := NetworkManager.FetchById(netId) if err != nil { return nil, nil, errors.Wrapf(err, "fetch by id %q", netId) } net := netObj.(*SNetwork) used, err := net.getFreeAddressCount() if err != nil { return nil, nil, errors.Wrapf(err, "get network %q free address count", net.GetName()) } if used == 0 && !reserved && !options.Options.BaremetalServerReuseHostIp { return nil, nil, errors.Errorf("network %q out of usage", net.GetName()) } matchNetIfs := make([]SNetInterface, 0) netifs := hh.GetHostNetInterfaces() 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, nil } return nil, nil, errors.Errorf("not found matched netinterface by net %q wire %q", net.GetName(), net.WireId) } func (hh *SHost) GetNetworkWithId(netId string, reserved bool) (*SNetwork, error) { var q1, q2, q3, q4 *sqlchemy.SQuery { // classic network networks := NetworkManager.Query() netifs := NetInterfaceManager.Query().SubQuery() hosts := HostManager.Query().SubQuery() q1 = networks q1 = q1.Join(netifs, sqlchemy.Equals(netifs.Field("wire_id"), networks.Field("wire_id"))) q1 = q1.Join(hosts, sqlchemy.Equals(hosts.Field("id"), netifs.Field("baremetal_id"))) q1 = q1.Filter(sqlchemy.Equals(networks.Field("id"), netId)) q1 = q1.Filter(sqlchemy.Equals(hosts.Field("id"), hh.Id)) } { // vpc network 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.OR( sqlchemy.AND( sqlchemy.Equals(regions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD), sqlchemy.NOT(sqlchemy.Equals(vpcs.Field("id"), api.DEFAULT_VPC_ID)), ), sqlchemy.AND( sqlchemy.Equals(regions.Field("provider"), api.CLOUD_PROVIDER_CLOUDPODS), sqlchemy.NOT(sqlchemy.Equals(vpcs.Field("external_id"), api.DEFAULT_VPC_ID)), ), ), ) } { // network additional wires networks := NetworkManager.Query() networkAdditionalWires := NetworkAdditionalWireManager.Query().SubQuery() netifs := NetInterfaceManager.Query().SubQuery() hosts := HostManager.Query().SubQuery() q3 = networks q3 = q3.Join(networkAdditionalWires, sqlchemy.Equals(networks.Field("id"), networkAdditionalWires.Field("network_id"))) q3 = q3.Join(netifs, sqlchemy.Equals(netifs.Field("wire_id"), networkAdditionalWires.Field("wire_id"))) q3 = q3.Join(hosts, sqlchemy.Equals(hosts.Field("id"), netifs.Field("baremetal_id"))) q3 = q3.Filter(sqlchemy.Equals(networks.Field("id"), netId)) q3 = q3.Filter(sqlchemy.Equals(hosts.Field("id"), hh.Id)) } { // host local network networks := NetworkManager.Query() wires := WireManager.Query().SubQuery() vpcs := VpcManager.Query().SubQuery() regions := CloudregionManager.Query().SubQuery() q4 = networks q4 = q4.Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))) q4 = q4.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id"))) q4 = q4.Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id"))) q4 = q4.Filter(sqlchemy.Equals(networks.Field("id"), netId)) q4 = q4.Filter( sqlchemy.OR( sqlchemy.AND( sqlchemy.Equals(regions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD), sqlchemy.Equals(vpcs.Field("id"), api.DEFAULT_VPC_ID), sqlchemy.Equals(wires.Field("id"), computeapi.DEFAULT_HOST_LOCAL_WIRE_ID), ), sqlchemy.AND( sqlchemy.Equals(regions.Field("provider"), api.CLOUD_PROVIDER_CLOUDPODS), sqlchemy.Equals(vpcs.Field("external_id"), api.DEFAULT_VPC_ID), sqlchemy.Equals(wires.Field("external_id"), computeapi.DEFAULT_HOST_LOCAL_WIRE_ID), ), ), ) } q := sqlchemy.Union(q1, q2, q3, q4).Query().Distinct() 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) FetchHostByHostname(hostname string) *SHost { host := SHost{} host.SetModelManager(manager, &host) err := manager.Query().Startswith("name", hostname).First(&host) if err != nil { log.Errorf("fetch host by hostname %s failed %s", hostname, err) return nil } else { return &host } } func (manager *SHostManager) totalCountQ( ctx context.Context, userCred mcclient.IIdentityProvider, scope rbacscope.TRbacScope, rangeObjs []db.IStandaloneModel, hostStatus, status string, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, enabled, isBaremetal tristate.TriState, policyResult rbacutils.SPolicyResult, ) *sqlchemy.SQuery { hosts := manager.Query().SubQuery() q := hosts.Query( hosts.Field("mem_size"), hosts.Field("memory_used_mb"), hosts.Field("page_size_kb"), hosts.Field("mem_reserved"), hosts.Field("mem_cmtbound"), hosts.Field("cpu_count"), hosts.Field("cpu_usage_percent"), hosts.Field("cpu_reserved"), hosts.Field("cpu_cmtbound"), hosts.Field("storage_size"), ) if scope != rbacscope.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() { if isBaremetal.Bool() { q = q.Filter(sqlchemy.AND( sqlchemy.IsTrue(hosts.Field("is_baremetal")), sqlchemy.Equals(hosts.Field("host_type"), api.HOST_TYPE_BAREMETAL), )) } else { q = q.Filter(sqlchemy.OR( sqlchemy.IsFalse(hosts.Field("is_baremetal")), sqlchemy.NotEquals(hosts.Field("host_type"), api.HOST_TYPE_BAREMETAL), )) } } q = db.ObjectIdQueryWithPolicyResult(ctx, q, HostManager, policyResult) isolatedDevices := IsolatedDeviceManager.Query().SubQuery() iq := isolatedDevices.Query( isolatedDevices.Field("host_id"), sqlchemy.SUM("isolated_reserved_memory", isolatedDevices.Field("reserved_memory")), sqlchemy.SUM("isolated_reserved_cpu", isolatedDevices.Field("reserved_cpu")), sqlchemy.SUM("isolated_reserved_storage", isolatedDevices.Field("reserved_storage")), ).IsNullOrEmpty("guest_id").GroupBy(isolatedDevices.Field("host_id")).SubQuery() q = q.LeftJoin(iq, sqlchemy.Equals(q.Field("id"), iq.Field("host_id"))) q.AppendField( iq.Field("isolated_reserved_memory"), iq.Field("isolated_reserved_cpu"), iq.Field("isolated_reserved_storage"), ) q = AttachUsageQuery(q, hosts, hostTypes, resourceTypes, providers, brands, cloudEnv, rangeObjs) // log.Debugf("hostCount: %s", q.String()) return q } type HostStat struct { MemSize int MemoryUsedMb int64 PageSizeKB int MemReserved int MemCmtbound float32 CpuCount int CpuUsagePercent float64 CpuReserved int CpuCmtbound float32 StorageSize int IsolatedReservedMemory int64 IsolatedReservedCpu int64 IsolatedReservedStorage int64 } type HostsCountStat struct { StorageSize int64 Count int64 Memory int64 MemoryUsed int64 MemoryTotal int64 MemoryVirtual float64 MemoryReserved int64 CPU int64 CPUTotal int64 CPUUsed int64 CPUVirtual float64 IsolatedReservedMemory int64 IsolatedReservedCpu int64 IsolatedReservedStorage int64 } 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 rMem int64 = 0 tCPU int64 = 0 tVCPU float64 = 0.0 irMem int64 = 0 irCpu int64 = 0 irStore int64 = 0 totalMem int64 = 0 totalMemUsed int64 = 0 totalCPU int64 = 0 totalCPUUsed 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) totalMem += int64(stat.MemSize) totalMemUsed += int64(stat.MemoryUsedMb) tCPU += int64(aCpu) totalCPU += int64(stat.CpuCount) totalCPUUsed += stat.CpuUsagePercent * float64(stat.CpuCount) / 100 if isHugePage(stat.PageSizeKB) { stat.MemCmtbound = 1.0 } else if stat.MemCmtbound <= 0.0 { stat.MemCmtbound = options.Options.DefaultMemoryOvercommitBound } if stat.CpuCmtbound <= 0.0 { stat.CpuCmtbound = options.Options.DefaultCPUOvercommitBound } rMem += int64(stat.MemReserved) tVmem += float64(float32(aMem) * stat.MemCmtbound) tVCPU += float64(float32(aCpu) * stat.CpuCmtbound) irMem += stat.IsolatedReservedMemory irCpu += stat.IsolatedReservedCpu irStore += stat.IsolatedReservedStorage } return HostsCountStat{ StorageSize: tStore, Count: tCnt, Memory: tMem, MemoryUsed: totalMemUsed, MemoryTotal: totalMem, MemoryVirtual: tVmem, MemoryReserved: rMem, CPU: tCPU, CPUUsed: int64(totalCPUUsed), CPUTotal: totalCPU, CPUVirtual: tVCPU, IsolatedReservedCpu: irCpu, IsolatedReservedMemory: irMem, IsolatedReservedStorage: irStore, } } func (manager *SHostManager) TotalCount( ctx context.Context, userCred mcclient.IIdentityProvider, scope rbacscope.TRbacScope, rangeObjs []db.IStandaloneModel, hostStatus, status string, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, enabled, isBaremetal tristate.TriState, policyResult rbacutils.SPolicyResult, ) HostsCountStat { return manager.calculateCount( manager.totalCountQ( ctx, userCred, scope, rangeObjs, hostStatus, status, hostTypes, resourceTypes, providers, brands, cloudEnv, enabled, isBaremetal, policyResult, ), ) } func (hh *SHost) GetIHost(ctx context.Context) (cloudprovider.ICloudHost, error) { host, _, err := hh.GetIHostAndProvider(ctx) return host, err } func (hh *SHost) GetIHostAndProvider(ctx context.Context) (cloudprovider.ICloudHost, cloudprovider.ICloudProvider, error) { iregion, provider, err := hh.GetIRegionAndProvider(ctx) if err != nil { return nil, nil, errors.Wrapf(err, "GetIRegionAndProvider") } ihost, err := iregion.GetIHostById(hh.ExternalId) if err != nil { return nil, nil, errors.Wrapf(err, "iregion.GetIHostById(%s)", hh.ExternalId) } return ihost, provider, nil } func (hh *SHost) GetIRegionAndProvider(ctx context.Context) (cloudprovider.ICloudRegion, cloudprovider.ICloudProvider, error) { provider, err := hh.GetDriver(ctx) if err != nil { return nil, nil, errors.Wrapf(err, "GetDriver") } var iregion cloudprovider.ICloudRegion if provider.GetFactory().IsOnPremise() { iregion, err = provider.GetOnPremiseIRegion() if err != nil { return nil, nil, errors.Wrapf(err, "provider.GetOnPremiseIRegio") } } else { region, err := hh.GetRegion() if err != nil { return nil, nil, errors.Wrapf(err, "GetRegion") } iregion, err = provider.GetIRegionById(region.ExternalId) if err != nil { return nil, nil, errors.Wrapf(err, "provider.GetIRegionById(%s)", region.ExternalId) } } return iregion, provider, nil } func (hh *SHost) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) { region, _, err := hh.GetIRegionAndProvider(ctx) return region, err } func (hh *SHost) getDiskConfig() jsonutils.JSONObject { bs := hh.GetBaremetalstorage() if bs != nil { return bs.Config } return nil } func (hh *SHost) GetBaremetalServer() *SGuest { if !hh.IsBaremetal { return nil } guest := SGuest{} guest.SetModelManager(GuestManager, &guest) q := GuestManager.Query().Equals("host_id", hh.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 } type SHostGuestResourceUsage struct { GuestCount int GuestVcpuCount int GuestVmemSize int } func (hh *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"), hh.Id), sqlchemy.Equals(q.Field("backup_host_id"), hh.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 fetchHostGuestResource(hostIds []string, status string) (map[string]SHostGuestResourceUsage, error) { ret := map[string]SHostGuestResourceUsage{} // Query by host_id { guests := GuestManager.Query().In("host_id", hostIds) if len(status) > 0 { guests = guests.Equals("status", status) } sq := guests.SubQuery() q := sq.Query( sqlchemy.COUNT("id").Label("guest_count"), sq.Field("host_id"), sqlchemy.SUM("guest_vcpu_count", sq.Field("vcpu_count")), sqlchemy.SUM("guest_vmem_size", sq.Field("vmem_size")), ).GroupBy(sq.Field("host_id")) stat := []struct { HostId string SHostGuestResourceUsage }{} if err := q.All(&stat); err != nil { return nil, err } for i := range stat { ret[stat[i].HostId] = stat[i].SHostGuestResourceUsage } } // Query by backup_host_id, and attribute usage to backup host { guests := GuestManager.Query().In("backup_host_id", hostIds) if len(status) > 0 { guests = guests.Equals("status", status) } sq := guests.SubQuery() backupHostId := sq.Field("backup_host_id").Label("host_id") q := sq.Query( sqlchemy.COUNT("id").Label("guest_count"), backupHostId, sqlchemy.SUM("guest_vcpu_count", sq.Field("vcpu_count")), sqlchemy.SUM("guest_vmem_size", sq.Field("vmem_size")), ).GroupBy(sq.Field("backup_host_id")) stat := []struct { HostId string SHostGuestResourceUsage }{} if err := q.All(&stat); err != nil { return nil, err } for i := range stat { v := ret[stat[i].HostId] v.GuestCount += stat[i].GuestCount v.GuestVcpuCount += stat[i].GuestVcpuCount v.GuestVmemSize += stat[i].GuestVmemSize ret[stat[i].HostId] = v } } return ret, nil } func fetchHostNics(hostIds []string) (map[string][]*types.SNic, error) { nicQ := NetInterfaceManager.Query().In("baremetal_id", hostIds).SubQuery() wires := WireManager.Query().SubQuery() zones := ZoneManager.Query().SubQuery() hn := HostnetworkManager.Query().SubQuery() networks := NetworkManager.Query().SubQuery() q := nicQ.Query( nicQ.Field("mac"), nicQ.Field("vlan_id"), nicQ.Field("baremetal_id"), nicQ.Field("wire_id"), nicQ.Field("rate"), nicQ.Field("nic_type"), nicQ.Field("index"), nicQ.Field("link_up"), nicQ.Field("bridge"), nicQ.Field("mtu"), wires.Field("name").Label("wire"), wires.Field("bandwidth"), hn.Field("ip_addr"), hn.Field("ip6_addr"), networks.Field("guest_gateway").Label("gateway"), networks.Field("guest_gateway6").Label("gateway6"), networks.Field("guest_dns").Label("dns"), networks.Field("guest_domain").Label("domain"), networks.Field("guest_ntp").Label("ntp"), networks.Field("guest_ip_mask").Label("masklen"), networks.Field("guest_ip6_mask").Label("masklen6"), networks.Field("name").Label("net"), networks.Field("id").Label("net_id"), zones.Field("name").Label("zone"), ) q = q.LeftJoin(wires, sqlchemy.Equals(wires.Field("id"), nicQ.Field("wire_id"))) q = q.LeftJoin(hn, sqlchemy.AND( sqlchemy.Equals(nicQ.Field("baremetal_id"), hn.Field("baremetal_id")), sqlchemy.Equals(nicQ.Field("mac"), hn.Field("mac_addr")), sqlchemy.Equals(nicQ.Field("vlan_id"), hn.Field("vlan_id")), )) q = q.LeftJoin(networks, sqlchemy.Equals(hn.Field("network_id"), networks.Field("id"))) q = q.LeftJoin(zones, sqlchemy.Equals(wires.Field("zone_id"), zones.Field("id"))) nics := []struct { types.SNic BaremetalId string Zone string }{} err := q.All(&nics) if err != nil { return nil, err } ret := map[string][]*types.SNic{} for i := range nics { nic := nics[i] _, ok := ret[nic.BaremetalId] if !ok { ret[nic.BaremetalId] = []*types.SNic{} } if len(nic.Gateway) > 0 && !regutils.MatchIP4Addr(nic.Gateway) { nic.Gateway = "" } if len(nic.Dns) == 0 && len(nic.Zone) > 0 { srvs, _ := auth.GetDNSServers(options.Options.Region, nic.Zone) if len(srvs) > 0 { nic.Dns = strings.Join(srvs, ",") } else { nic.Dns = options.Options.DNSServer } } if len(nic.Domain) == 0 { nic.Domain = options.Options.DNSDomain } if len(nic.Ntp) == 0 && len(nic.Zone) > 0 { srvs, _ := auth.GetNTPServers(options.Options.Region, nic.Zone) if len(srvs) > 0 { nic.Ntp = strings.Join(srvs, ",") } } ret[nic.BaremetalId] = append(ret[nic.BaremetalId], &nic.SNic) } return ret, nil } func fetchHostStorages(hostIds []string) (map[string]*SStorageCapacity, error) { hoststorages := HoststorageManager.Query().In("host_id", hostIds).SubQuery() storageQ := StorageManager.Query().IsTrue("enabled").NotEquals("storage_type", api.STORAGE_BAREMETAL).In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery() diskReadySQ := DiskManager.Query().Equals("status", api.DISK_READY).SubQuery() diskReadyQ := diskReadySQ.Query(sqlchemy.SUM("sum", diskReadySQ.Field("disk_size")).Label("used")).GroupBy(diskReadySQ.Field("storage_id")) readySQ := diskReadyQ.SubQuery() diskWasteSQ := DiskManager.Query().NotEquals("status", api.DISK_READY).SubQuery() diskWasteQ := diskWasteSQ.Query(sqlchemy.SUM("sum", diskWasteSQ.Field("disk_size")).Label("wasted")).GroupBy(diskWasteSQ.Field("storage_id")) wasteSQ := diskWasteQ.SubQuery() q := storageQ.Query( storageQ.Field("id"), storageQ.Field("capacity"), storageQ.Field("reserved"), hoststorages.Field("host_id"), storageQ.Field("cmtbound"), storageQ.Field("actual_capacity_used"), readySQ.Field("used"), wasteSQ.Field("wasted"), ) q = q.Join(hoststorages, sqlchemy.Equals(q.Field("id"), hoststorages.Field("storage_id"))) q = q.LeftJoin(readySQ, sqlchemy.Equals(readySQ.Field("storage_id"), storageQ.Field("id"))) q = q.LeftJoin(wasteSQ, sqlchemy.Equals(wasteSQ.Field("storage_id"), storageQ.Field("id"))) values := []struct { HostId string Capacity int64 Reserved int64 Cmtbound float32 ActualCapacityUsed int64 Used int64 Wasted int64 }{} err := q.All(&values) if err != nil { return nil, err } ret := map[string]*SStorageCapacity{} for i := range values { v := values[i] _, ok := ret[v.HostId] if !ok { ret[v.HostId] = &SStorageCapacity{} } capa := SStorageCapacity{} capa.Capacity = v.Capacity - v.Reserved capa.Used = v.Used capa.Wasted = v.Wasted cmtbound := options.Options.DefaultStorageOvercommitBound if v.Cmtbound > 0 { cmtbound = v.Cmtbound } capa.VCapacity = int64(float32(capa.Capacity) * cmtbound) capa.ActualUsed = v.ActualCapacityUsed ret[v.HostId].Add(capa) } return ret, nil } func fetchHostSchedtags(hostIds []string) (map[string][]api.SchedtagShortDescDetails, error) { schedtags := SchedtagManager.Query().SubQuery() objschedtags := HostschedtagManager.Query().SubQuery() q := schedtags.Query( objschedtags.Field("host_id"), schedtags.Field("id"), schedtags.Field("name"), schedtags.Field("default_strategy").Label("default"), sqlchemy.NewStringField("schedtag").Label("res_name"), ) q = q.Join(objschedtags, sqlchemy.AND(sqlchemy.Equals(objschedtags.Field("schedtag_id"), schedtags.Field("id")), sqlchemy.IsFalse(objschedtags.Field("deleted")))) q = q.Filter(sqlchemy.In(objschedtags.Field("host_id"), hostIds)) tags := []struct { Id string HostId string Name string ResName string Default string }{} err := q.All(&tags) if err != nil { return nil, err } ret := map[string][]api.SchedtagShortDescDetails{} for i := range tags { _, ok := ret[tags[i].HostId] if !ok { ret[tags[i].HostId] = []api.SchedtagShortDescDetails{} } tag := api.SchedtagShortDescDetails{} jsonutils.Update(&tag, tags[i]) ret[tags[i].HostId] = append(ret[tags[i].HostId], tag) } return ret, nil } type sGuestCnt struct { GuestCnt int BackupGuestCnt int RunningGuestCnt int ReadyGuestCnt int OtherGuestCnt int PendingDeletedGuestCnt int NonsystemGuestCnt int } func (manager *SHostManager) FetchGuestCnt(hostIds []string) map[string]*sGuestCnt { ret := map[string]*sGuestCnt{} if len(hostIds) == 0 { return ret } guests := []SGuest{} err := GuestManager.RawQuery().IsFalse("deleted").In("host_id", hostIds).NotEquals("hypervisor", api.HYPERVISOR_POD).All(&guests) if err != nil { log.Errorf("query host %s guests error: %v", hostIds, err) } for _, guest := range guests { _, ok := ret[guest.HostId] if !ok { ret[guest.HostId] = &sGuestCnt{} } if guest.PendingDeleted { ret[guest.HostId].PendingDeletedGuestCnt += 1 continue } ret[guest.HostId].GuestCnt += 1 switch guest.Status { case api.VM_RUNNING: ret[guest.HostId].RunningGuestCnt += 1 case api.VM_READY: ret[guest.HostId].ReadyGuestCnt += 1 default: ret[guest.HostId].OtherGuestCnt += 1 } if !guest.IsSystem { ret[guest.HostId].NonsystemGuestCnt += 1 } } GuestManager.RawQuery().IsFalse("deleted").In("backup_host_id", hostIds).NotEquals("hypervisor", api.HYPERVISOR_POD).All(&guests) for _, guest := range guests { _, ok := ret[guest.BackupHostId] if !ok { ret[guest.BackupHostId] = &sGuestCnt{} } ret[guest.BackupHostId].BackupGuestCnt += 1 } return ret } func (hh *SHost) GetReservedResourceForIsolatedDevice() (int, *api.IsolatedDeviceReservedResourceInput) { if devs := IsolatedDeviceManager.FindByHost(hh.Id); len(devs) == 0 { return -1, nil } else { return len(devs), hh.GetDevsReservedResource(devs) } } func (hh *SHost) GetDevsReservedResource(devs []SIsolatedDevice) *api.IsolatedDeviceReservedResourceInput { reservedCpu, reservedMem, reservedStorage := 0, 0, 0 reservedResourceForGpu := api.IsolatedDeviceReservedResourceInput{ ReservedStorage: &reservedStorage, ReservedMemory: &reservedMem, ReservedCpu: &reservedCpu, } for _, dev := range devs { if !utils.IsInStringArray(dev.DevType, api.VALID_GPU_TYPES) { continue } reservedCpu += dev.ReservedCpu reservedMem += dev.ReservedMemory reservedStorage += dev.ReservedStorage } return &reservedResourceForGpu } func (hh *SHost) GetMetadataHiddenKeys() []string { return []string{} } 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 } var hideCpuTypoInfo = jsonutils.QueryBoolean(query, "hide_cpu_topo_info", false) hostIds := make([]string, len(objs)) hosts := make([]*SHost, len(objs)) for i := range rows { rows[i] = api.HostDetails{ EnabledStatusInfrasResourceBaseDetails: stdRows[i], ManagedResourceInfo: managerRows[i], ZoneResourceInfo: zoneRows[i], } host := objs[i].(*SHost) hostIds[i] = host.Id hosts[i] = host } baremetalServers, err := fetchBaremetalServer(hostIds) if err != nil { log.Errorf("fetchBaremetalServer error: %v", err) return rows } serverIds := []string{} for _, server := range baremetalServers { serverIds = append(serverIds, server.Id) } serverIps := fetchGuestIPs(serverIds, tristate.False) status := "" if options.Options.IgnoreNonrunningGuests { status = api.VM_RUNNING } guestResources, err := fetchHostGuestResource(hostIds, status) if err != nil { log.Errorf("fetchHostGuestResource error: %v", err) return rows } metas := []db.SMetadata{} err = db.Metadata.Query().In("obj_id", hostIds).In("key", []string{api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN, api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN}).All(&metas) if err != nil { log.Errorf("query meta error: %v", err) return rows } downMap, shutdownMap := map[string]bool{}, map[string]bool{} for _, meta := range metas { switch meta.Key { case api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN: downMap[meta.ObjId] = (meta.Value == "enable") case api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN: shutdownMap[meta.ObjId] = (meta.Value == "enable") } } isolatedDevices := IsolatedDeviceManager.FindByHosts(hostIds) isolatedDeviceMap := map[string][]SIsolatedDevice{} for i := range isolatedDevices { _, ok := isolatedDeviceMap[isolatedDevices[i].HostId] if !ok { isolatedDeviceMap[isolatedDevices[i].HostId] = []SIsolatedDevice{} } isolatedDeviceMap[isolatedDevices[i].HostId] = append(isolatedDeviceMap[isolatedDevices[i].HostId], isolatedDevices[i]) } schedtags, err := fetchHostSchedtags(hostIds) if err != nil { log.Errorf("fetchHostSchedtags error: %v", err) // return rows } storages, err := fetchHostStorages(hostIds) if err != nil { log.Errorf("host storages error: %v", err) // return rows } nics, err := fetchHostNics(hostIds) if err != nil { log.Errorf("fetchHostNics error: %v", err) // return rows } guestCnts := manager.FetchGuestCnt(hostIds) hostFiles, err := fetchHostHostFiles(hostIds) if err != nil { log.Errorf("fetchHostHostFiles error: %v", err) } for i := range rows { cnt, ok := guestCnts[hostIds[i]] if ok { rows[i].Guests = cnt.GuestCnt rows[i].RunningGuests = cnt.RunningGuestCnt rows[i].ReadyGuests = cnt.ReadyGuestCnt rows[i].OtherGuests = cnt.OtherGuestCnt rows[i].NonsystemGuests = cnt.NonsystemGuestCnt rows[i].PendingDeletedGuests = cnt.PendingDeletedGuestCnt } if server, ok := baremetalServers[hostIds[i]]; ok { rows[i].ServerId = server.Id rows[i].Server = server.Name rows[i].ServerPendingDeleted = server.PendingDeleted if hosts[i].HostType == api.HOST_TYPE_BAREMETAL && len(serverIps) > 0 { if ips, _ := serverIps[server.Id]; len(ips) > 0 { rows[i].ServerIps = strings.Join(ips, ",") } } } if hosts[i].EnableHealthCheck && hostHealthChecker != nil { rows[i].AllowHealthCheck = true } rows[i].AutoMigrateOnHostDown = downMap[hostIds[i]] rows[i].AutoMigrateOnHostShutdown = shutdownMap[hostIds[i]] if hosts[i].IsBaremetal { rows[i].CanPrepare = true if server := baremetalServers[hostIds[i]]; server != nil && server.Status != api.VM_ADMIN { rows[i].CanPrepare = false if showReason { rows[i].PrepareFailReason = fmt.Sprintf("Cannot prepare baremetal in server status %s", server.Status) } } err := hosts[i].canPrepare() if err != nil && rows[i].CanPrepare { rows[i].CanPrepare = false if showReason { rows[i].PrepareFailReason = err.Error() } } } if !isList { pinnedCpus, _ := hosts[i].GetPinnedCpusetCores(ctx, userCred, nil) if pinnedCpus != nil { rows[i].GuestPinnedCpus = pinnedCpus.ToSlice() } } if usage, ok := guestResources[hostIds[i]]; ok { rows[i].CpuCommit = usage.GuestVcpuCount rows[i].MemCommit = usage.GuestVmemSize totalCpu := hosts[i].GetCpuCount() cpuCommitRate := 0.0 if totalCpu > 0 && usage.GuestVcpuCount > 0 { cpuCommitRate = float64(usage.GuestVcpuCount) * 1.0 / float64(totalCpu) } rows[i].CpuCommitRate = cpuCommitRate totalMem := hosts[i].GetMemSize() memCommitRate := 0.0 if totalMem > 0 && usage.GuestVmemSize > 0 { memCommitRate = float64(usage.GuestVmemSize) * 1.0 / float64(totalMem) } rows[i].MemCommitRate = memCommitRate } if devs, ok := isolatedDeviceMap[hostIds[i]]; ok { rows[i].IsolatedDeviceCount = len(devs) for j := range devs { dev := devs[j] if rows[i].IsolatedDeviceTypeCount == nil { rows[i].IsolatedDeviceTypeCount = make(map[string]int, 0) } if cnt, ok := rows[i].IsolatedDeviceTypeCount[dev.DevType]; ok { rows[i].IsolatedDeviceTypeCount[dev.DevType] = cnt + 1 } else { rows[i].IsolatedDeviceTypeCount[dev.DevType] = 1 } } rows[i].ReservedResourceForGpu = hosts[i].GetDevsReservedResource(devs) } if capa, ok := storages[hostIds[i]]; ok { rows[i].Storage = capa.Capacity rows[i].StorageUsed = capa.Used rows[i].ActualStorageUsed = capa.ActualUsed rows[i].StorageWaste = capa.Wasted rows[i].StorageVirtual = capa.VCapacity rows[i].StorageFree = capa.GetFree() rows[i].StorageCommitRate = capa.GetCommitRate() } rows[i].IsPrepaidRecycle = hosts[i].IsPrepaidRecycle() rows[i].CpuCommitBound = hosts[i].GetCPUOvercommitBound() rows[i].MemCommitBound = hosts[i].GetMemoryOvercommitBound() rows[i].Spec = hosts[i].GetHardwareSpecification() rows[i].Schedtags, _ = schedtags[hostIds[i]] rows[i].NicInfo, _ = nics[hostIds[i]] rows[i].NicCount = len(rows[i].NicInfo) rows[i].HostFiles = hostFiles[hostIds[i]] if hideCpuTypoInfo { sysInfo, ok := hosts[i].SysInfo.(*jsonutils.JSONDict) if ok { sysInfo.Remove("cpu_info") sysInfo.Remove("topology") } delete(rows[i].Metadata, "cpu_info") delete(rows[i].Metadata, "topology") } } return rows } type SInfrasStatusInfo struct { apis.TotalCountBase StatusInfo []apis.StatusStatisticStatusInfo } type SHostTotalCount struct { SInfrasStatusInfo MemoryUsed int64 MemoryTotal int64 CPUUsed int64 CPUTotal int64 } func (manager *SHostManager) CustomizedTotalCount(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, totalQ *sqlchemy.SQuery) (int, jsonutils.JSONObject, error) { results := SHostTotalCount{} totalQ = totalQ.AppendField(sqlchemy.SUM("cpu_total", totalQ.Field("cpu_count"))) totalQ = totalQ.AppendField(sqlchemy.SUM("memory_total", totalQ.Field("mem_size"))) totalQ = totalQ.AppendField(sqlchemy.SUM("memory_used", totalQ.Field("memory_used_mb"))) totalQ = totalQ.AppendField(sqlchemy.CASTInt(sqlchemy.SUM("cpu_used", sqlchemy.MUL("use_cpu", totalQ.Field("cpu_usage_percent"), totalQ.Field("cpu_count"), sqlchemy.NewConstField(0.01))), "cpu_used")) err := totalQ.First(&results) if err != nil && errors.Cause(err) != sql.ErrNoRows { return -1, nil, errors.Wrapf(err, "First") } _, statusInfo, err := manager.SEnabledStatusInfrasResourceBaseManager.CustomizedTotalCount(ctx, userCred, query, totalQ) if err != nil { return -1, nil, errors.Wrapf(err, "virt.CustomizedTotalCount") } statusInfo.Unmarshal(&results.SInfrasStatusInfo) log.Debugf("CustomizedTotalCount %s", jsonutils.Marshal(results)) return results.Count, jsonutils.Marshal(results), nil } func fetchBaremetalServer(hostIds []string) (map[string]*SGuest, error) { guests := []SGuest{} err := GuestManager.Query().In("host_id", hostIds).Equals("hypervisor", api.HOST_TYPE_BAREMETAL).All(&guests) if err != nil { return nil, err } ret := map[string]*SGuest{} for i := range guests { ret[guests[i].HostId] = &guests[i] } return ret, nil } func (hh *SHost) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { retval := jsonutils.NewDict() retval.Set("host_id", jsonutils.NewString(hh.Id)) zone, _ := hh.GetZone() retval.Set("zone", jsonutils.NewString(zone.GetName())) return retval, nil } return jsonutils.NewDict(), nil } func (hh *SHost) GetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { ret, ok := hh.IpmiInfo.(*jsonutils.JSONDict) if !ok { return nil, httperrors.NewNotFoundError("No ipmi information was found for host %s", hh.Name) } password, err := ret.GetString("password") if err != nil { return nil, httperrors.NewNotFoundError("IPMI has no password information") } descryptedPassword, err := utils.DescryptAESBase64(hh.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 (hh *SHost) RequestScanIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential) error { _, err := hh.Request(ctx, userCred, "POST", fmt.Sprintf("/hosts/%s/probe-isolated-devices", hh.Id), mcclient.GetTokenHeaders(userCred), nil) if err != nil { return errors.Wrapf(err, "request host %s probe isolaed devices", hh.Id) } return nil } func (hh *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(hh.ManagerUri, "", method, url, headers, body) return ret, err } func (hh *SHost) GetLocalStoragecache() *SStoragecache { localStorages := hh.GetAttachedLocalStorages() for i := 0; i < len(localStorages); i += 1 { sc := localStorages[i].GetStoragecache() if sc != nil { return sc } } return nil } func (hh *SHost) GetStoragecache() *SStoragecache { localStorages := hh.GetAttachedEnabledHostStorages(nil) for i := 0; i < len(localStorages); i += 1 { sc := localStorages[i].GetStoragecache() if sc != nil { return sc } } return nil } func (hh *SHost) PostCreate( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject, ) { hh.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, hh.Id) if err != nil { log.Errorf("fetchIpmiInfo fail %s", err) return } ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict) if ipmiInfoJson.Length() > 0 { _, err := hh.SaveUpdates(func() error { hh.IpmiInfo = ipmiInfoJson return nil }) if err != nil { log.Errorf("save updates: %v", err) } else if len(ipmiInfo.IpAddr) > 0 { hh.setIpmiIp(userCred, ipmiInfo.IpAddr) } } if len(input.AccessIp) > 0 { hh.setAccessIp(userCred, input.AccessIp) } if len(input.AccessMac) > 0 { hh.setAccessMac(userCred, input.AccessMac) } noProbe := false if input.NoProbe != nil { noProbe = *input.NoProbe } if len(hh.ZoneId) > 0 && hh.HostType == api.HOST_TYPE_BAREMETAL && !noProbe { // ipmiInfo, _ := hh.GetIpmiInfo() if len(ipmiInfo.IpAddr) > 0 { hh.StartBaremetalCreateTask(ctx, userCred, kwargs, "") } } if hh.OvnVersion != "" && hh.OvnMappedIpAddr == "" { HostManager.lockAllocOvnMappedIpAddr(ctx) defer HostManager.unlockAllocOvnMappedIpAddr(ctx) addr, err := HostManager.allocOvnMappedIpAddr(ctx) if err != nil { log.Errorf("host %s(%s): alloc vpc mapped addr: %v", hh.Name, hh.Id, err) } if _, err := db.Update(hh, func() error { hh.OvnMappedIpAddr = addr hh.OvnMappedIp6Addr = api.GenVpcMappedIP6(addr) return nil }); err != nil { log.Errorf("host %s(%s): db update vpc mapped addr: %v", hh.Name, hh.Id, err) } } keys := GetHostQuotaKeysFromCreateInput(ownerId, 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) } hh.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data) notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Obj: hh, Action: notifyclient.ActionCreate, }) } func (hh *SHost) StartBaremetalCreateTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error { if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCreateTask", hh, 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.ZoneId) > 0 { _, input.ZoneResourceInput, err = ValidateZoneResourceInput(ctx, 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.ZoneId, "") 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, api.AddressTypeIPv4) 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.ZoneId if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() { return input, httperrors.NewInputParameterError("IPMI address located in different zone than specified") } input.ZoneId = zoneObj.GetId() // data.Set("zone_id", jsonutils.NewString(zoneObj.GetId())) } if !noProbe || input.NoBMC { 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(ctx, 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(ctx, 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(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), false, []api.TNetworkType{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, api.AddressTypeIPv4) 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.ZoneId // 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, api.AddressTypeIPv4) 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.ZoneId = 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") } name := input.Name if len(name) == 0 { name = input.GenerateName } input.HostnameInput, err = manager.SHostnameResourceBaseManager.ValidateHostname(name, "", input.HostnameInput) if err != nil { return input, err } keys := GetHostQuotaKeysFromCreateInput(ownerId, 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 (hh *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostUpdateInput) (api.HostUpdateInput, error) { // validate Hostname if len(input.Hostname) > 0 { if !regutils.MatchDomainName(input.Hostname) { return input, httperrors.NewInputParameterError("hostname should be a legal domain name") } } var err error input.HostAccessAttributes, err = HostManager.inputUniquenessCheck(input.HostAccessAttributes, hh.ZoneId, hh.Id) if err != nil { return input, errors.Wrap(err, "inputUniquenessCheck") } if hh.IsHugePage() && input.MemCmtbound != nil && *input.MemCmtbound != hh.MemCmtbound { return input, errors.Errorf("host mem is hugepage, cannot update mem_cmtbound") } if input.CpuReserved != nil { info := hh.GetMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_INFO, nil) if len(info) > 0 { return input, errors.Wrap(httperrors.ErrInputParameter, "host cpu has been reserved, cannot update cpu_reserved") } } input.HostSizeAttributes, err = HostManager.ValidateSizeParams(input.HostSizeAttributes) if err != nil { return input, errors.Wrap(err, "ValidateSizeParams") } ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, hh.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() != hh.ZoneId { return input, httperrors.NewInputParameterError("New IPMI address located in another zone!") } } val := jsonutils.NewDict() val.Update(hh.IpmiInfo) val.Update(ipmiInfoJson) input.IpmiInfo = val } input.EnabledStatusInfrasResourceBaseUpdateInput, err = hh.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput) if err != nil { return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.ValidateUpdateData") } if len(input.Name) > 0 { hh.UpdateDnsRecords(false) } if input.EnableNumaAllocate != nil { if hh.HostType != api.HOST_TYPE_CONTAINER { if cnt, err := hh.GetRunningGuestCount(); err != nil { return input, errors.Wrap(err, "GetRunningGuestCount") } else if cnt > 0 { return input, errors.Errorf("Host has running guest, can't enable/disable numa allocate") } } } return input, nil } func (hh *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { hh.SEnabledStatusInfrasResourceBase.PostUpdate(ctx, userCred, query, data) if data.Contains("cpu_cmtbound") || data.Contains("mem_cmtbound") || data.Contains("enable_numa_allocate") { hh.ClearSchedDescCache() } if hh.OvnVersion != "" && hh.OvnMappedIpAddr == "" { HostManager.lockAllocOvnMappedIpAddr(ctx) defer HostManager.unlockAllocOvnMappedIpAddr(ctx) addr, err := HostManager.allocOvnMappedIpAddr(ctx) if err != nil { log.Errorf("host %s(%s): alloc vpc mapped addr: %v", hh.Name, hh.Id, err) return } if _, err := db.Update(hh, func() error { hh.OvnMappedIpAddr = addr hh.OvnMappedIp6Addr = api.GenVpcMappedIP6(addr) return nil }); err != nil { log.Errorf("host %s(%s): db update vpc mapped addr: %v", hh.Name, hh.Id, err) return } } // update baremetal host related server if guest := hh.GetBaremetalServer(); guest != nil && hh.HostType == api.HOST_TYPE_BAREMETAL { if _, err := db.Update(guest, func() error { guest.VmemSize = hh.MemSize guest.VcpuCount = hh.CpuCount return nil }); err != nil { log.Errorf("baremetal host %s update related server %s spec error: %v", hh.GetName(), guest.GetName(), err) } } notSyncConf, _ := data.Bool("not_sync_config") if !notSyncConf { if err := hh.startSyncConfig(ctx, userCred, "", true); err != nil { log.Errorf("start sync host %q config after updated", hh.GetName()) } } } func (hh *SHost) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) { hh.SEnabledStatusInfrasResourceBase.PostDelete(ctx, userCred) notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Obj: hh, Action: notifyclient.ActionDelete, }) } func (hh *SHost) UpdateDnsRecords(isAdd bool) { for _, netif := range hh.GetHostNetInterfaces() { hh.UpdateDnsRecord(&netif, isAdd) } } func (hh *SHost) UpdateDnsRecord(netif *SNetInterface, isAdd bool) { name := hh.GetNetifName(netif) if len(name) == 0 { return } bn := netif.GetHostNetwork() 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 (hh *SHost) GetNetifName(netif *SNetInterface) string { if netif.NicType == api.NIC_TYPE_IPMI { return hh.GetName() } else if netif.NicType == api.NIC_TYPE_ADMIN { return hh.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) && !regutils.MatchIP6Addr(data.IpmiIpAddr) { msg := fmt.Sprintf("ipmi_ip_addr: %v not valid address", data.IpmiIpAddr) log.Errorf("%s", 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 (hh *SHost) PerformStart( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostPerformStartInput, ) (jsonutils.JSONObject, error) { if !hh.IsBaremetal { return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host") } if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY}) { return nil, httperrors.NewInvalidStatusError("Cannot start baremetal with active guest") } guest := hh.GetBaremetalServer() if guest != nil { if hh.HostType == api.HOST_TYPE_BAREMETAL && utils.ToBool(guest.GetMetadata(ctx, "is_fake_baremetal_server", userCred)) { return nil, hh.InitializedGuestStart(ctx, userCred, guest) } // if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) { // return nil, httperrors.NewBadRequestError("Cannot start baremetal with active guest") // } hh.SetStatus(ctx, userCred, api.BAREMETAL_START_MAINTAIN, "") return guest.PerformStart(ctx, userCred, query, api.GuestPerformStartInput{}) } params := jsonutils.NewDict() params.Set("force_reboot", jsonutils.NewBool(false)) params.Set("action", jsonutils.NewString("start")) return hh.PerformMaintenance(ctx, userCred, nil, params) } func (hh *SHost) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if hh.GetEnabled() { return nil, httperrors.NewInvalidStatusError("Host is not disabled") } return nil, hh.purge(ctx, userCred) } func (hh *SHost) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !hh.IsBaremetal { return nil, httperrors.NewBadRequestError("Cannot stop a non-baremetal host") } if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_RUNNING}) { return nil, httperrors.NewInvalidStatusError("Cannot stop baremetal with non-active guest") } guest := hh.GetBaremetalServer() if guest != nil { if hh.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(ctx, "is_fake_baremetal_server", userCred)) { return nil, hh.InitializedGuestStop(ctx, userCred, guest) } hh.SetStatus(ctx, userCred, api.BAREMETAL_START_MAINTAIN, "") input := api.ServerStopInput{} data.Unmarshal(&input) return guest.PerformStop(ctx, userCred, query, input) } } return nil, hh.StartBaremetalUnmaintenanceTask(ctx, userCred, false, "stop") } func (hh *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 } return task.ScheduleRun(nil) } func (hh *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 (hh *SHost) PerformMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { return nil, httperrors.NewInvalidStatusError("Cannot do maintenance in status %s", hh.Status) } guest := hh.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(ctx, userCred, api.VM_ADMIN, "") } if hh.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)) hh.SetStatus(ctx, userCred, api.BAREMETAL_START_MAINTAIN, "") task, err := taskman.TaskManager.NewTask(ctx, "BaremetalMaintenanceTask", hh, userCred, params, "", "", nil) if err != nil { return nil, err } return nil, task.ScheduleRun(nil) } func (hh *SHost) PerformUnmaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_RUNNING, api.BAREMETAL_READY}) { return nil, httperrors.NewInvalidStatusError("Cannot do unmaintenance in status %s", hh.Status) } guest := hh.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 := hh.GetMetadata(ctx, "__maint_guest_running", userCred) var startGuest = false if utils.ToBool(guestRunning) { startGuest = true } return nil, hh.StartBaremetalUnmaintenanceTask(ctx, userCred, startGuest, action) } func (hh *SHost) StartBaremetalUnmaintenanceTask(ctx context.Context, userCred mcclient.TokenCredential, startGuest bool, action string) error { hh.SetStatus(ctx, 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", hh, userCred, params, "", "", nil) if err != nil { return err } task.ScheduleRun(nil) return nil } func (hh *SHost) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { guest := hh.GetBaremetalServer() if guest != nil { return guest.StartSyncstatus(ctx, userCred, parentTaskId) } task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncStatusTask", hh, userCred, nil, parentTaskId, "", nil) if err != nil { return errors.Wrapf(err, "NewTask") } return task.ScheduleRun(nil) } func (hh *SHost) PerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.HostOfflineInput) (jsonutils.JSONObject, error) { if hh.HostStatus != api.HOST_OFFLINE { _, err := hh.SaveUpdates(func() error { hh.HostStatus = api.HOST_OFFLINE if input.UpdateHealthStatus != nil && *input.UpdateHealthStatus { hh.EnableHealthCheck = false } // Note: update host status to unknown on host offline // we did not have host status after host offline hh.Status = api.BAREMETAL_UNKNOWN return nil }) if err != nil { return nil, err } db.OpsLog.LogEvent(hh, db.ACT_OFFLINE, input.Reason, userCred) logclient.AddActionLogWithContext(ctx, hh, logclient.ACT_OFFLINE, input, userCred, true) ndata := jsonutils.Marshal(hh).(*jsonutils.JSONDict) if len(input.Reason) > 0 { ndata.Add(jsonutils.NewString(input.Reason), "reason") } notifyclient.SystemExceptionNotify(ctx, napi.ActionOffline, HostManager.Keyword(), ndata) hh.SyncAttachedStorageStatus(ctx) } return nil, nil } func (hh *SHost) PerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if hh.HostStatus != api.HOST_ONLINE { _, err := hh.SaveUpdates(func() error { hh.LastPingAt = time.Now() hh.HostStatus = api.HOST_ONLINE hh.EnableHealthCheck = true if !hh.IsMaintaining() { hh.Status = api.BAREMETAL_RUNNING } return nil }) if err != nil { return nil, err } if hostHealthChecker != nil { hostHealthChecker.WatchHost(context.Background(), hh.GetHostnameByName()) } db.OpsLog.LogEvent(hh, db.ACT_ONLINE, "", userCred) logclient.AddActionLogWithContext(ctx, hh, logclient.ACT_ONLINE, data, userCred, true) hh.SyncAttachedStorageStatus(ctx) hh.StartUploadAllGuestsStatusTask(ctx, userCred) } return nil, nil } func (hh *SHost) PerformRestartHostAgent( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) (jsonutils.JSONObject, error) { _, err := hh.Request(ctx, userCred, "POST", fmt.Sprintf("/hosts/%s/restart-host-agent", hh.Id), mcclient.GetTokenHeaders(userCred), data) if err != nil { return nil, err } return nil, nil } func (hh *SHost) PerformAutoMigrateOnHostDown( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostAutoMigrateInput, ) (jsonutils.JSONObject, error) { if input.AutoMigrateOnHostShutdown == "enable" && input.AutoMigrateOnHostDown != "enable" { return nil, httperrors.NewBadRequestError("must enable auto_migrate_on_host_down at same time") } var meta = make(map[string]interface{}) if input.AutoMigrateOnHostShutdown == "enable" { meta[api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN] = "enable" } else if input.AutoMigrateOnHostShutdown == "disable" { meta[api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN] = "disable" } data := jsonutils.NewDict() if input.AutoMigrateOnHostDown == "enable" { data.Set("shutdown_servers", jsonutils.JSONTrue) meta[api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN] = "enable" } else if input.AutoMigrateOnHostDown == "disable" { meta[api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN] = "disable" data.Set("shutdown_servers", jsonutils.JSONFalse) } _, err := hh.Request(ctx, userCred, "POST", fmt.Sprintf("/hosts/%s/shutdown-servers-on-host-down", hh.Id), mcclient.GetTokenHeaders(userCred), data) if err != nil { return nil, err } logclient.AddActionLogWithContext(ctx, hh, logclient.ACT_AUTO_MIGRATE_ON_HOST_DOWN, nil, userCred, true) return nil, hh.SetAllMetadata(ctx, meta, userCred) } func (hh *SHost) StartUploadAllGuestsStatusTask(ctx context.Context, userCred mcclient.TokenCredential) error { if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUploadAllGuestsStatusTask", hh, userCred, nil, "", "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (hh *SHost) GetStoragesByMasterHost() ([]string, error) { sq := StorageManager.Query() sq = sq.In("storage_type", api.SHARED_STORAGE) sq = sq.Filter(sqlchemy.OR(sqlchemy.Equals(sq.Field("master_host"), hh.Id), sqlchemy.IsNullOrEmpty(sq.Field("master_host")))) subq := sq.SubQuery() hsq := HoststorageManager.Query().Equals("host_id", hh.Id) hsq = hsq.Join(subq, sqlchemy.Equals(subq.Field("id"), hsq.Field("storage_id"))) hostStorages := make([]SHoststorage, 0) if err := hsq.All(&hostStorages); err != nil && err != sql.ErrNoRows { return nil, errors.Wrap(err, "get hostStorages") } else if err == sql.ErrNoRows { return nil, nil } storages := make([]string, len(hostStorages)) for i := range storages { storages[i] = hostStorages[i].StorageId } return storages, nil } func (hh *SHost) PerformReportDmesg(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SHostReportDmesgInput) (jsonutils.JSONObject, error) { for i := range input.Entries { logLevel := db.LogLevelToString(input.Entries[i].Level) HostDmesgLogManager.LogDmesg(ctx, hh, logLevel, input.Entries[i].Time, input.Entries[i].Message, userCred) } return nil, nil } func (hh *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SHostPingInput) (jsonutils.JSONObject, error) { if hh.HostType == api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewNotSupportedError("ping host type %s not support", hh.HostType) } if input.WithData { // piggyback storage stats info log.Debugf("host ping %#v", input) for _, si := range input.StorageStats { storageObj, err := StorageManager.FetchById(si.StorageId) if err != nil { log.Errorf("fetch storage %s error %s", si.StorageId, err) } else { storage := storageObj.(*SStorage) _, err := db.Update(storage, func() error { storage.Capacity = si.CapacityMb storage.ActualCapacityUsed = si.ActualCapacityUsedMb return nil }) if err != nil { log.Errorf("update storage info error %s", err) } } } if len(hh.ManagerId) == 0 { guests, _ := hh.GetGuests() for _, guest := range guests { if utils.IsInStringArray(guest.Id, input.QgaRunningGuestIds) { if guest.QgaStatus != api.QGA_STATUS_AVAILABLE { guest.UpdateQgaStatus(api.QGA_STATUS_AVAILABLE) } } else { if guest.QgaStatus != api.QGA_STATUS_UNKNOWN { guest.UpdateQgaStatus(api.QGA_STATUS_UNKNOWN) } } } } } hh.SaveUpdates(func() error { if hh.HostStatus == api.HOST_ONLINE { hh.LastPingAt = time.Now() } if input.WithData { if input.RootPartitionUsedCapacityMb > 0 { hh.RootPartitionUsedCapacityMb = input.RootPartitionUsedCapacityMb } if input.MemoryUsedMb > 0 { hh.MemoryUsedMb = input.MemoryUsedMb } if input.CpuUsagePercent > 0 { hh.CpuUsagePercent = input.CpuUsagePercent } } return nil }) if hh.HostStatus != api.HOST_ONLINE { hh.PerformOnline(ctx, userCred, query, nil) } else { if hh.hasUnknownGuests() && len(hh.ManagerId) == 0 { hh.StartUploadAllGuestsStatusTask(ctx, userCred) } } if len(hh.ManagerId) > 0 { return nil, nil } result := jsonutils.NewDict() result.Set("name", jsonutils.NewString(hh.GetName())) dependSvcs := []string{"ntpd", "kafka", apis.SERVICE_TYPE_INFLUXDB, apis.SERVICE_TYPE_VICTORIA_METRICS, "elasticsearch", "opentsdb"} catalog := auth.GetCatalogData(dependSvcs, options.Options.Region) if catalog == nil { return nil, errors.Wrap(errors.ErrServer, "Get catalog error") } result.Set("catalog", catalog) if storages, err := hh.GetStoragesByMasterHost(); err != nil { return nil, errors.Wrap(err, "get storages by master host") } else { result.Set("master_host_storages", jsonutils.NewStringArray(storages)) } hostFiles, err := hh.getHostFiles() if err != nil { return nil, errors.Wrap(err, "get host files") } result.Set("host_files", jsonutils.Marshal(hostFiles)) appParams := appsrv.AppContextGetParams(ctx) if appParams != nil { // skip log&trace, when everything is normal appParams.SkipTrace = true appParams.SkipLog = true } return result, nil } func (host *SHost) getHostNodeReservePercent(reservedCpusStr string) (map[string]float32, error) { reservedCpuset, err := cpuset.Parse(reservedCpusStr) if err != nil { return nil, errors.Wrap(err, "cpuset parse reserved cpus") } topoObj, err := host.SysInfo.Get("topology") if err != nil { return nil, errors.Wrap(err, "get topology from host sys_info") } info := new(hostapi.HostTopology) if err := topoObj.Unmarshal(info); err != nil { return nil, errors.Wrap(err, "Unmarshal host topology struct") } nodecpus := map[int]int{} nodeReservedCpus := map[int]int{} for i := range info.Nodes { cSet := cpuset.NewBuilder() for j := 0; j < len(info.Nodes[i].Cores); j++ { for k := 0; k < len(info.Nodes[i].Cores[j].LogicalProcessors); k++ { if reservedCpuset.Contains(info.Nodes[i].Cores[j].LogicalProcessors[k]) { if cnt, ok := nodeReservedCpus[info.Nodes[i].ID]; !ok { nodeReservedCpus[info.Nodes[i].ID] = 1 } else { nodeReservedCpus[info.Nodes[i].ID] = 1 + cnt } } cSet.Add(info.Nodes[i].Cores[j].LogicalProcessors[k]) } } nodecpus[info.Nodes[i].ID] = cSet.Result().Size() } reserveRate := map[string]float32{} for nodeId, cnt := range nodecpus { reserveCnt, ok := nodeReservedCpus[nodeId] if !ok { reserveCnt = 0 } reserveRate[strconv.Itoa(nodeId)] = float32(reserveCnt) / float32(cnt) } return reserveRate, nil } func (host *SHost) getHostLogicalCores() ([]int, error) { cpuObj, err := host.SysInfo.Get("cpu_info") if err != nil { return nil, errors.Wrap(err, "get cpu info from host sys_info") } cpuInfo := new(hostapi.HostCPUInfo) if err := cpuObj.Unmarshal(cpuInfo); err != nil { return nil, errors.Wrap(err, "Unmarshal host cpu info struct") } // get host logical cores allCores := []int{} if len(cpuInfo.Processors) != 0 { for _, p := range cpuInfo.Processors { for _, core := range p.Cores { allCores = append(allCores, core.LogicalProcessors...) } } sort.Ints(allCores) } else { topoObj, err := host.SysInfo.Get("topology") if err != nil { return nil, errors.Wrap(err, "get topology from host sys_info") } hostTopo := new(hostapi.HostTopology) if err := topoObj.Unmarshal(hostTopo); err != nil { return nil, errors.Wrap(err, "Unmarshal host topology struct") } for _, node := range hostTopo.Nodes { for _, cores := range node.Cores { allCores = append(allCores, cores.LogicalProcessors...) } } } return allCores, nil } func (hh *SHost) PerformUnreserveCpus( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) (jsonutils.JSONObject, error) { return nil, hh.RemoveMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_INFO, userCred) } func (hh *SHost) PerformReserveCpus( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostReserveCpusInput, ) (jsonutils.JSONObject, error) { if !utils.IsInStringArray(hh.HostType, []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_CONTAINER}) { return nil, httperrors.NewNotSupportedError("host type %s not support reserve cpus", hh.HostType) } if input.Cpus == "" { return nil, httperrors.NewInputParameterError("missing cpus") } cs, err := cpuset.Parse(input.Cpus) if err != nil { return nil, httperrors.NewInputParameterError("cpus %s not valid", input.Cpus) } allCores, err := hh.getHostLogicalCores() if err != nil { return nil, err } hSets := sets.NewInt(allCores...) cSlice := cs.ToSlice() if !hSets.HasAll(cSlice...) { return nil, httperrors.NewInputParameterError("Host cores not contains input %v", input.Cpus) } if hSets.Len() == len(cSlice) { return nil, httperrors.NewInputParameterError("Can't reserve host all cpus") } pinnedCores, err := hh.GetPinnedCpusetCores(ctx, userCred, nil) if err != nil { return nil, err } if pinnedCores != nil { if cs.Union(*pinnedCores).Size() != (cs.Size() + pinnedCores.Size()) { return nil, httperrors.NewBadRequestError("request cpus confilct with guest pinned cpus") } } if input.Mems != "" { mems, err := cpuset.Parse(input.Mems) if err != nil { return nil, httperrors.NewInputParameterError("mems %s not valid", input.Mems) } // to slice will sort slice default memSlice := mems.ToSlice() if 0 > memSlice[len(memSlice)-1] || memSlice[len(memSlice)-1] >= int(hh.NodeCount) { return nil, httperrors.NewInputParameterError("mems %s out of range", input.Mems) } } if len(input.Cpus) > 0 { reservePercent, err := hh.getHostNodeReservePercent(input.Cpus) if err != nil { return nil, errors.Errorf("failed getHostNodeReservePercent: %s", err) } err = hh.SetMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_RATE, reservePercent, userCred) if err != nil { return nil, err } } else { err = hh.RemoveMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_RATE, userCred) if err != nil { return nil, err } } err = hh.SetMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_INFO, input, userCred) if err != nil { return nil, err } if err = hh.updateHostReservedCpus(ctx, userCred); err != nil { return nil, errors.Wrap(err, "update host reserved cpus") } return nil, err } func (hh *SHost) HasBMC() bool { ipmiInfo, _ := hh.GetIpmiInfo() if ipmiInfo.Username != "" && ipmiInfo.Password != "" { return true } return false } func (hh *SHost) IsUEFIBoot() bool { info, _ := hh.GetUEFIInfo() if info == nil { return false } if len(info.PxeBootNum) == 0 { return false } return true } func (hh *SHost) isRedfishCapable() bool { ipmiInfo, _ := hh.GetIpmiInfo() if ipmiInfo.Verified && ipmiInfo.RedfishApi { return true } return false } func (hh *SHost) canPrepare() error { if !hh.isRedfishCapable() && len(hh.AccessMac) == 0 && len(hh.Uuid) == 0 { return httperrors.NewInvalidStatusError("need valid access_mac and uuid to do prepare") } if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING, api.BAREMETAL_PREPARE_FAIL}) { return httperrors.NewInvalidStatusError("Cannot prepare baremetal in status %s", hh.Status) } return nil } func (hh *SHost) PerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !hh.IsBaremetal { return nil, httperrors.NewInvalidStatusError("not a baremetal") } server := hh.GetBaremetalServer() if server != nil && server.Status != api.VM_ADMIN { return nil, httperrors.NewInvalidStatusError("Cannot prepare baremetal in server status %s", server.Status) } err := hh.canPrepare() if err != nil { return nil, err } var onfinish string if server != nil && hh.Status == api.BAREMETAL_READY { onfinish = "shutdown" } return nil, hh.StartPrepareTask(ctx, userCred, onfinish, "") } func (hh *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)) } hh.SetStatus(ctx, userCred, api.BAREMETAL_PREPARE, "start prepare task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalPrepareTask", hh, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (hh *SHost) PerformIpmiProbe(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_READY, api.BAREMETAL_RUNNING, api.BAREMETAL_PROBE_FAIL, api.BAREMETAL_UNKNOWN}) { return nil, hh.StartIpmiProbeTask(ctx, userCred, "") } return nil, httperrors.NewInvalidStatusError("Cannot do Ipmi-probe in status %s", hh.Status) } func (hh *SHost) StartIpmiProbeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { data := jsonutils.NewDict() hh.SetStatus(ctx, userCred, api.BAREMETAL_START_PROBE, "start ipmi-probe task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalIpmiProbeTask", hh, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (hm *SHostManager) PerformValidateIpmi(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *baremetalapi.ValidateIPMIRequest) (*baremetalapi.ValidateIPMIResponse, error) { resp, err := hm.BaremetalSyncRequest(ctx, "POST", "/baremetals/validate-ipmi", mcclient.GetTokenHeaders(userCred), jsonutils.Marshal(input).(*jsonutils.JSONDict), "") if err != nil { return nil, err } out := new(baremetalapi.ValidateIPMIResponse) resp.Unmarshal(out) return out, nil } func (hh *SHost) PerformInitialize( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ) (jsonutils.JSONObject, error) { if !utils.IsInStringArray( hh.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_PREPARE_FAIL}) { return nil, httperrors.NewBadRequestError( "Cannot do initialization in status %s", hh.Status) } name, err := data.GetString("name") if err != nil || hh.GetBaremetalServer() != nil { return nil, nil } err = db.NewNameValidator(ctx, GuestManager, userCred, name, nil) if err != nil { return nil, err } if hh.IpmiInfo == nil || !hh.IpmiInfo.Contains("ip_addr") || !hh.IpmiInfo.Contains("password") { return nil, httperrors.NewBadRequestError("IPMI infomation not configured") } guest := &SGuest{} guest.Name = name guest.VmemSize = hh.MemSize guest.VcpuCount = hh.CpuCount guest.DisableDelete = tristate.True guest.Hypervisor = api.HYPERVISOR_BAREMETAL guest.HostId = hh.Id guest.ProjectId = userCred.GetProjectId() guest.DomainId = userCred.GetProjectDomainId() guest.Status = api.VM_RUNNING guest.PowerStates = api.VM_POWER_STATES_ON guest.OsType = "Linux" guest.SetModelManager(GuestManager, guest) err = GuestManager.TableSpec().Insert(ctx, 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": hh.AccessIp}, userCred) caps := hh.GetAttachedLocalStorageCapacity() diskConfig := &api.DiskConfig{SizeMb: int(caps.GetFree())} err = guest.CreateDisksOnHost(ctx, userCred, hh, []*api.DiskConfig{diskConfig}, nil, true, true, nil, nil, true) if err != nil { log.Errorf("Host perform initialize failed on create disk %s", err) } net, err := hh.getNetworkOfIPOnHost(ctx, hh.AccessIp) if err != nil { log.Errorf("host perfrom initialize failed fetch net of access ip %s", err) } else { if options.Options.BaremetalServerReuseHostIp { _, err = guest.attach2NetworkDesc(ctx, userCred, hh, &api.NetworkConfig{Network: net.Id}, nil, nil) if err != nil { log.Errorf("host perform initialize failed on attach network %s", err) } } } return nil, nil } func validateHostNetif(input api.HostNetifInput) (api.HostNetifInput, error) { mac := input.Mac if len(input.Mac) > 0 { mac = netutils.FormatMacAddr(input.Mac) } if len(mac) == 0 { return input, errors.Wrapf(httperrors.ErrInputParameter, "Invaild mac address %s", input.Mac) } input.Mac = mac vlan := input.VlanId if vlan == 0 { vlan = 1 } if vlan < 0 || vlan > 4095 { return input, errors.Wrapf(httperrors.ErrInputParameter, "Invalid vlan_id %d", input.VlanId) } input.VlanId = vlan return input, nil } func (h *SHost) PerformAddNetif( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostAddNetifInput, ) (jsonutils.JSONObject, error) { log.Debugf("add_netif %s", jsonutils.Marshal(input)) var err error input.HostNetifInput, err = validateHostNetif(input.HostNetifInput) if err != nil { return nil, errors.Wrap(err, "validateHostNetif") } mac := input.Mac vlan := input.VlanId wireId := input.WireId if len(input.WireId) > 0 { wireObj, err := WireManager.FetchByIdOrName(ctx, userCred, input.WireId) if err != nil { if errors.Cause(err) == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError2(WireManager.Keyword(), input.WireId) } else { return nil, errors.Wrap(err, "FetchByIdOrName") } } wireId = wireObj.GetId() } ipAddr := input.IpAddr if len(ipAddr) > 0 && !regutils.MatchIP4Addr(ipAddr) { return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid ip_addr %s", ipAddr) } ip6Addr := input.Ip6Addr if len(ip6Addr) > 0 && !regutils.MatchIP6Addr(ip6Addr) { return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid ip6_addr %s", ip6Addr) } rate := input.Rate nicType := input.NicType index := input.Index linkUp := input.LinkUp mtu := input.Mtu reset := (input.Reset != nil && *input.Reset) netIf := input.Interface bridge := input.Bridge reserve := (input.Reserve != nil && *input.Reserve) requireDesignatedIp := (input.RequireDesignatedIp != nil && *input.RequireDesignatedIp) requireIpv6 := (input.RequireIpv6 != nil && *input.RequireIpv6) strictIpv6 := (input.StrictIpv6 != nil && *input.StrictIpv6) isLinkUp := tristate.None if linkUp != "" { if utils.ToBool(linkUp) { isLinkUp = tristate.True } else { isLinkUp = tristate.False } } err = h.addNetif(ctx, userCred, mac, vlan, wireId, ipAddr, ip6Addr, int(rate), nicType, index, isLinkUp, int16(mtu), reset, netIf, bridge, reserve, requireDesignatedIp, requireIpv6, strictIpv6) return nil, errors.Wrap(err, "addNetif") } func (h *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential, mac string, vlanId int, wireId string, ipAddr string, ip6Addr string, rate int, nicType compute.TNicType, index int, linkUp tristate.TriState, mtu int16, reset bool, strInterface *string, strBridge *string, reserve bool, requireDesignatedIp bool, requireIpv6 bool, strictIpv6 bool, ) error { var sw *SWire if len(wireId) > 0 { iWire, err := WireManager.FetchById(wireId) if err != nil { if err == sql.ErrNoRows { return httperrors.NewResourceNotFoundError2(WireManager.Keyword(), wireId) } else { return httperrors.NewInternalServerError("find Wire %s error: %s", wireId, err) } } sw = iWire.(*SWire) if len(ipAddr) > 0 || len(ip6Addr) > 0 { var v4addr *netutils.IPV4Addr var v6addr *netutils.IPV6Addr if len(ipAddr) > 0 { iIpAddr, err := netutils.NewIPV4Addr(ipAddr) if err != nil { return httperrors.NewInputParameterError("invalid ipaddr %s", ipAddr) } v4addr = &iIpAddr } if len(ip6Addr) > 0 { iIp6Addr, err := netutils.NewIPV6Addr(ip6Addr) if err != nil { return httperrors.NewInputParameterError("invalid ip6addr %s", ip6Addr) } v6addr = &iIp6Addr } var v4net, v6net *SNetwork swNets, err := sw.getNetworks(ctx, userCred, userCred, NetworkManager.AllowScope(userCred)) if err != nil { return httperrors.NewInputParameterError("no networks on wire %s", wireId) } for i := range swNets { if v4net == nil && v4addr != nil && swNets[i].IsAddressInRange(*v4addr) { v4net = &swNets[i] } if v6net == nil && v6addr != nil && swNets[i].IsAddress6InRange(*v6addr) { v6net = &swNets[i] } if v4net != nil && v6net != nil { break } } if v4net == nil && v6net == nil { var addrs []string if len(ipAddr) > 0 { addrs = append(addrs, ipAddr) } if len(ip6Addr) > 0 { addrs = append(addrs, ip6Addr) } return httperrors.NewBadRequestError("IP %s not attach to wire %s", strings.Join(addrs, ","), wireId) } if v4net != nil && v6net != nil && v4net.Id != v6net.Id { return httperrors.NewConflictError("IPv4 %s and IPv6 %s must be on the same network", ipAddr, ip6Addr) } } } else { var v4wire, v6wire *SWire if len(ipAddr) > 0 { ipWire, err := WireManager.GetOnPremiseWireOfIp(ipAddr) if err != nil { return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) } v4wire = ipWire } if len(ip6Addr) > 0 { ipWire, err := WireManager.GetOnPremiseWireOfIp6(ip6Addr) if err != nil { return httperrors.NewBadRequestError("IPv6 %s not attach to any wire", ip6Addr) } v6wire = ipWire } if v4wire != nil && v6wire != nil && v4wire.Id != v6wire.Id { return httperrors.NewConflictError("IPv4 %s and IPv6 %s must be on the same wire", ipAddr, ip6Addr) } if v4wire != nil { sw = v4wire } else if v6wire != nil { sw = v6wire } } netif, err := NetInterfaceManager.FetchByMacVlan(mac, vlanId) if err != nil { if errors.Cause(err) != sql.ErrNoRows { return httperrors.NewInternalServerError("fail to fetch netif by mac %s: %s", mac, err) } // else not found netif = &SNetInterface{} netif.SetModelManager(NetInterfaceManager, netif) netif.Mac = mac netif.VlanId = vlanId } var changed bool if netif.BaremetalId != h.Id { if len(netif.BaremetalId) > 0 { changed = true // previously conencted to another host } netif.BaremetalId = h.Id } if sw != nil && netif.WireId != sw.Id { if len(netif.WireId) > 0 { changed = true } netif.WireId = sw.Id } else if netif.WireId != "" && sw == nil { changed = true netif.WireId = "" } if rate > 0 && rate != netif.Rate { netif.Rate = rate } if nicType != "" && nicType != netif.NicType { netif.NicType = nicType } if index >= 0 { netif.Index = index } if !linkUp.IsNone() && linkUp.Bool() != netif.LinkUp { netif.LinkUp = linkUp.Bool() } if mtu > 0 && mtu != netif.Mtu { netif.Mtu = mtu } if strInterface != nil { netif.Interface = *strInterface } if strBridge != nil { netif.Bridge = *strBridge } // ensure index is unique on host { ifs := h.GetHostNetInterfaces() dupIdx := false var maxIdx int for i := range ifs { if ifs[i].Mac == netif.Mac && ifs[i].VlanId == netif.VlanId { // find self, skip continue } if netif.Index == ifs[i].Index { // duplicate nic index dupIdx = true } if maxIdx < ifs[i].Index { maxIdx = ifs[i].Index } } if dupIdx { netif.Index = maxIdx + 1 } } err = NetInterfaceManager.TableSpec().InsertOrUpdate(ctx, netif) if err != nil { return errors.Wrap(err, "InsertOrUpdate") } if changed || reset { h.DisableNetif(ctx, userCred, netif, false) } if netif.NicType == api.NIC_TYPE_ADMIN { oldadmins := h.GetAdminNetInterfaces() for i := range oldadmins { oldNetif := oldadmins[i] if oldNetif.Mac != netif.Mac || oldNetif.VlanId != netif.VlanId { // make normal netif err := oldNetif.setNicType(api.NIC_TYPE_NORMAL) if err != nil { return errors.Wrapf(err, "setNicType %s", oldNetif.String()) } } } err := h.setAccessMac(userCred, netif.Mac) if err != nil { return errors.Wrap(err, "setAccessMac") } // inherit wire's class metadata sw = netif.GetWire() if sw != nil { err := db.InheritFromTo(ctx, userCred, sw, h) if err != nil { return errors.Wrapf(err, "unable to inherit class metadata from sw %s", sw.GetName()) } } } if len(ipAddr) > 0 || len(ip6Addr) > 0 { err = h.EnableNetif(ctx, userCred, netif, "", ipAddr, ip6Addr, "", "", reserve, requireDesignatedIp, requireIpv6, strictIpv6) if err != nil { return httperrors.NewBadRequestError("%v", err) } } h.ClearSchedDescCache() return nil } func (h *SHost) PerformEnableNetif( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostEnableNetifInput, ) (jsonutils.JSONObject, error) { log.Debugf("enable_netif %s", jsonutils.Marshal(input)) var err error input.HostNetifInput, err = validateHostNetif(input.HostNetifInput) if err != nil { return nil, errors.Wrap(err, "validateHostNetif") } netif := h.GetNetInterface(input.Mac, input.VlanId) if netif == nil { return nil, httperrors.NewBadRequestError("Interface %s(vlan:%d) not exist", input.Mac, input.VlanId) } // if netif.NicType ! !utils.IsInArray(netif.NicType, api.NIC_TYPES) { // return nil, httperrors.NewBadRequestError("Only ADMIN and IPMI nic can be enable") // } reserve := (input.Reserve != nil && *input.Reserve) requireDesignatedIp := (input.RequireDesignatedIp != nil && *input.RequireDesignatedIp) requireIpv6 := (input.RequireIpv6 != nil && *input.RequireIpv6) strictIpv6 := (input.StrictIpv6 != nil && *input.StrictIpv6) err = h.EnableNetif(ctx, userCred, netif, input.NetworkId, input.IpAddr, input.Ip6Addr, input.AllocDir, input.NetType, reserve, requireDesignatedIp, requireIpv6, strictIpv6) if err != nil { return nil, httperrors.NewBadRequestError("%v", err) } return nil, nil } func (h *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, network, ipAddr, ip6Addr, allocDir string, netType api.TNetworkType, reserve, requireDesignatedIp bool, requireIpv6 bool, strictIpv6 bool) error { // bn := netif.GetHostNetwork() // if bn != nil { // log.Debugf("Netif has been attach2network? %s", jsonutils.Marshal(bn)) // return nil // } var v4net, v6net *SNetwork var err error if len(ipAddr) > 0 && !strictIpv6 { net, err := netif.GetCandidateNetworkForIp(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), ipAddr) if net != nil { log.Infof("find network %s for ip4 %s", net.GetName(), ipAddr) v4net = net } else if requireDesignatedIp { log.Errorf("Cannot allocate IP %s, not reachable: %s", ipAddr, err) return fmt.Errorf("Cannot allocate IP %s, not reachable: %s", ipAddr, err) } else { log.Infof("not found network with scope: %s, ip_addr: %s, err: %s", NetworkManager.AllowScope(userCred), ipAddr, err) // the ipaddr is not usable, should be reset to empty ipAddr = "" } } if len(ip6Addr) > 0 { if v4net != nil { ip6, err := netutils.NewIPV6Addr(ip6Addr) if err != nil { return errors.Wrapf(err, "netutils.NewIPV6Addr: %s", ip6Addr) } if v4net.IsAddress6InRange(ip6) { v6net = v4net } } else { net, err := netif.GetCandidateNetworkForIp6(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), ip6Addr) if net != nil { log.Infof("find network %s for ip %s", net.GetName(), ip6Addr) v6net = net } else if requireIpv6 { log.Errorf("Cannot allocate IPv6 %s, not reachable: %s", ip6Addr, err) return fmt.Errorf("Cannot allocate IPv6 %s, not reachable: %s", ip6Addr, err) } else { log.Infof("not found network with scope: %s, ip6_addr: %s, err: %s", NetworkManager.AllowScope(userCred), ip6Addr, err) // the ipaddr is not usable, should be reset to empty ip6Addr = "" } } } var net *SNetwork if v4net != nil && v6net != nil { if v4net.Id != v6net.Id { return errors.Wrap(httperrors.ErrConflict, "v4net and v6net must be on the same network") } net = v4net } else if v6net != nil { net = v6net } else if v4net != nil { net = v4net } wire := netif.GetWire() if wire == nil { return fmt.Errorf("No wire attached") } if h.ZoneId == "" { if _, err := h.SaveUpdates(func() error { h.ZoneId = wire.ZoneId return nil }); err != nil { return errors.Wrapf(err, "set host zone_id %s by wire", wire.ZoneId) } } if net == nil { if len(network) > 0 { iNet, err := NetworkManager.FetchByIdOrName(ctx, 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 []api.TNetworkType if len(netType) > 0 && netType != api.NETWORK_TYPE_BAREMETAL { netTypes = []api.TNetworkType{netType, api.NETWORK_TYPE_BAREMETAL} } else { netTypes = []api.TNetworkType{api.NETWORK_TYPE_BAREMETAL} } net, err = wire.GetCandidatePrivateNetwork(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), false, netTypes) if err != nil { return fmt.Errorf("fail to find private network %s", err) } if net == nil { net, err = wire.GetCandidateAutoAllocNetwork(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), false, netTypes) if err != nil { return fmt.Errorf("fail to find public network %s", err) } if net == nil { return fmt.Errorf("No auto_alloc network found of wire: %s(%s), netTypes: %v", wire.GetName(), wire.GetId(), netTypes) } } } } else if net.WireId != wire.Id { return fmt.Errorf("conflict??? candiate net is not on wire") } attachOpt := &hostAttachNetworkOption{ netif: netif, net: net, ipAddr: ipAddr, allocDir: allocDir, reserved: reserve, requireDesignatedIp: requireDesignatedIp, ip6Addr: ip6Addr, requireIpv6: requireIpv6, strictIpv6: strictIpv6, } bn, err := h.attach2Network(ctx, userCred, attachOpt) if err != nil { return errors.Wrap(err, "hh.Attach2Network") } bnIP := bn.IpAddr if len(bnIP) == 0 { bnIP = bn.Ip6Addr } switch netif.NicType { case api.NIC_TYPE_IPMI: err = h.setIpmiIp(userCred, bnIP) if err != nil { return errors.Wrap(err, "setIpmiIp") } case api.NIC_TYPE_ADMIN: err = h.setAccessIp(userCred, bnIP) if err != nil { return errors.Wrap(err, "setAccessIp") } } return nil } func (hh *SHost) PerformDisableNetif( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostDisableNetifInput, ) (jsonutils.JSONObject, error) { var err error input.HostNetifInput, err = validateHostNetif(input.HostNetifInput) if err != nil { return nil, errors.Wrap(err, "validateHostNetif") } netif := hh.GetNetInterface(input.Mac, input.VlanId) if netif == nil { return nil, httperrors.NewBadRequestError("Interface %s(vlan:%d) not exists", input.Mac, input.VlanId) } reserve := (input.Reserve != nil && *input.Reserve) err = hh.DisableNetif(ctx, userCred, netif, reserve) if err != nil { return nil, httperrors.NewBadRequestError("%v", err) } return nil, nil } /* * Disable a net interface, remove IP address if assigned */ func (hh *SHost) DisableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error { bn := netif.GetHostNetwork() var ipAddr, ip6Addr string if bn != nil { ipAddr = bn.IpAddr ip6Addr = bn.Ip6Addr hh.UpdateDnsRecord(netif, false) hh.DeleteBaremetalnetwork(ctx, userCred, bn, reserve) } var err error switch netif.NicType { case api.NIC_TYPE_IPMI: if ipAddr == hh.IpmiIp || ip6Addr == hh.IpmiIp { err = hh.setIpmiIp(userCred, "") } case api.NIC_TYPE_ADMIN: if ipAddr == hh.AccessIp || ip6Addr == hh.AccessIp { err = hh.setAccessIp(userCred, "") } } return err } type hostAttachNetworkOption struct { netif *SNetInterface net *SNetwork ipAddr string allocDir string reserved bool requireDesignatedIp bool ip6Addr string requireIpv6 bool strictIpv6 bool } func (hh *SHost) IsIpAddrWithinConvertedGuest(ctx context.Context, userCred mcclient.TokenCredential, ipAddr, ip6Addr string, netif *SNetInterface) error { if !hh.IsBaremetal { return httperrors.NewNotAcceptableError("Not a baremetal") } // ? // if hh.HostType == api.HOST_TYPE_KVM { // return httperrors.NewNotAcceptableError("Not being convert to hypervisor") // } bmServer := hh.GetBaremetalServer() if bmServer == nil { return httperrors.NewNotAcceptableError("Not found baremetal server record") } guestNics, err := bmServer.GetNetworks("") if err != nil { return errors.Wrap(err, "Get guest networks") } var findNic *SGuestnetwork for idx := range guestNics { nic := guestNics[idx] if nic.MacAddr == netif.Mac { findNic = &nic break } } if findNic == nil { return httperrors.NewNotFoundError("Not found guest nic by mac %s", netif.Mac) } if len(ipAddr) > 0 && findNic.IpAddr != ipAddr { return httperrors.NewNotAcceptableError("Guest nic ip addr %s not equal %s", findNic.IpAddr, ipAddr) } if len(ip6Addr) > 0 && findNic.Ip6Addr != ip6Addr { return httperrors.NewNotAcceptableError("Guest nic ip addr6 %s not equal %s", findNic.Ip6Addr, ip6Addr) } return nil } func (hh *SHost) attach2Network( ctx context.Context, userCred mcclient.TokenCredential, opt *hostAttachNetworkOption, ) (*SHostnetwork, error) { log.Infof("host attach2Network: %s", jsonutils.Marshal(opt)) netif := opt.netif net := opt.net ipAddr := opt.ipAddr ip6Addr := opt.ip6Addr allocDir := opt.allocDir reserved := opt.reserved requireDesignatedIp := opt.requireDesignatedIp bn := opt.netif.GetHostNetwork() lockman.LockObject(ctx, net) defer lockman.ReleaseObject(ctx, net) var freeIp4, freeIp6 string if (!opt.strictIpv6 || len(ipAddr) > 0) && (bn == nil || bn.IpAddr != ipAddr) && net.HasIPv4Addr() { // allocate ipv4 address usedAddrs := net.GetUsedAddresses(ctx) if ipAddr != "" { // converted baremetal can resuse related guest network ip if err := hh.IsIpAddrWithinConvertedGuest(ctx, userCred, ipAddr, "", netif); err == nil { // force remove used server addr for reuse delete(usedAddrs, ipAddr) } else { log.Warningf("check IsIpAddrWithinConvertedGuest: %v", err) } } freeIp, err := net.GetFreeIP(ctx, userCred, usedAddrs, nil, ipAddr, api.IPAllocationDirection(allocDir), reserved, api.AddressTypeIPv4) if err != nil { return nil, errors.Wrap(err, "net.GetFreeIPv4") } if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp { return nil, fmt.Errorf("IPv4 address %s is occupied, get %s instead", ipAddr, freeIp) } freeIp4 = freeIp } if (opt.requireIpv6 || len(ip6Addr) > 0) && (bn == nil || bn.Ip6Addr != ip6Addr) && net.HasIPv6Addr() { usedAddrs6 := net.GetUsedAddresses6(ctx) if ip6Addr != "" { // converted baremetal can resuse related guest network ip if err := hh.IsIpAddrWithinConvertedGuest(ctx, userCred, "", ip6Addr, netif); err == nil { // force remove used server addr for reuse delete(usedAddrs6, ip6Addr) } else { log.Warningf("check IsIpAddrWithinConvertedGuest: %v", err) } } freeIp, err := net.GetFreeIP(ctx, userCred, usedAddrs6, nil, ip6Addr, api.IPAllocationDirection(allocDir), reserved, api.AddressTypeIPv6) if err != nil { return nil, errors.Wrap(err, "net.GetFreeIPv6") } if len(ip6Addr) > 0 && ip6Addr != freeIp && requireDesignatedIp { return nil, fmt.Errorf("IPv6 address %s is occupied, get %s instead", ip6Addr, freeIp) } freeIp6 = freeIp } if bn == nil { bn = &SHostnetwork{} bn.SetModelManager(HostnetworkManager, bn) bn.BaremetalId = hh.Id bn.NetworkId = net.Id bn.MacAddr = netif.Mac bn.VlanId = netif.VlanId bn.IpAddr = freeIp4 bn.Ip6Addr = freeIp6 err := HostnetworkManager.TableSpec().Insert(ctx, bn) if err != nil { return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert") } } else if (freeIp4 != "" && freeIp4 != bn.IpAddr) || (freeIp6 != "" && freeIp6 != bn.Ip6Addr) { _, err := db.Update(bn, func() error { if freeIp4 != "" { bn.IpAddr = freeIp4 } if freeIp6 != "" { bn.Ip6Addr = freeIp6 } return nil }) if err != nil { return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert") } } else { return bn, nil } var addrs []string if len(freeIp4) > 0 { addrs = append(addrs, freeIp4) } if len(freeIp6) > 0 { addrs = append(addrs, freeIp6) } db.OpsLog.LogAttachEvent(ctx, hh, net, userCred, jsonutils.NewString(strings.Join(addrs, ","))) hh.UpdateDnsRecord(netif, true) // net.UpdateBaremetalNetmap(bn, hh.GetNetifName(netif)) return bn, nil } func (hh *SHost) PerformRemoveNetif( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostRemoveNetifInput, ) (jsonutils.JSONObject, error) { var err error input.HostNetifInput, err = validateHostNetif(input.HostNetifInput) if err != nil { return nil, errors.Wrap(err, "validateHostNetif") } netif := hh.GetNetInterface(input.Mac, input.VlanId) if netif == nil { return nil, httperrors.NewBadRequestError("Interface %s(vlan:%d) not exists", input.Mac, input.VlanId) } reserve := (input.Reserve != nil && *input.Reserve) return nil, hh.RemoveNetif(ctx, userCred, netif, reserve) } func (h *SHost) RemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error { h.DisableNetif(ctx, userCred, netif, reserve) // is this a converted host? if h.HostType == api.HOST_TYPE_HYPERVISOR && h.IsBaremetal { guests, err := h.GetGuests() if err != nil { return errors.Wrap(err, "GetGuests") } for i := range guests { guest := &guests[i] if guest.Hypervisor == api.HYPERVISOR_BAREMETAL { gn, err := guest.GetGuestnetworkByMac(netif.Mac) if err != nil && errors.Cause(err) != sql.ErrNoRows { return errors.Wrap(err, "GetGuestnetworkByMac") } else if gn != nil { err = gn.Detach(ctx, userCred) if err != nil { return errors.Wrap(err, "detach guest nic") } } } } } if netif.NicType == api.NIC_TYPE_ADMIN && h.AccessMac == netif.Mac { err := h.setAccessMac(userCred, "") if err != nil { return errors.Wrap(err, "setAccessMac") } } err := netif.Delete(ctx, userCred) if err != nil { return errors.Wrap(err, "netif.Remove") } h.ClearSchedDescCache() return nil } func (hh *SHost) getNetifsOnWire(wireId string) []SNetInterface { return hh.getNetInterfacesInternal(wireId, api.HOST_NIC_TYPES) } func (hh *SHost) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if hh.HostType != api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewBadRequestError("Cannot sync status a non-baremetal host") } hh.SetStatus(ctx, userCred, api.BAREMETAL_SYNCING_STATUS, "") return nil, hh.StartSyncstatus(ctx, userCred, "") } func (hh *SHost) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !hh.IsBaremetal { return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host") } if hh.Status != api.BAREMETAL_RUNNING { return nil, httperrors.NewBadRequestError("Cannot reset baremetal in status %s", hh.Status) } guest := hh.GetBaremetalServer() if guest != nil { if hh.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 hh.PerformMaintenance(ctx, userCred, query, kwargs) } func (hh *SHost) PerformRemoveAllNetifs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { netifs := hh.GetAllNetInterfaces() for i := 0; i < len(netifs); i++ { if netifs[i].NicType == api.NIC_TYPE_NORMAL { hh.RemoveNetif(ctx, userCred, &netifs[i], false) } } return nil, nil } func (hh *SHost) PerformEnable( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput, ) (jsonutils.JSONObject, error) { if !hh.GetEnabled() { _, err := hh.SEnabledStatusInfrasResourceBase.PerformEnable(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformEnable") } hh.SyncAttachedStorageStatus(ctx) hh.updateNotify(ctx, userCred) } return nil, nil } func (hh *SHost) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) { if hh.GetEnabled() { _, err := hh.SEnabledStatusInfrasResourceBase.PerformDisable(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformDisable") } hh.SyncAttachedStorageStatus(ctx) hh.updateNotify(ctx, userCred) } return nil, nil } func (hh *SHost) PerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CacheImageInput) (jsonutils.JSONObject, error) { if hh.HostType == api.HOST_TYPE_BAREMETAL || hh.HostStatus != api.HOST_ONLINE { return nil, httperrors.NewInvalidStatusError("Cannot perform cache image in status %s", hh.Status) } if len(input.ImageId) == 0 { return nil, httperrors.NewMissingParameterError("image_id") } img, err := CachedimageManager.getImageInfo(ctx, userCred, input.ImageId, false) if err != nil { return nil, httperrors.NewNotFoundError("image %s not found", input.ImageId) } input.ImageId = img.Id if len(img.Checksum) != 0 && regutils.MatchUUID(img.Checksum) { return nil, httperrors.NewInvalidStatusError("Cannot cache image with no checksum") } return nil, hh.StartImageCacheTask(ctx, userCred, input) } func (hh *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, input api.CacheImageInput) error { var sc *SStoragecache switch hh.HostType { case api.HOST_TYPE_BAREMETAL: case api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_ESXI: sc = hh.GetLocalStoragecache() default: sc = hh.GetStoragecache() } if sc == nil { return errors.Wrap(errors.ErrNotSupported, "No associate storage cache found") } return sc.StartImageCacheTask(ctx, userCred, input) } func (hh *SHost) isAlterNameUnique(name string) (bool, error) { q := HostManager.Query().Equals("name", name).NotEquals("id", hh.Id).Equals("zone_id", hh.ZoneId) cnt, err := q.CountWithError() if err != nil { return false, err } return cnt == 0, nil } func (hh *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 hh.HostType != api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewNotAcceptableError("Must be a baremetal host") } if hh.GetBaremetalServer() != nil { return nil, httperrors.NewNotAcceptableError("Baremetal host is aleady occupied") } if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { return nil, httperrors.NewNotAcceptableError("Connot convert hypervisor in status %s", hh.Status) } // check ownership var ownerId mcclient.IIdentityProvider hostOwnerId := hh.GetOwnerId() if userCred.GetProjectDomainId() != hostOwnerId.GetProjectDomainId() { if !db.IsAdminAllowPerform(ctx, userCred, hh, "convert-hypervisor") { return nil, httperrors.NewNotSufficientPrivilegeError("require system previleges to convert host in other domain") } firstProject, err := db.TenantCacheManager.FindFirstProjectOfDomain(ctx, hostOwnerId.GetProjectDomainId()) if err != nil { return nil, errors.Wrap(err, "FindFirstProjectOfDomain") } ownerId = firstProject } else { ownerId = userCred } driver, err := GetHostDriver(hostType, api.CLOUD_PROVIDER_ONECLOUD) if err != nil { return nil, errors.Wrapf(err, "GetHostDriver") } if data.Contains("name") { name, _ := data.GetString("name") err := hh.GetModelManager().ValidateName(name) if err != nil { return nil, err } uniq, err := hh.isAlterNameUnique(name) if err != nil { return nil, httperrors.NewInternalServerError("isAlterNameUnique fail %s", err) } if !uniq { return nil, httperrors.NewDuplicateNameError(name, hh.Id) } } image, _ := data.GetString("image") raid, _ := data.GetString("raid") input, err := driver.PrepareConvert(hh, image, raid, data) if err != nil { return nil, httperrors.NewNotAcceptableError("Convert error: %s", err.Error()) } // admin delegate user to create system resource input.ProjectDomainId = ownerId.GetProjectDomainId() input.ProjectId = ownerId.GetProjectId() params := input.JSON(input) adminCred := auth.AdminCredential() guest, err := db.DoCreate(GuestManager, ctx, adminCred, nil, params, ownerId) if err != nil { return nil, err } func() { lockman.LockObject(ctx, guest) defer lockman.ReleaseObject(ctx, guest) guest.PostCreate(ctx, adminCred, ownerId, nil, params) }() log.Infof("Host convert to %s", guest.GetName()) db.OpsLog.LogEvent(hh, 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", hh, adminCred, opts, "", "", nil) if err != nil { return nil, err } task.ScheduleRun(nil) hh.SetStatus(ctx, userCred, api.BAREMETAL_START_CONVERT, "") return nil, nil } func (hh *SHost) PerformUndoConvert(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !hh.IsBaremetal { return nil, httperrors.NewNotAcceptableError("Not a baremetal") } if hh.HostType == api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewNotAcceptableError("Not being convert to hypervisor") } if hh.GetEnabled() { return nil, httperrors.NewNotAcceptableError("Host should be disabled") } if !utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { return nil, httperrors.NewNotAcceptableError("Cannot unconvert in status %s", hh.Status) } driver, err := hh.GetHostDriver() if err != nil { return nil, errors.Wrapf(err, "GetHostDriver") } err = driver.PrepareUnconvert(hh) if err != nil { return nil, httperrors.NewNotAcceptableError("%v", err) } guests, err := hh.GetGuests() if err != nil { return nil, httperrors.NewGeneralError(errors.Wrapf(err, "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(hh, db.ACT_UNCONVERT_START, "", userCred) logclient.AddActionLogWithContext(ctx, hh, logclient.ACT_UNCONVERT_START, nil, userCred, true) task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnconvertHypervisorTask", hh, userCred, nil, "", "", nil) if err != nil { return nil, err } task.ScheduleRun(nil) return nil, nil } func (hh *SHost) UpdateDiskConfig(userCred mcclient.TokenCredential, layouts []baremetal.Layout) error { bs := hh.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 } // TODO: support multithreaded operation /*func (host *SHost) SyncEsxiHostWires(ctx context.Context, userCred mcclient.TokenCredential, remoteHost cloudprovider.ICloudHost) compare.SyncResult { lockman.LockObject(ctx, host) defer lockman.ReleaseObject(ctx, host) result := compare.SyncResult{} ca := host.GetCloudaccount() host2wires, err := ca.GetHost2Wire(ctx, userCred) if err != nil { result.Error(errors.Wrap(err, "unable to GetHost2Wire")) return result } log.Infof("host2wires: %s", jsonutils.Marshal(host2wires)) ihost := remoteHost.(*esxi.SHost) remoteHostId := ihost.GetId() vsWires := host2wires[remoteHostId] log.Infof("vsWires: %s", jsonutils.Marshal(vsWires)) netIfs := host.GetHostNetInterfaces() for i := range vsWires { vsWire := vsWires[i] if vsWire.SyncTimes > 0 { continue } netif := host.findNetIfs(netIfs, vsWire.Mac, 1) if netif == nil { // do nothing continue } if netif.Bridge != vsWire.VsId { db.Update(netif, func() error { netif.Bridge = vsWire.VsId return nil }) } if len(netif.WireId) == 0 { db.Update(netif, func() error { netif.WireId = vsWire.WireId return nil }) } vsWires[i].SyncTimes += 1 } log.Infof("after sync: %s", jsonutils.Marshal(host2wires)) ca.SetHost2Wire(ctx, userCred, host2wires) return result }*/ /*func (host *SHost) findHostwire(hostwires []SHostwire, wireId string, mac string) *SHostwire { for i := range hostwires { if hostwires[i].WireId == wireId && hostwires[i].MacAddr == mac { return &hostwires[i] } } return nil }*/ func (host *SHost) findNetIfs(netIfs []SNetInterface, mac string, vlanId int) *SNetInterface { for i := range netIfs { if netIfs[i].Mac == mac && netIfs[i].VlanId == vlanId { return &netIfs[i] } } return nil } func (host *SHost) SyncHostExternalNics(ctx context.Context, userCred mcclient.TokenCredential, ihost cloudprovider.ICloudHost, provider *SCloudprovider) compare.SyncResult { result := compare.SyncResult{} netIfs := host.GetHostNetInterfaces() extNics, err := ihost.GetIHostNics() if err != nil { log.Errorf("GetIHostNics fail %s", err) result.Error(err) return result } log.Debugf("SyncHostExternalNics for host %s netIfs %d ihost %s extNics %d", host.Name, len(netIfs), ihost.GetName(), len(extNics)) 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) for i := 0; i < len(netIfs); i++ { find := false for j := 0; j < len(extNics); j++ { if netIfs[i].Mac == extNics[j].GetMac() && netIfs[i].VlanId == extNics[j].GetVlanId() { // find! need to update find = true obn := netIfs[i].GetHostNetwork() var oip string if obn != nil { oip = obn.IpAddr } nip := extNics[j].GetIpAddr() if oip != nip { if obn != nil { disables = append(disables, &netIfs[i]) } if len(nip) > 0 { enables = append(enables, extNics[j]) } } else { wireId := "" extWire := extNics[j].GetIWire() if extWire != nil { wire, err := WireManager.FetchWireByExternalId(provider.Id, extWire.GetGlobalId()) if err != nil { result.AddError(err) } else { wireId = wire.Id } } // in sync, sync interface and bridge if netIfs[i].Bridge != extNics[j].GetBridge() || netIfs[i].Interface != extNics[j].GetDevice() || netIfs[i].WireId != wireId { _, err := db.Update(&netIfs[i], func() error { netIfs[i].Interface = extNics[j].GetDevice() netIfs[i].Bridge = extNics[j].GetBridge() netIfs[i].WireId = wireId return nil }) if err != nil { result.Error(errors.Wrap(err, "update interface and bridge fail")) return result } } } break } } if !find { // need to remove removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: false}) } } for j := 0; j < len(extNics); j++ { find := false for i := 0; i < len(netIfs); i++ { if netIfs[i].Mac == extNics[j].GetMac() && netIfs[i].VlanId == extNics[j].GetVlanId() { find = true break } } if !find { // need to add adds = append(adds, sAddNetInterface{netif: extNics[j], reserveIp: false}) } } // find out which ip need to be reserved for i := 0; i < len(removes); i++ { var oip string obn := removes[i].netif.GetHostNetwork() if obn != nil { oip = obn.IpAddr } if len(oip) == 0 { // skip continue } for j := 0; j < len(adds); j++ { if oip == adds[j].netif.GetIpAddr() { // find out ! IP reserved but interface changed! removes[i].reserveIp = true adds[j].reserveIp = true break } } } log.Debugf("SyncHostExternalNics %s remove %d disable %d enable %d add %d", host.Name, len(removes), len(disables), len(enables), len(adds)) for i := len(removes) - 1; i >= 0; i -= 1 { log.Debugf("remove netif %s", removes[i].netif.Mac) 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 { log.Debugf("disable netif %s", disables[i].Mac) 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(), enables[i].GetVlanId()) // always true reserved address pool log.Debugf("enable netif %s", enables[i].GetMac()) err = host.EnableNetif(ctx, userCred, netif, "", enables[i].GetIpAddr(), "", "", "", true, true, false, false) if err != nil { result.AddError(err) } else { result.Add() } } for i := 0; i < len(adds); i += 1 { log.Debugf("add netif %s", adds[i].netif.GetMac()) // always try reserved pool extNic := adds[i].netif var strNetIf, strBridge *string netif := extNic.GetDevice() bridge := extNic.GetBridge() if len(netif) > 0 { strNetIf = &netif } if len(bridge) > 0 { strBridge = &bridge } wireId := "" extWire := extNic.GetIWire() if extWire != nil { wire, err := WireManager.FetchWireByExternalId(provider.Id, extWire.GetGlobalId()) if err != nil { result.AddError(err) } else { wireId = wire.Id } } err = host.addNetif(ctx, userCred, extNic.GetMac(), extNic.GetVlanId(), wireId, extNic.GetIpAddr(), "", 0, compute.TNicType(extNic.GetNicType()), int(extNic.GetIndex()), extNic.IsLinkUp(), int16(extNic.GetMtu()), false, strNetIf, strBridge, true, true, false, false) 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 (hh *SHost) IsBaremetalAgentReady() bool { return hh.isAgentReady(api.AgentTypeBaremetal) } func (hh *SHost) BaremetalSyncRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) { return HostManager.BaremetalSyncRequest(ctx, method, url, headers, body, hh.ZoneId) } func (hm *SHostManager) BaremetalSyncRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict, zoneId string) (jsonutils.JSONObject, error) { return hm.doAgentRequest(api.AgentTypeBaremetal, ctx, method, url, headers, body, zoneId) } func (hh *SHost) IsEsxiAgentReady() bool { return hh.isAgentReady(api.AgentTypeEsxi) } func (hh *SHost) EsxiRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) { return HostManager.doAgentRequest(api.AgentTypeEsxi, ctx, method, url, headers, body, hh.ZoneId) } func (hh *SHost) GetAgent(at api.TAgentType) *SBaremetalagent { return HostManager.GetAgent(at, hh.ZoneId) } func (hm *SHostManager) GetAgent(at api.TAgentType, zoneId string) *SBaremetalagent { agent := BaremetalagentManager.GetAgent(at, zoneId) if agent == nil { agent = BaremetalagentManager.GetAgent(at, "") } return agent } func (hh *SHost) isAgentReady(agentType api.TAgentType) bool { agent := hh.GetAgent(agentType) if agent == nil { log.Errorf("%s ready: false", agentType) return false } return true } func (hm *SHostManager) doAgentRequest(agentType api.TAgentType, ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict, zoneId string) (jsonutils.JSONObject, error) { agent := hm.GetAgent(agentType, 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(managerId, hostType, hostIp string) (*SHost, error) { q := manager.Query() q = q.Equals("access_ip", hostIp).Equals("host_type", hostType) if len(managerId) > 0 { q = q.Equals("manager_id", managerId) } ret := []SHost{} err := db.FetchModelObjects(manager, q, &ret) if err != nil { return nil, err } if len(ret) == 0 { return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s %s", hostType, hostIp) } if len(ret) > 1 { return nil, errors.Wrapf(cloudprovider.ErrDuplicateId, "%s %s", hostType, hostIp) } return &ret[0], nil } func (hh *SHost) getCloudProviderInfo() SCloudProviderInfo { var region *SCloudregion zone, _ := hh.GetZone() if zone != nil { region, _ = zone.GetRegion() } provider := hh.GetCloudprovider() return MakeCloudProviderInfo(region, zone, provider) } func (hh *SHost) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { desc := hh.SEnabledStatusInfrasResourceBase.GetShortDesc(ctx) info := hh.getCloudProviderInfo() desc.Update(jsonutils.Marshal(&info)) return desc } func (hh *SHost) MarkGuestUnknown(ctx context.Context, userCred mcclient.TokenCredential) { guests, _ := hh.GetGuests() for _, guest := range guests { guest.SetStatus(ctx, userCred, api.VM_UNKNOWN, "host offline") guest.UpdateQgaStatus(api.QGA_STATUS_UNKNOWN) } guests2 := hh.GetGuestsBackupOnThisHost() for _, guest := range guests2 { guest.SetBackupGuestStatus(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). In("host_type", []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_CONTAINER}).IsNullOrEmpty("manager_id") q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("last_ping_at")), sqlchemy.LT(q.Field("last_ping_at"), deadline))) hosts := []SHost{} err := db.FetchModelObjects(manager, q, &hosts) if err != nil { return } updateHealthStatus := false for i := range hosts { func() { lockman.LockObject(ctx, &hosts[i]) defer lockman.ReleaseObject(ctx, &hosts[i]) hosts[i].PerformOffline(ctx, userCred, nil, &api.HostOfflineInput{UpdateHealthStatus: &updateHealthStatus, Reason: fmt.Sprintf("last ping detection at %s", deadline)}) hosts[i].MarkGuestUnknown(ctx, userCred) }() } } func (hh *SHost) IsPrepaidRecycleResource() bool { return hh.ResourceType == api.HostResourceTypePrepaidRecycle } 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) 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(ctx, userCred, api.HOST_STATUS_RUNNING, "exit maintenance") if err != nil { return nil, err } logclient.AddSimpleActionLog(host, logclient.ACT_HOST_UNMAINTENANCE, "host unmaintenance", userCred, true) return nil, nil } 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 { iHost, _ := HostManager.FetchByIdOrName(ctx, userCred, preferHost) if iHost == nil { return nil, httperrors.NewBadRequestError("Host %s not found", preferHost) } host := iHost.(*SHost) preferHostId = host.Id err := host.IsAssignable(ctx, userCred) if err != nil { return nil, errors.Wrap(err, "IsAssignable") } } guests := host.GetKvmGuests() for i := 0; i < len(guests); i++ { lockman.LockObject(ctx, &guests[i]) defer lockman.ReleaseObject(ctx, &guests[i]) guest, err := guests[i].validateForBatchMigrate(ctx, false) 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(ctx, 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) autoMigrateOnHostShutdown(ctx context.Context) bool { return host.GetMetadata(ctx, api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN, nil) == "enable" } func (host *SHost) RemoteHealthStatus(ctx context.Context) string { var status = api.HOST_HEALTH_STATUS_UNKNOWN userCred := auth.AdminCredential() res, err := host.Request( ctx, userCred, "GET", "/hosts/health-status", mcclient.GetTokenHeaders(userCred), nil, ) if err != nil { log.Errorf("failed get remote health status %s", err) } else { status, _ = res.GetString("status") } log.Infof("remote health status %s", status) return status } func (host *SHost) GetHostnameByName() string { hostname := host.Name accessIp := strings.Replace(host.AccessIp, ".", "-", -1) if strings.HasSuffix(host.Name, "-"+accessIp) { hostname = hostname[0 : len(hostname)-len(accessIp)-1] } return hostname } func (host *SHost) OnHostDown(ctx context.Context, userCred mcclient.TokenCredential) { log.Errorf("watched host down %s, status %s", host.Name, host.HostStatus) hostHealthChecker.UnwatchHost(ctx, host.GetHostnameByName()) if host.HostStatus == api.HOST_OFFLINE && !host.EnableHealthCheck && !host.autoMigrateOnHostShutdown(ctx) { // hostagent requested offline, and not enable auto migrate on host shutdown log.Infof("host not need auto migrate on host shutdown") return } hostname := host.Name if host.HostStatus == api.HOST_OFFLINE { // host has been marked offline, check host status in k8s coreCli, err := tokens.GetCoreClient() if err != nil { log.Errorf("failed get k8s client %s", err) return } hostname = host.GetHostnameByName() node, err := coreCli.Nodes().Get(context.TODO(), hostname, metav1.GetOptions{}) if err != nil { log.Errorf("failed get node %s info %s", hostname, err) return } // check node status is ready if length := len(node.Status.Conditions); length > 0 { if node.Status.Conditions[length-1].Type == v1.NodeReady && node.Status.Conditions[length-1].Status == v1.ConditionTrue { log.Infof("node %s status ready, no need entry rescue", hostname) return } } } log.Errorf("host %s down, try rescue guests", hostname) if _, err := host.SaveCleanUpdates(func() error { host.EnableHealthCheck = false host.HostStatus = api.HOST_OFFLINE return nil }); err != nil { log.Errorf("update host %s failed %s", host.Id, err) } data := jsonutils.NewDict() data.Set("reason", jsonutils.NewString("host down")) db.OpsLog.LogEvent(host, db.ACT_HOST_DOWN, data, userCred) logclient.AddActionLogWithContext(ctx, host, logclient.ACT_OFFLINE, data, userCred, false) notifyclient.SystemExceptionNotify(ctx, napi.ActionHostDown, HostManager.Keyword(), data) host.SyncCleanSchedDescCache() host.switchWithBackup(ctx, userCred) host.migrateOnHostDown(ctx, userCred) } func (host *SHost) switchWithBackup(ctx context.Context, userCred mcclient.TokenCredential) { guests := host.GetGuestsMasterOnThisHost() for i := 0; i < len(guests); i++ { data := jsonutils.NewDict() _, err := guests[i].PerformSwitchToBackup(ctx, userCred, nil, data) if err != nil { db.OpsLog.LogEvent( &guests[i], db.ACT_SWITCH_FAILED, fmt.Sprintf("PerformSwitchToBackup on host down: %s", err), userCred, ) logclient.AddSimpleActionLog( &guests[i], logclient.ACT_SWITCH_TO_BACKUP, fmt.Sprintf("PerformSwitchToBackup on host down: %s", err), userCred, false, ) } } } func (host *SHost) migrateOnHostDown(ctx context.Context, userCred mcclient.TokenCredential) { if host.GetMetadata(ctx, api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN, nil) == "enable" { if err := host.MigrateSharedStorageServers(ctx, userCred); err != nil { db.OpsLog.LogEvent(host, db.ACT_HOST_DOWN, fmt.Sprintf("migrate servers failed %s", err), userCred) } } } func (host *SHost) MigrateSharedStorageServers(ctx context.Context, userCred mcclient.TokenCredential) error { guests, err := host.GetGuests() if err != nil { return errors.Wrapf(err, "host %s(%s) get guests", host.Name, host.Id) } migGuests := []*SGuest{} hostGuests := []*api.GuestBatchMigrateParams{} for i := 0; i < len(guests); i++ { if guests[i].isNotRunningStatus(guests[i].Status) { // skip not running guests continue } lockman.LockObject(ctx, &guests[i]) defer lockman.ReleaseObject(ctx, &guests[i]) _, err := guests[i].validateForBatchMigrate(ctx, true) if err != nil { continue } else { bmp := &api.GuestBatchMigrateParams{ Id: guests[i].Id, LiveMigrate: false, RescueMode: true, OldStatus: guests[i].Status, } guests[i].SetStatus(ctx, userCred, api.VM_START_MIGRATE, "host down") hostGuests = append(hostGuests, bmp) migGuests = append(migGuests, &guests[i]) } } kwargs := jsonutils.NewDict() kwargs.Set("guests", jsonutils.Marshal(hostGuests)) db.OpsLog.LogEvent(host, db.ACT_HOST_DOWN_AUTO_MIGRATE, kwargs, userCred) logclient.AddActionLogWithContext(ctx, host, logclient.ACT_HOST_DOWN_AUTO_MIGRATE, kwargs, userCred, true) notifyclient.SystemExceptionNotify(ctx, napi.ActionHostDownAutoMigrate, HostManager.Keyword(), kwargs) return GuestManager.StartHostGuestsMigrateTask(ctx, userCred, migGuests, kwargs, "") } func (host *SHost) SetStatus(ctx context.Context, userCred mcclient.TokenCredential, status string, reason string) error { err := host.SEnabledStatusInfrasResourceBase.SetStatus(ctx, 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(ctx, 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 (host *SHost) GetNics() []*types.SNic { netifs := host.GetAllNetInterfaces() nicInfos := []*types.SNic{} if netifs != nil && len(netifs) > 0 { for i := 0; i < len(netifs); i += 1 { nicInfos = append(nicInfos, netifs[i].getBaremetalJsonDesc()) } } return nicInfos } func (host *SHost) GetUEFIInfo() (*types.EFIBootMgrInfo, error) { if host.UefiInfo == nil { return nil, nil } info := new(types.EFIBootMgrInfo) if err := host.UefiInfo.Unmarshal(info); err != nil { return nil, errors.Wrap(err, "host.UefiInfo.Unmarshal") } return info, nil } func (hh *SHost) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { url := fmt.Sprintf("/baremetals/%s/jnlp", hh.Id) header := mcclient.GetTokenHeaders(userCred) resp, err := hh.BaremetalSyncRequest(ctx, "POST", url, header, nil) if err != nil { return nil, errors.Wrap(err, "BaremetalSyncRequest") } return resp, nil } func (hh *SHost) PerformInsertIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(hh.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, hh.StartInsertIsoTask(ctx, userCred, image.Id, boot, "") } return nil, httperrors.NewInvalidStatusError("Cannot do insert-iso in status %s", hh.Status) } func (hh *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") hh.SetStatus(ctx, userCred, api.BAREMETAL_START_INSERT_ISO, "start insert iso task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCdromTask", hh, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (hh *SHost) PerformEjectIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if utils.IsInStringArray(hh.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) { return nil, hh.StartEjectIsoTask(ctx, userCred, "") } return nil, httperrors.NewInvalidStatusError("Cannot do eject-iso in status %s", hh.Status) } func (hh *SHost) StartEjectIsoTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { data := jsonutils.NewDict() data.Add(jsonutils.NewString(api.BAREMETAL_CDROM_ACTION_EJECT), "action") hh.SetStatus(ctx, userCred, api.BAREMETAL_START_EJECT_ISO, "start eject iso task") if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCdromTask", hh, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) return nil } } func (hh *SHost) PerformSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if hh.HostType != api.HOST_TYPE_BAREMETAL { return nil, httperrors.NewBadRequestError("Cannot sync config a non-baremetal host") } hh.SetStatus(ctx, userCred, api.BAREMETAL_SYNCING_STATUS, "") return nil, hh.StartSyncConfig(ctx, userCred, "") } func (hh *SHost) StartSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { return hh.startSyncConfig(ctx, userCred, parentTaskId, false) } func (hh *SHost) startSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, noStatus bool) error { data := jsonutils.NewDict() data.Add(jsonutils.NewBool(noStatus), "not_sync_status") task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncConfigTask", hh, userCred, data, parentTaskId, "", nil) if err != nil { return err } return task.ScheduleRun(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) { ret, err := host.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformChangeOwner") } localStorages := host._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) for i := range localStorages { _, err := localStorages[i].performChangeOwnerInternal(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "local storage change owner") } } err = host.StartSyncTask(ctx, userCred, "") if err != nil { return nil, errors.Wrap(err, "PerformChangeOwner StartSyncTask err") } return ret, nil } func (hh *SHost) StartSyncTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { if task, err := taskman.TaskManager.NewTask(ctx, "HostSyncTask", hh, userCred, jsonutils.NewDict(), parentTaskId, "", nil); err != nil { log.Errorln(err) return err } else { task.ScheduleRun(nil) } return nil } func (host *SHost) GetChangeOwnerRequiredDomainIds() []string { requires := stringutils2.SSortedStrings{} guests, _ := host.GetGuests() for i := range guests { requires = stringutils2.Append(requires, guests[i].DomainId) } return requires } func GetHostQuotaKeysFromCreateInput(owner mcclient.IIdentityProvider, input api.HostCreateInput) quotas.SDomainRegionalCloudResourceKeys { ownerId := &db.SOwnerId{DomainId: owner.GetProjectDomainId()} var zone *SZone if len(input.ZoneId) > 0 { zone = ZoneManager.FetchZoneById(input.ZoneId) } zoneKeys := fetchZonalQuotaKeys(rbacscope.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(rbacscope.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.PerformPublicDomainInput) (jsonutils.JSONObject, error) { // perform public for all connected local storage storages := host._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) 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._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) 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) } func (host *SHost) PerformSetReservedResourceForIsolatedDevice( ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.IsolatedDeviceReservedResourceInput, ) (jsonutils.JSONObject, error) { if input.ReservedCpu != nil && *input.ReservedCpu < 0 { return nil, httperrors.NewInputParameterError("reserved cpu must >= 0") } if input.ReservedMemory != nil && *input.ReservedMemory < 0 { return nil, httperrors.NewInputParameterError("reserved memory must >= 0") } if input.ReservedStorage != nil && *input.ReservedStorage < 0 { return nil, httperrors.NewInputParameterError("reserved storage must >= 0") } devs := IsolatedDeviceManager.FindByHost(host.Id) if len(devs) == 0 { return nil, nil } if input.ReservedCpu != nil && host.GetCpuCount() < *input.ReservedCpu*len(devs) { return nil, httperrors.NewBadRequestError( "host %s can't reserve %d cpu for each isolated device, not enough", host.Name, *input.ReservedCpu) } if input.ReservedMemory != nil && host.GetMemSize() < *input.ReservedMemory*len(devs) { return nil, httperrors.NewBadRequestError( "host %s can't reserve %dM memory for each isolated device, not enough", host.Name, *input.ReservedMemory) } caps := host.GetAttachedLocalStorageCapacity() if input.ReservedStorage != nil && caps.Capacity < int64(*input.ReservedStorage*len(devs)) { return nil, httperrors.NewBadRequestError( "host %s can't reserve %dM storage for each isolated device, not enough", host.Name, input.ReservedStorage) } defer func() { go host.ClearSchedDescCache() }() for i := 0; i < len(devs); i++ { _, err := db.Update(&devs[i], func() error { if input.ReservedCpu != nil { devs[i].ReservedCpu = *input.ReservedCpu } if input.ReservedMemory != nil { devs[i].ReservedMemory = *input.ReservedMemory } if input.ReservedStorage != nil { devs[i].ReservedStorage = *input.ReservedStorage } return nil }) if err != nil { return nil, errors.Wrap(err, "update isolated device") } } logclient.AddSimpleActionLog(host, logclient.ACT_SET_RESERVE_RESOURCE_FOR_ISOLATED_DEVICES, nil, userCred, true) return nil, nil } func (manager *SHostManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings, ) (*sqlchemy.SQuery, error) { q, err := manager.SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys") } if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) { q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) if err != nil { return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys") } } if keys.ContainsAny(manager.SZoneResourceBaseManager.GetExportKeys()...) { q, err = manager.SZoneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) if err != nil { return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemExportKeys") } } return q, nil } func (manager *SHostManager) FetchHostByExtId(extid string) *SHost { host := SHost{} host.SetModelManager(manager, &host) err := manager.Query().Equals("external_id", extid).First(&host) if err != nil { log.Errorf("fetchHostByExtId fail %s", err) return nil } else { return &host } } func (host *SHost) IsAssignable(ctx context.Context, userCred mcclient.TokenCredential) error { if db.IsAdminAllowPerform(ctx, userCred, host, "assign-host") { return nil } else if db.IsDomainAllowPerform(ctx, userCred, host, "assign-host") && (userCred.GetProjectDomainId() == host.DomainId || host.PublicScope == string(rbacscope.ScopeSystem) || (host.PublicScope == string(rbacscope.ScopeDomain) && utils.IsInStringArray(userCred.GetProjectDomainId(), host.GetSharedDomains()))) { return nil } else { return httperrors.NewNotSufficientPrivilegeError("Only system admin can assign host") } } func (manager *SHostManager) initHostname() error { hosts := []SHost{} q := manager.Query().IsNullOrEmpty("hostname") err := db.FetchModelObjects(manager, q, &hosts) if err != nil { return errors.Wrapf(err, "db.FetchModelObjects") } for i := range hosts { db.Update(&hosts[i], func() error { hostname, _ := manager.SHostnameResourceBaseManager.ValidateHostname( hosts[i].Hostname, "", api.HostnameInput{ Hostname: hosts[i].Name, }, ) hosts[i].Hostname = hostname.Hostname return nil }) } return nil } func (manager *SHostManager) initOvnMappedIp6Addr() error { hosts := []SHost{} q := manager.Query().IsNotEmpty("ovn_version") q = q.Filter( sqlchemy.OR( sqlchemy.IsNullOrEmpty(q.Field("ovn_mapped_ip6_addr")), sqlchemy.IsNullOrEmpty(q.Field("ovn_mapped_ip_addr")), ), ) err := db.FetchModelObjects(manager, q, &hosts) if err != nil { return errors.Wrapf(err, "db.FetchModelObjects") } for i := range hosts { hh := &hosts[i] var v4addr string if hh.OvnMappedIp6Addr == "" { addr, err := HostManager.allocOvnMappedIpAddr(context.Background()) if err != nil { return errors.Wrapf(err, "host %s(%s): alloc vpc mapped addr", hh.Name, hh.Id) } v4addr = addr } else { v4addr = hh.OvnMappedIpAddr } if _, err := db.Update(hh, func() error { hh.OvnMappedIpAddr = v4addr hh.OvnMappedIp6Addr = api.GenVpcMappedIP6(v4addr) return nil }); err != nil { return errors.Wrapf(err, "host %s(%s): db update vpc mapped addr", hh.Name, hh.Id) } } return nil } func (manager *SHostManager) InitializeData() error { var err error err = manager.initHostname() if err != nil { return errors.Wrapf(err, "initHostname") } err = manager.initOvnMappedIp6Addr() if err != nil { return errors.Wrapf(err, "initOvnMappedIp6Addr") } return nil } func (hh *SHost) PerformProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { driver, err := hh.GetHostDriver() if err != nil { return nil, errors.Wrapf(err, "GetHostDriver") } return driver.RequestProbeIsolatedDevices(ctx, userCred, hh, data) } func (hh *SHost) PerformSyncIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { devs, err := IsolatedDeviceManager.GetAllDevsOnHost(hh.Id) if err != nil { return nil, err } reqDevs, err := data.GetArray("isolated_devices") if err != nil { return nil, httperrors.NewMissingParameterError("isolated_devices") } retDevs := jsonutils.NewArray() foundDevIndex := map[int]struct{}{} eg := errgroup.Group{} wg := sync.Mutex{} for i := range devs { foundDev := false dev := &devs[i] for j := range reqDevs { venderDeviceId, _ := reqDevs[j].GetString("vendor_device_id") devAddr, _ := reqDevs[j].GetString("addr") mdevId, _ := reqDevs[j].GetString("mdev_id") if dev.VendorDeviceId == venderDeviceId && dev.Addr == devAddr && dev.MdevId == mdevId { eg.Go(func() error { // update isolated device log.Infof("dev %s %s do update", dev.DevType, dev.Addr) devRet, err := db.DoUpdate(IsolatedDeviceManager, dev, ctx, userCred, jsonutils.NewDict(), reqDevs[j]) if err != nil { return err } wg.Lock() retDevs.Add(devRet) wg.Unlock() return nil }) foundDevIndex[j] = struct{}{} foundDev = true break } } if !foundDev { eg.Go(func() error { // detach isolated device params := jsonutils.NewDict() params.Set("purge", jsonutils.JSONTrue) _, err := dev.PerformPurge(ctx, userCred, nil, params) if err != nil { return err } return err }) } } if err = eg.Wait(); err != nil { return nil, err } for i := range reqDevs { if _, ok := foundDevIndex[i]; ok { continue } // create isolated device dev, err := db.DoCreate(IsolatedDeviceManager, ctx, userCred, nil, reqDevs[i], userCred) if err != nil { return nil, err } devRet, err := db.GetItemDetails(IsolatedDeviceManager, dev, ctx, userCred) if err != nil { return nil, err } retDevs.Add(devRet) } res := jsonutils.NewDict() res.Set("isolated_devices", retDevs) return res, nil } func (hh *SHost) GetPinnedCpusetCores(ctx context.Context, userCred mcclient.TokenCredential, excludeGuestIds []string) (*cpuset.CPUSet, error) { gsts, err := hh.GetGuests() if err != nil { return nil, errors.Wrap(err, "Get all guests") } ret := cpuset.NewBuilder() for _, gst := range gsts { if utils.IsInStringArray(gst.Id, excludeGuestIds) { continue } pinned, err := gst.getPinnedCpusetCores(ctx, userCred) if err != nil { return nil, errors.Wrapf(err, "get guest %s pinned cpuset cores", gst.GetName()) } ret.Add(pinned...) } resCpuset := ret.Result() if resCpuset.Size() == 0 { return nil, nil } return &resCpuset, nil } func (hh *SHost) GetReservedCpus() (*cpuset.CPUSet, error) { reservedCpusStr := hh.GetMetadata(context.Background(), api.HOSTMETA_RESERVED_CPUS_INFO, nil) if reservedCpusStr != "" { reservedCpusJson, err := jsonutils.ParseString(reservedCpusStr) if err != nil { return nil, errors.Wrap(err, "parse reserved cpus info failed") } reservedCpusInfo := api.HostReserveCpusInput{} err = reservedCpusJson.Unmarshal(&reservedCpusInfo) if err != nil { return nil, errors.Wrap(err, "unmarshal host reserved cpus info failed") } if reservedCpusInfo.Cpus == "" { return nil, nil } cs, err := cpuset.Parse(reservedCpusInfo.Cpus) if err != nil { return nil, errors.Wrap(err, "parse reserved cpuset") } return &cs, nil } return nil, nil } func (hh *SHost) updateHostReservedCpus(ctx context.Context, userCred mcclient.TokenCredential) error { reservedCpus, err := hh.GetReservedCpus() if err != nil { return err } pinnedCpus, err := hh.GetPinnedCpusetCores(ctx, userCred, nil) if err != nil { return err } var reservedCpuCnt = 0 if reservedCpus != nil { reservedCpuCnt += reservedCpus.Size() } if pinnedCpus != nil { reservedCpuCnt += pinnedCpus.Size() } if hh.CpuReserved != reservedCpuCnt { _, err = db.Update(hh, func() error { hh.CpuReserved = reservedCpuCnt return nil }) if err != nil { return err } } hh.ClearSchedDescCache() return nil } func (h *SHost) PerformSyncGuestNicTraffics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestNicTrafficSyncInput) (jsonutils.JSONObject, error) { for guestId := range input.Traffic { nicTrafficMap := input.Traffic[guestId] guest := GuestManager.FetchGuestById(guestId) gns, err := guest.GetNetworks("") if err != nil { log.Errorf("failed fetch guest %s networks %s", guestId, err) continue } for i := range gns { nicTraffic, ok := nicTrafficMap[gns[i].MacAddr] if !ok { continue } if err := gns[i].UpdateNicTrafficUsed(ctx, guest, nicTraffic, input.SyncAt, input.IsReset); err != nil { log.Errorf("failed update guestnetwork %d traffic used %s", gns[i].RowId, err) continue } } } return nil, nil } func (h *SHost) GetDetailsAppOptions(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { return h.Request(ctx, userCred, httputils.GET, "/app-options", nil, nil) } func (h *SHost) GetDetailsWorkerStats(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { return h.Request(ctx, userCred, httputils.GET, "/worker_stats", nil, nil) } func (hh *SHost) GetDetailsIsolatedDeviceNumaStats(ctx context.Context, userCred mcclient.TokenCredential, input *api.HostIsolatedDeviceNumaStatsInput) (jsonutils.JSONObject, error) { if !utils.IsInStringArray(input.DevType, api.VALID_PASSTHROUGH_TYPES) { return nil, httperrors.NewInputParameterError("dev_type %s is invalid", input.DevType) } stats, err := IsolatedDeviceManager.GetHostAllocatedIsolatedDeviceNumaStats(input.DevType, hh.Id) if err != nil { return nil, err } return jsonutils.Marshal(stats), nil } func (hh *SHost) IsAttach2Wire(wireId string) bool { netifs := hh.getNetifsOnWire(wireId) return len(netifs) > 0 } func (h *SHost) updateNotify(ctx context.Context, userCred mcclient.TokenCredential) { notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Action: notifyclient.ActionUpdate, Obj: h, }) }