Files
cloudpods/pkg/compute/models/hosts.go
T
2024-04-10 01:33:25 +08:00

6611 lines
209 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
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/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"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "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/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
}
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:"16" 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 `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" default:"8" list:"domain" update:"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"`
// 内存大小,单位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" default:"1" list:"domain" update:"domain" create:"domain_optional"`
// 页大小
PageSizeKB int `nullable:"false" default:"4" list:"domain" update:"domain" create:"domain_optional"`
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"`
// 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"`
// UEFI详情
UefiInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"`
}
func (manager *SHostManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{
{ZoneManager},
}
}
// 宿主机/物理机列表
func (manager *SHostManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.HostListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
q, err = manager.SZoneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ZonalFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemFilter")
}
resType := query.ResourceType
if len(resType) > 0 {
switch resType {
case api.HostResourceTypeShared:
q = q.Filter(
sqlchemy.OR(
sqlchemy.IsNullOrEmpty(q.Field("resource_type")),
sqlchemy.Equals(q.Field("resource_type"), api.HostResourceTypeShared),
),
)
default:
q = q.Equals("resource_type", resType)
}
}
q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter")
}
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 {
hnQ := HostnetworkManager.Query("baremetal_id") //.Contains("ip_addr", query.AnyIp).SubQuery()
conditions := []sqlchemy.ICondition{}
for _, ip := range query.AnyIp {
conditions = append(conditions, sqlchemy.Contains(hnQ.Field("ip_addr"), ip))
}
hn := hnQ.Filter(
sqlchemy.OR(conditions...),
)
conditions = []sqlchemy.ICondition{}
for _, ip := range query.AnyIp {
conditions = append(conditions, sqlchemy.Contains(q.Field("access_ip"), ip))
conditions = append(conditions, sqlchemy.Contains(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)
}
}
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 {
hostType, ok := api.HYPERVISOR_HOSTTYPE[hypervisorStr]
if !ok {
return nil, httperrors.NewInputParameterError("not supported hypervisor %s", hypervisorStr)
}
q = q.Filter(sqlchemy.Equals(q.Field("host_type"), hostType))
}
usable := (query.Usable != nil && *query.Usable)
if usable {
hosts := HostManager.Query().SubQuery()
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,
"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) > 0 {
q = q.In(f, vars)
}
}
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.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")})
}
return q, nil
}
func (manager *SHostManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SZoneResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (manager *SHostManager) CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*db.CustomizeListFilters, error) {
filters := db.NewCustomizeListFilters()
if query.Contains("cdrom_boot") {
cdromBoot := jsonutils.QueryBoolean(query, "cdrom_boot", false)
cdromBootF := func(obj jsonutils.JSONObject) (bool, error) {
id, err := obj.GetString("id")
if err != nil {
return false, err
}
host := manager.FetchHostById(id)
ipmiInfo, err := host.GetIpmiInfo()
if err != nil {
return false, err
}
if cdromBoot && ipmiInfo.CdromBoot {
return true, nil
}
if !cdromBoot && !ipmiInfo.CdromBoot {
return true, nil
}
return false, nil
}
filters.Append(cdromBootF)
}
return filters, nil
}
func (hh *SHost) IsArmHost() bool {
return hh.CpuArchitecture == apis.OS_ARCH_AARCH64
}
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) {
zone, err := hh.GetZone()
if err != nil {
return nil, err
}
return zone.GetRegion()
}
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 hh.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) 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:%d", managerUrl.Scheme, strings.Split(managerUrl.Host, ":")[0], 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("compute_v2", options.Options.Region, "")
if err != nil {
return nil, err
}
var script string
script += fmt.Sprintf("curl -k -fsSL -H 'X-Auth-Token: %s' %s/misc/bm-prepare-script", userCred.GetTokenString(), regionUri)
res := jsonutils.NewDict()
res.Add(jsonutils.NewString(script), "script")
return res, nil
}
func (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) GetEnabledKvmHost() (*SHost, error) {
hostq := HostManager.Query().IsTrue("enabled").Equals("host_status", api.HOST_ONLINE).In("host_type", []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_KVM})
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 && len(bn.IpAddr) > 0 && regutils.MatchIP4Addr(bn.IpAddr) {
ReservedipManager.ReserveIP(ctx, userCred, net, bn.IpAddr, "Delete baremetalnetwork to reserve", api.AddressTypeIPv4)
}
}
func (hh *SHost) GetHostDriver() IHostDriver {
if !utils.IsInStringArray(hh.HostType, api.HOST_TYPES) {
log.Fatalf("Unsupported host type %s", hh.HostType)
}
return GetHostDriver(hh.HostType)
}
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], provider)
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, provider *SCloudprovider) error {
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.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()
hh.StorageType = extHost.GetStorageType()
hh.HostType = extHost.GetHostType()
hh.OvnVersion = extHost.GetOvnVersion()
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 err := hh.syncSchedtags(ctx, userCred, extHost); err != nil {
log.Errorf("syncSchedtags fail: %v", err)
return 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 = 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 (s *SHost) getAllSchedtagsWithExtSchedtagKey(ctx context.Context, userCred mcclient.TokenCredential) (map[string]*SSchedtag, error) {
q := SchedtagManager.Query().Equals("resource_type", HostManager.KeywordPlural())
sts := make([]SSchedtag, 0, 5)
err := db.FetchModelObjects(SchedtagManager, q, &sts)
if err != nil {
return nil, err
}
stMap := make(map[string]*SSchedtag)
for i := range sts {
extTagName := sts[i].GetMetadata(ctx, METADATA_EXT_SCHEDTAG_KEY, userCred)
if len(extTagName) == 0 {
continue
}
stMap[extTagName] = &sts[i]
}
return stMap, nil
}
func (s *SHost) syncSchedtags(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost) error {
stq := SchedtagManager.Query()
subq := HostschedtagManager.Query("schedtag_id").Equals("host_id", s.Id).SubQuery()
stq = stq.Join(subq, sqlchemy.Equals(stq.Field("id"), subq.Field("schedtag_id")))
schedtags := make([]SSchedtag, 0)
err := db.FetchModelObjects(SchedtagManager, stq, &schedtags)
if err != nil {
return errors.Wrap(err, "db.FetchModelObjects")
}
extSchedtagStrs, err := extHost.GetSchedtags()
if err != nil {
return errors.Wrap(err, "extHost.GetSchedtags")
}
extStStrSet := sets.NewString(extSchedtagStrs...)
removed := make([]*SSchedtag, 0)
removedIds := make([]string, 0)
for i := range schedtags {
stag := &schedtags[i]
extTagName := stag.GetMetadata(ctx, METADATA_EXT_SCHEDTAG_KEY, userCred)
if len(extTagName) == 0 {
continue
}
if !extStStrSet.Has(extTagName) {
removed = append(removed, stag)
removedIds = append(removedIds, stag.GetId())
} else {
extStStrSet.Delete(extTagName)
}
}
added := extStStrSet.UnsortedList()
var stagMap map[string]*SSchedtag
if len(added) > 0 {
stagMap, err = s.getAllSchedtagsWithExtSchedtagKey(ctx, userCred)
if err != nil {
return errors.Wrap(err, "getAllSchedtagsWithExtSchedtagKey")
}
}
for _, stStr := range added {
st, ok := stagMap[stStr]
if !ok {
st = &SSchedtag{
ResourceType: HostManager.KeywordPlural(),
}
st.DomainId = s.DomainId
st.Name = stStr
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 %q", stStr)
}
st.SetMetadata(ctx, METADATA_EXT_SCHEDTAG_KEY, stStr, userCred)
}
// attach
hostschedtag := &SHostschedtag{
HostId: s.GetId(),
}
hostschedtag.SetModelManager(HostschedtagManager, hostschedtag)
hostschedtag.SchedtagId = st.GetId()
err = HostschedtagManager.TableSpec().Insert(ctx, hostschedtag)
if err != nil {
return errors.Wrapf(err, "unable to create hostschedtag for tag %q host %q", stStr, s.GetId())
}
}
if len(removedIds) == 0 {
return nil
}
q := HostschedtagManager.Query().Equals("host_id", s.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(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()
host.OvnVersion = extHost.GetOvnVersion()
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.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())
if err := host.syncSchedtags(ctx, userCred, extHost); err != nil {
log.Errorf("newFromCloudHost fail in syncSchedtags %v", err)
return nil, 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 {
return false, ""
}
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 {
tags, _ := ext.GetTags()
for key := range tags {
key = strings.Trim(key, "")
if len(key) > 0 && utils.IsInStringArray(key, userKeys) {
return true, key
}
}
}
return false, ""
}
func (self *SGuest) Purge(ctx context.Context, userCred mcclient.TokenCredential) error {
return self.purge(ctx, userCred)
}
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 system tag key: %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 system tag key: %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() == 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 *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))
}
q := sqlchemy.Union(q1, q2, q3).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("mem_reserved"),
hosts.Field("mem_cmtbound"),
hosts.Field("cpu_count"),
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
MemReserved int
MemCmtbound float32
CpuCount int
CpuReserved int
CpuCmtbound float32
StorageSize int
IsolatedReservedMemory int64
IsolatedReservedCpu int64
IsolatedReservedStorage int64
}
type HostsCountStat struct {
StorageSize int64
Count int64
Memory int64
MemoryTotal int64
MemoryVirtual float64
MemoryReserved int64
CPU int64
CPUTotal 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
totalCPU int64 = 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)
tCPU += int64(aCpu)
totalCPU += int64(stat.CpuCount)
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,
MemoryTotal: totalMem,
MemoryVirtual: tVmem,
MemoryReserved: rMem,
CPU: tCPU,
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
}
func (hh *SHost) GetSchedtags() []SSchedtag {
return GetSchedtags(HostschedtagManager, hh.Id)
}
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 (hh *SHost) getMoreDetails(ctx context.Context, out api.HostDetails, showReason bool) api.HostDetails {
server := hh.GetBaremetalServer()
if server != nil {
out.ServerId = server.Id
out.Server = server.Name
out.ServerPendingDeleted = server.PendingDeleted
if hh.HostType == api.HOST_TYPE_BAREMETAL {
out.ServerIps = strings.Join(server.GetRealIPs(), ",")
}
}
nics := hh.GetNics()
if nics != nil && len(nics) > 0 {
// nicInfos := []jsonutils.JSONObject{}
// for i := 0; i < len(nics); i += 1 {
// nicInfos = append(nicInfos, jsonutils.Marshal(nics[i]))
// }
out.NicCount = len(nics)
out.NicInfo = nics
}
out.Schedtags = GetSchedtagsDetailsToResourceV2(hh, ctx)
var usage *SHostGuestResourceUsage
if options.Options.IgnoreNonrunningGuests {
usage = hh.getGuestsResource(api.VM_RUNNING)
} else {
usage = hh.getGuestsResource("")
}
if usage != nil {
out.CpuCommit = usage.GuestVcpuCount
out.MemCommit = usage.GuestVmemSize
}
totalCpu := hh.GetCpuCount()
cpuCommitRate := 0.0
if totalCpu > 0 && usage.GuestVcpuCount > 0 {
cpuCommitRate = float64(usage.GuestVcpuCount) * 1.0 / float64(totalCpu)
}
out.CpuCommitRate = cpuCommitRate
totalMem := hh.GetMemSize()
memCommitRate := 0.0
if totalMem > 0 && usage.GuestVmemSize > 0 {
memCommitRate = float64(usage.GuestVmemSize) * 1.0 / float64(totalMem)
}
out.MemCommitRate = memCommitRate
capa := hh.GetAttachedLocalStorageCapacity()
out.Storage = capa.Capacity
out.StorageUsed = capa.Used
out.ActualStorageUsed = capa.ActualUsed
out.StorageWaste = capa.Wasted
out.StorageVirtual = capa.VCapacity
out.StorageFree = capa.GetFree()
out.StorageCommitRate = capa.GetCommitRate()
out.Spec = hh.GetHardwareSpecification()
// custom cpu mem commit bound
out.CpuCommitBound = hh.GetCPUOvercommitBound()
out.MemCommitBound = hh.GetMemoryOvercommitBound()
// extra = hh.SManagedResourceBase.getExtraDetails(ctx, extra)
out.IsPrepaidRecycle = false
if hh.IsPrepaidRecycle() {
out.IsPrepaidRecycle = true
}
if hh.IsBaremetal {
out.CanPrepare = true
err := hh.canPrepare()
if err != nil {
out.CanPrepare = false
if showReason {
out.PrepareFailReason = err.Error()
}
}
}
if hh.EnableHealthCheck && hostHealthChecker != nil {
out.AllowHealthCheck = true
}
if hh.GetMetadata(ctx, api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN, nil) == "enable" {
out.AutoMigrateOnHostDown = true
}
if hh.GetMetadata(ctx, api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN, nil) == "enable" {
out.AutoMigrateOnHostShutdown = true
}
if count, rs := hh.GetReservedResourceForIsolatedDevice(); rs != nil {
out.ReservedResourceForGpu = *rs
out.IsolatedDeviceCount = count
}
return out
}
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
}
hostIds := make([]string, 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
rows[i] = host.getMoreDetails(ctx, rows[i], showReason)
}
guestCnts := manager.FetchGuestCnt(hostIds)
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
}
}
return rows
}
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
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, &quota, &quota, 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, []string{api.NETWORK_TYPE_PXE, api.NETWORK_TYPE_BAREMETAL, api.NETWORK_TYPE_GUEST})
if err != nil {
return input, httperrors.NewGeneralError(err)
}
accessNet = net
}
}
}
if accessNet != nil {
lockman.LockObject(ctx, accessNet)
defer lockman.ReleaseObject(ctx, accessNet)
accessIp, err := accessNet.GetFreeIP(ctx, userCred, nil, nil, accessIpAddr, api.IPAllocationNone, true, 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, &quota)
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 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") {
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
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) {
msg := fmt.Sprintf("ipmi_ip_addr: %s not valid ipv4 address", data.IpmiIpAddr)
log.Errorf(msg)
return info, errors.Wrap(httperrors.ErrInvalidFormat, msg)
}
info.IpAddr = data.IpmiIpAddr
if data.IpmiPresent != nil {
info.Present = *data.IpmiPresent
}
if data.IpmiLanChannel != nil {
info.LanChannel = *data.IpmiLanChannel
}
if data.IpmiVerified != nil {
info.Verified = *data.IpmiVerified
}
if data.IpmiRedfishApi != nil {
info.RedfishApi = *data.IpmiRedfishApi
}
if data.IpmiCdromBoot != nil {
info.CdromBoot = *data.IpmiCdromBoot
}
if data.IpmiPxeBoot != nil {
info.PxeBoot = *data.IpmiPxeBoot
}
return info, nil
}
func (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
}
task.ScheduleRun(nil)
return 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
}
task.ScheduleRun(nil)
return nil, 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 err
}
task.ScheduleRun(nil)
return 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.StartSyncAllGuestsStatusTask(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
}
return nil, hh.SetAllMetadata(ctx, meta, userCred)
}
func (hh *SHost) StartSyncAllGuestsStatusTask(ctx context.Context, userCred mcclient.TokenCredential) error {
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncAllGuestsStatusTask", hh, userCred, nil, "", "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return 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 %s", jsonutils.Marshal(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)
}
}
}
hh.SetMetadata(ctx, "root_partition_used_capacity_mb", input.RootPartitionUsedCapacityMb, userCred)
hh.SetMetadata(ctx, "memory_used_mb", input.MemoryUsedMb, userCred)
}
if hh.HostStatus != api.HOST_ONLINE {
hh.PerformOnline(ctx, userCred, query, nil)
} else {
hh.SaveUpdates(func() error {
hh.LastPingAt = time.Now()
return nil
})
if hh.hasUnknownGuests() {
hh.StartSyncAllGuestsStatusTask(ctx, userCred)
}
}
result := jsonutils.NewDict()
result.Set("name", jsonutils.NewString(hh.GetName()))
dependSvcs := []string{"ntpd", "kafka", apis.SERVICE_TYPE_INFLUXDB, apis.SERVICE_TYPE_VICTORIA_METRICS, "elasticsearch"}
catalog := auth.GetCatalogData(dependSvcs, options.Options.Region)
if catalog == nil {
return nil, fmt.Errorf("Get catalog error")
}
result.Set("catalog", catalog)
appParams := appsrv.AppContextGetParams(ctx)
if appParams != nil {
// skip log&trace, when everything is normal
appParams.SkipTrace = true
appParams.SkipLog = true
}
return result, nil
}
func (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 hh.HostType != api.HOST_TYPE_HYPERVISOR {
return nil, httperrors.NewNotSupportedError("host type %s not support reserve cpus", hh.HostType)
}
cnt, err := hh.GetRunningGuestCount()
if err != nil {
return nil, err
}
if cnt > 0 {
return nil, httperrors.NewBadRequestError("host %s has %d guests, can't update reserve cpus", hh.Id, cnt)
}
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")
}
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)
}
}
err = hh.SetMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_INFO, input, userCred)
if err != nil {
return nil, err
}
if hh.CpuReserved < cs.Size() {
_, err = db.Update(hh, func() error {
hh.CpuReserved = cs.Size()
return nil
})
}
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.IsBaremetal {
return httperrors.NewInvalidStatusError("not a baremetal")
}
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)
}
server := hh.GetBaremetalServer()
if server != nil && server.Status != api.VM_ADMIN {
return httperrors.NewInvalidStatusError("Cannot prepare baremetal in server status %s", server.Status)
}
return nil
}
func (hh *SHost) PerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := hh.canPrepare()
if err != nil {
return nil, err
}
var onfinish string
server := hh.GetBaremetalServer()
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 (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.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
wire := 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")
}
}
wire = wireObj.GetId()
}
ipAddr := input.IpAddr
if len(ipAddr) > 0 && !regutils.MatchIP4Addr(ipAddr) {
return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid ip_addr %s", ipAddr)
}
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)
isLinkUp := tristate.None
if linkUp != "" {
if utils.ToBool(linkUp) {
isLinkUp = tristate.True
} else {
isLinkUp = tristate.False
}
}
err = h.addNetif(ctx, userCred, mac, vlan, wire, ipAddr, int(rate), nicType, index, isLinkUp,
int16(mtu), reset, netIf, bridge, reserve, requireDesignatedIp)
return nil, errors.Wrap(err, "addNetif")
}
func (h *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential,
mac string, vlanId int, wire string, ipAddr string,
rate int, nicType compute.TNicType, index int, linkUp tristate.TriState, mtu int16,
reset bool, strInterface *string, strBridge *string,
reserve bool, requireDesignatedIp bool,
) error {
var sw *SWire
if len(wire) > 0 {
iWire, err := WireManager.FetchByIdOrName(ctx, userCred, wire)
if err != nil {
if err == sql.ErrNoRows {
return httperrors.NewResourceNotFoundError2(WireManager.Keyword(), wire)
} else {
return httperrors.NewInternalServerError("find Wire %s error: %s", wire, err)
}
}
sw = iWire.(*SWire)
if len(ipAddr) > 0 {
iIpAddr, err := netutils.NewIPV4Addr(ipAddr)
if err != nil {
return httperrors.NewInputParameterError("invalid ipaddr %s", ipAddr)
}
findAddr := false
swNets, err := sw.getNetworks(ctx, userCred, userCred, NetworkManager.AllowScope(userCred))
if err != nil {
return httperrors.NewInputParameterError("no networks on wire %s", wire)
}
for i := range swNets {
if swNets[i].IsAddressInRange(iIpAddr) {
findAddr = true
break
}
}
if !findAddr {
return httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire)
}
}
} else if len(ipAddr) > 0 && len(wire) == 0 {
ipWire, err := WireManager.GetOnPremiseWireOfIp(ipAddr)
if err != nil {
return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr)
}
sw = ipWire
}
netif, err := NetInterfaceManager.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 {
err = h.EnableNetif(ctx, userCred, netif, "", ipAddr, "", "", reserve, requireDesignatedIp)
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)
err = h.EnableNetif(ctx, userCred, netif, input.NetworkId, input.IpAddr, input.AllocDir, input.NetType, reserve, requireDesignatedIp)
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, allocDir string, netType string, reserve, requireDesignatedIp bool) error {
bn := netif.GetHostNetwork()
if bn != nil {
log.Debugf("Netif has been attach2network? %s", jsonutils.Marshal(bn))
return nil
}
var net *SNetwork
var err error
if len(ipAddr) > 0 {
net, err = netif.GetCandidateNetworkForIp(ctx, userCred, userCred, NetworkManager.AllowScope(userCred), ipAddr)
if net != nil {
log.Infof("find network %s for ip %s", net.GetName(), ipAddr)
} else if requireDesignatedIp {
log.Errorf("Cannot allocate IP %s, not reachable", ipAddr)
return fmt.Errorf("Cannot allocate IP %s, not reachable", ipAddr)
} else {
// the ipaddr is not usable, should be reset to empty
ipAddr = ""
}
}
wire := netif.GetWire()
if wire == nil {
return fmt.Errorf("No wire attached")
}
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 []string
if len(netType) > 0 && netType != api.NETWORK_TYPE_BAREMETAL {
netTypes = []string{netType, api.NETWORK_TYPE_BAREMETAL}
} else {
netTypes = []string{api.NETWORK_TYPE_BAREMETAL}
}
net, err = wire.GetCandidatePrivateNetwork(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 network found")
}
}
}
} 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,
}
bn, err = h.Attach2Network(ctx, userCred, attachOpt)
if err != nil {
return errors.Wrap(err, "hh.Attach2Network")
}
switch netif.NicType {
case api.NIC_TYPE_IPMI:
err = h.setIpmiIp(userCred, bn.IpAddr)
if err != nil {
return errors.Wrap(err, "setIpmiIp")
}
case api.NIC_TYPE_ADMIN:
err = h.setAccessIp(userCred, bn.IpAddr)
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 string
if bn != nil {
ipAddr = bn.IpAddr
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 {
err = hh.setIpmiIp(userCred, "")
}
case api.NIC_TYPE_ADMIN:
if ipAddr == hh.AccessIp {
err = hh.setAccessIp(userCred, "")
}
}
return err
}
type hostAttachNetworkOption struct {
netif *SNetInterface
net *SNetwork
ipAddr string
allocDir string
reserved bool
requireDesignatedIp bool
}
func (hh *SHost) IsIpAddrWithinConvertedGuest(ctx context.Context, userCred mcclient.TokenCredential, ipAddr 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 findNic.IpAddr != ipAddr {
return httperrors.NewNotAcceptableError("Guest nic ip addr %s not equal %s", findNic.IpAddr, ipAddr)
}
return nil
}
func (hh *SHost) Attach2Network(
ctx context.Context,
userCred mcclient.TokenCredential,
opt *hostAttachNetworkOption,
) (*SHostnetwork, error) {
netif := opt.netif
net := opt.net
ipAddr := opt.ipAddr
allocDir := opt.allocDir
reserved := opt.reserved
requireDesignatedIp := opt.requireDesignatedIp
lockman.LockObject(ctx, net)
defer lockman.ReleaseObject(ctx, net)
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.GetFreeIP")
}
if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp {
return nil, fmt.Errorf("IP address %s is occupied, get %s instead", ipAddr, freeIp)
}
bn := &SHostnetwork{}
bn.BaremetalId = hh.Id
bn.SetModelManager(HostnetworkManager, bn)
bn.NetworkId = net.Id
bn.IpAddr = freeIp
bn.MacAddr = netif.Mac
err = HostnetworkManager.TableSpec().Insert(ctx, bn)
if err != nil {
return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert")
}
db.OpsLog.LogAttachEvent(ctx, hh, net, userCred, jsonutils.NewString(freeIp))
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 := GetHostDriver(hostType)
if driver == nil {
return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", hostType)
}
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 := hh.GetDriverWithDefault()
if driver == nil {
return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", hh.HostType)
}
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)
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) GetDriverWithDefault() IHostDriver {
hostType := hh.HostType
if len(hostType) == 0 {
hostType = api.HOST_TYPE_DEFAULT
}
return GetHostDriver(hostType)
}
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)
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)
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 hh.doAgentRequest(api.AgentTypeBaremetal, ctx, method, url, headers, body)
}
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 hh.doAgentRequest(api.AgentTypeEsxi, ctx, method, url, headers, body)
}
func (hh *SHost) GetAgent(at api.TAgentType) *SBaremetalagent {
agent := BaremetalagentManager.GetAgent(at, hh.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 (hh *SHost) doAgentRequest(agentType api.TAgentType, ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
agent := hh.GetAgent(agentType)
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")
}
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).
Equals("host_type", api.HOST_TYPE_HYPERVISOR)
q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("last_ping_at")),
sqlchemy.LT(q.Field("last_ping_at"), deadline)))
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
}
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)
db.OpsLog.LogEvent(host, db.ACT_HOST_DOWN, "", userCred)
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)
}
logclient.AddActionLogWithContext(ctx, host, logclient.ACT_OFFLINE, map[string]string{"reason": "host down"}, userCred, false)
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))
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
}
task.ScheduleRun(nil)
return nil
}
func (model *SHost) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
// make host default public
return model.SEnabledStatusInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
}
func (host *SHost) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) {
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")
}
}
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) InitializeData() error {
return manager.initHostname()
}
func (hh *SHost) PerformProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return hh.GetHostDriver().RequestProbeIsolatedDevices(ctx, userCred, hh, data)
}
func (hh *SHost) GetPinnedCpusetCores(ctx context.Context, userCred mcclient.TokenCredential) (map[string][]int, error) {
gsts, err := hh.GetGuests()
if err != nil {
return nil, errors.Wrap(err, "Get all guests")
}
ret := make(map[string][]int, 0)
for _, gst := range gsts {
pinned, err := gst.getPinnedCpusetCores(ctx, userCred)
if err != nil {
return nil, errors.Wrapf(err, "get guest %s pinned cpuset cores", gst.GetName())
}
ret[gst.GetId()] = pinned
}
return ret, nil
}
func (h *SHost) PerformSyncGuestNicTraffics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
guestTraffics, err := data.GetMap()
if err != nil {
return nil, errors.Wrap(err, "get guest traffics")
}
for guestId, nicTraffics := range guestTraffics {
nicTrafficMap := make(map[string]api.SNicTrafficRecord)
err = nicTraffics.Unmarshal(&nicTrafficMap)
if err != nil {
log.Errorf("failed unmarshal guest %s nic traffics %s", guestId, err)
continue
}
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[strconv.Itoa(int(gns[i].Index))]
if !ok {
continue
}
if err = gns[i].UpdateNicTrafficUsed(nicTraffic.RxTraffic, nicTraffic.TxTraffic); 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 (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,
})
}