Files
cloudpods/pkg/compute/models/hosts.go
2025-07-28 14:11:41 +08:00

6798 lines
217 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/fileutils"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/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"
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/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/baremetal"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/multicloud/esxi"
"yunion.io/x/onecloud/pkg/util/httputils"
"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
db.SHostNameValidatorManager
SZoneResourceBaseManager
SManagedResourceBaseManager
}
var HostManager *SHostManager
func init() {
HostManager = &SHostManager{
SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager(
SHost{},
"hosts_tbl",
"host",
"hosts",
),
}
HostManager.SetVirtualObject(HostManager)
HostManager.SetAlias("baremetal", "baremetals")
}
type SHost struct {
db.SEnabledStatusInfrasResourceBase
db.SExternalizedResourceBase
SZoneResourceBase `update:""`
SManagedResourceBase
SBillingResourceBase
// 机架
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:"false" 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" 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" list:"domain" update:"domain" create:"domain_optional"`
// 存储大小,单位Mb
StorageSize int `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 `nullable:"false" 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")
}
anyMac := query.AnyMac
if len(anyMac) > 0 {
anyMac := netutils.FormatMacAddr(anyMac)
if len(anyMac) == 0 {
return nil, httperrors.NewInputParameterError("invalid any_mac address")
}
netif, _ := NetInterfaceManager.FetchByMac(anyMac)
if netif != nil && len(netif.BaremetalId) > 0 {
q = q.Equals("id", netif.BaremetalId)
} else {
q = q.Equals("access_mac", anyMac)
}
}
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(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(nil, wireStr)
if wire == nil {
return nil, httperrors.NewResourceNotFoundError("Wire %s not found", wireStr)
}
hostwires := HostwireManager.Query().SubQuery()
scopeQuery := hostwires.Query(hostwires.Field("host_id")).Equals("wire_id", wire.GetId()).SubQuery()
q = q.In("id", scopeQuery)
}
storageStr := query.StorageId
if len(storageStr) > 0 {
storage, _ := StorageManager.FetchByIdOrName(nil, storageStr)
if storage == nil {
return nil, httperrors.NewResourceNotFoundError("Storage %s not found", storageStr)
}
hoststorages := HoststorageManager.Query().SubQuery()
scopeQuery := hoststorages.Query(hoststorages.Field("host_id")).Equals("storage_id", storage.GetId()).SubQuery()
notAttached := (query.StorageNotAttached != nil && *query.StorageNotAttached)
if !notAttached {
q = q.In("id", scopeQuery)
} else {
q = q.NotIn("id", scopeQuery)
}
}
hypervisorStr := query.Hypervisor
if len(hypervisorStr) > 0 {
hostType, ok := api.HYPERVISOR_HOSTTYPE[hypervisorStr]
if !ok {
return nil, httperrors.NewInputParameterError("not supported hypervisor %s", hypervisorStr)
}
q = q.Filter(sqlchemy.Equals(q.Field("host_type"), hostType))
}
usable := (query.Usable != nil && *query.Usable)
if usable {
hosts := HostManager.Query().SubQuery()
hostwires := HostwireManager.Query().SubQuery()
networks := NetworkManager.Query().SubQuery()
providers := usableCloudProviders().SubQuery()
hostQ1 := hosts.Query(hosts.Field("id"))
hostQ1 = hostQ1.Join(providers, sqlchemy.Equals(hosts.Field("manager_id"), providers.Field("id")))
hostQ1 = hostQ1.Join(hostwires, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")))
hostQ1 = hostQ1.Join(networks, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
hostQ1 = hostQ1.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE))
hostQ1 = hostQ1.Filter(sqlchemy.IsTrue(hosts.Field("enabled")))
hostQ2 := hosts.Query(hosts.Field("id"))
hostQ2 = hostQ2.Join(hostwires, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")))
hostQ2 = hostQ2.Join(networks, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
hostQ2 = hostQ2.Filter(sqlchemy.IsNullOrEmpty(hosts.Field("manager_id")))
hostQ2 = hostQ2.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE))
hostQ2 = hostQ2.Filter(sqlchemy.IsTrue(hosts.Field("enabled")))
q = q.Filter(sqlchemy.OR(
sqlchemy.In(q.Field("id"), hostQ1.SubQuery()),
sqlchemy.In(q.Field("id"), hostQ2.SubQuery()),
))
zones := ZoneManager.Query().SubQuery()
q = q.Join(zones, sqlchemy.Equals(q.Field("zone_id"), zones.Field("id"))).
Filter(sqlchemy.Equals(zones.Field("status"), api.ZONE_ENABLE))
q = q.In("status", []string{api.HOST_STATUS_RUNNING, api.HOST_STATUS_READY})
q = q.Filter(
sqlchemy.OR(
sqlchemy.AND(
sqlchemy.NotEquals(q.Field("host_type"), api.HOST_TYPE_BAREMETAL),
sqlchemy.Equals(q.Field("host_status"), api.HOST_ONLINE),
),
sqlchemy.Equals(q.Field("host_type"), api.HOST_TYPE_BAREMETAL),
),
)
}
if query.IsEmpty != nil {
isEmpty := *query.IsEmpty
sq := GuestManager.Query("host_id").IsNotEmpty("host_id").GroupBy("host_id").SubQuery()
if isEmpty {
q = q.NotIn("id", sq)
} else {
q = q.In("id", sq)
}
}
if query.Baremetal != nil {
isBaremetal := *query.Baremetal
if isBaremetal {
q = q.Equals("host_type", api.HOST_TYPE_BAREMETAL)
} else {
q = q.NotEquals("host_type", api.HOST_TYPE_BAREMETAL)
}
}
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()
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)
hostwires := HostwireManager.Query().SubQuery()
scopeQuery := hostwires.Query(hostwires.Field("host_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 (self *SHost) IsArmHost() bool {
return self.CpuArchitecture == apis.OS_ARCH_AARCH64
}
func (self *SHost) GetZone() (*SZone, error) {
zone, err := ZoneManager.FetchById(self.ZoneId)
if err != nil {
return nil, err
}
return zone.(*SZone), nil
}
func (self *SHost) GetRegion() (*SCloudregion, error) {
zone, err := self.GetZone()
if err != nil {
return nil, err
}
return zone.GetRegion()
}
func (self *SHost) GetCpuCount() int {
if self.CpuReserved > 0 && self.CpuReserved < self.CpuCount {
return int(self.CpuCount - self.CpuReserved)
} else {
return int(self.CpuCount)
}
}
func (self *SHost) GetMemSize() int {
if self.MemReserved > 0 && self.MemReserved < self.MemSize {
return self.MemSize - self.MemReserved
} else {
return self.MemSize
}
}
func (self *SHost) GetMemoryOvercommitBound() float32 {
if self.MemCmtbound > 0 {
return self.MemCmtbound
}
return options.Options.DefaultMemoryOvercommitBound
}
func (self *SHost) GetVirtualMemorySize() float32 {
return float32(self.GetMemSize()) * self.GetMemoryOvercommitBound()
}
func (self *SHost) GetCPUOvercommitBound() float32 {
if self.CpuCmtbound > 0 {
return self.CpuCmtbound
}
return options.Options.DefaultCPUOvercommitBound
}
func (self *SHost) GetVirtualCPUCount() float32 {
return float32(self.GetCpuCount()) * self.GetCPUOvercommitBound()
}
/*func (manager *SHostManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return userCred.IsSystemAdmin()
}
func (manager *SHostManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return userCred.IsSystemAdmin()
}
func (self *SHost) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return userCred.IsSystemAdmin()
}
func (self *SHost) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
return userCred.IsSystemAdmin()
}
func (self *SHost) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
return userCred.IsSystemAdmin()
}*/
func (self *SHost) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
return self.validateDeleteCondition(ctx, false)
}
func (self *SHost) ValidatePurgeCondition(ctx context.Context) error {
return self.validateDeleteCondition(ctx, true)
}
func (self *SHost) validateDeleteCondition(ctx context.Context, purge bool) error {
if !purge && self.IsBaremetal && self.HostType != api.HOST_TYPE_BAREMETAL {
return httperrors.NewInvalidStatusError("Host is a converted baremetal, should be unconverted before delete")
}
if self.GetEnabled() {
return httperrors.NewInvalidStatusError("Host is not disabled")
}
cnt, err := self.GetGuestCount()
if err != nil {
return httperrors.NewInternalServerError("getGuestCount fail %s", err)
}
if cnt > 0 {
return httperrors.NewNotEmptyError("Not an empty host")
}
for _, hoststorage := range self.GetHoststorages() {
storage := hoststorage.GetStorage()
if storage != nil && storage.IsLocal() {
cnt, err := storage.GetDiskCount()
if err != nil {
return httperrors.NewInternalServerError("GetDiskCount fail %s", err)
}
if cnt > 0 {
return httperrors.NewNotEmptyError("Local host storage is not empty???")
}
}
}
return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx, nil)
}
func (self *SHost) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
log.Infof("Host delete do nothing")
return nil
}
func (self *SHost) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if self.IsBaremetal {
return self.StartDeleteBaremetalTask(ctx, userCred, "")
} else {
return self.RealDelete(ctx, userCred)
}
}
func (self *SHost) StartDeleteBaremetalTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalDeleteTask", self, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
DeleteResourceJointSchedtags(self, ctx, userCred)
IsolatedDeviceManager.DeleteDevicesByHost(ctx, userCred, self)
for _, hoststorage := range self.GetHoststorages() {
storage := hoststorage.GetStorage()
if storage != nil && storage.IsLocal() {
cnt, err := storage.GetDiskCount()
if err != nil {
return errors.Wrapf(err, "GetDiskCount")
}
if cnt > 0 {
return httperrors.NewNotEmptyError("Inconsistent: local storage is not empty???")
}
}
}
for _, hoststorage := range self.GetHoststorages() {
storage := hoststorage.GetStorage()
hoststorage.Delete(ctx, userCred)
if storage != nil && storage.IsLocal() {
storage.Delete(ctx, userCred)
}
}
for _, bn := range self.GetBaremetalnetworks() {
self.DeleteBaremetalnetwork(ctx, userCred, &bn, false)
}
for _, netif := range self.GetNetInterfaces() {
netif.Remove(ctx, userCred)
}
for _, hostwire := range self.GetHostwires() {
hostwire.Detach(ctx, userCred)
// hostwire.Delete(ctx, userCred) ???
}
baremetalStorage := self.GetBaremetalstorage()
if baremetalStorage != nil {
store := baremetalStorage.GetStorage()
baremetalStorage.Delete(ctx, userCred)
if store != nil {
store.Delete(ctx, userCred)
}
}
return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred)
}
func (self *SHost) GetHoststoragesQuery() *sqlchemy.SQuery {
return HoststorageManager.Query().Equals("host_id", self.Id)
}
func (self *SHost) GetStorageCount() (int, error) {
return self.GetHoststoragesQuery().CountWithError()
}
func (self *SHost) GetHoststorages() []SHoststorage {
hoststorages := make([]SHoststorage, 0)
q := self.GetHoststoragesQuery()
err := db.FetchModelObjects(HoststorageManager, q, &hoststorages)
if err != nil && errors.Cause(err) != sql.ErrNoRows {
log.Errorf("GetHoststorages error %s", err)
return nil
}
return hoststorages
}
func (self *SHost) GetHoststorageOfId(storageId string) *SHoststorage {
hoststorage := SHoststorage{}
hoststorage.SetModelManager(HoststorageManager, &hoststorage)
err := self.GetHoststoragesQuery().Equals("storage_id", storageId).First(&hoststorage)
if err != nil {
if errors.Cause(err) != sql.ErrNoRows {
log.Errorf("GetHoststorageOfId fail %s", err)
}
return nil
}
return &hoststorage
}
func (self *SHost) GetHoststorageByExternalId(extId string) *SHoststorage {
hoststorage := SHoststorage{}
hoststorage.SetModelManager(HoststorageManager, &hoststorage)
hoststorages := HoststorageManager.Query().SubQuery()
storages := StorageManager.Query().SubQuery()
q := hoststorages.Query()
q = q.Join(storages, sqlchemy.Equals(hoststorages.Field("storage_id"), storages.Field("id")))
q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id))
q = q.Filter(sqlchemy.Equals(storages.Field("external_id"), extId))
err := q.First(&hoststorage)
if err != nil {
if errors.Cause(err) != sql.ErrNoRows {
log.Errorf("GetHoststorageByExternalId fail %s", err)
}
return nil
}
return &hoststorage
}
func (self *SHost) GetStorageByFilePath(path string) *SStorage {
hoststorages := self.GetHoststorages()
if hoststorages == nil {
return nil
}
for i := 0; i < len(hoststorages); i += 1 {
if len(hoststorages[i].MountPoint) > 0 && strings.HasPrefix(path, hoststorages[i].MountPoint) {
return hoststorages[i].GetStorage()
}
}
return nil
}
func (self *SHost) GetBaremetalstorage() *SHoststorage {
if !self.IsBaremetal {
return nil
}
hoststorages := HoststorageManager.Query().SubQuery()
storages := StorageManager.Query().SubQuery()
q := hoststorages.Query()
q = q.Join(storages, sqlchemy.AND(sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")),
sqlchemy.IsFalse(storages.Field("deleted"))))
q = q.Filter(sqlchemy.Equals(storages.Field("storage_type"), api.STORAGE_BAREMETAL))
q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id))
cnt, err := q.CountWithError()
if err != nil {
return nil
}
if cnt == 1 {
hs := SHoststorage{}
hs.SetModelManager(HoststorageManager, &hs)
err := q.First(&hs)
if err != nil {
log.Errorf("error %s", err)
return nil
}
return &hs
}
// log.Errorf("Cannot find baremetalstorage??")
return nil
}
func (self *SHost) SaveCleanUpdates(doUpdate func() error) (map[string]sqlchemy.SUpdateDiff, error) {
return self.saveUpdates(doUpdate, true)
}
func (self *SHost) SaveUpdates(doUpdate func() error) (map[string]sqlchemy.SUpdateDiff, error) {
return self.saveUpdates(doUpdate, false)
}
func (self *SHost) saveUpdates(doUpdate func() error, doSchedClean bool) (map[string]sqlchemy.SUpdateDiff, error) {
diff, err := db.Update(self, doUpdate)
if err != nil {
return nil, err
}
if doSchedClean {
self.ClearSchedDescCache()
}
return diff, nil
}
func (self *SHost) AllowPerformUpdateStorage(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject,
) bool {
return db.IsAdminAllowPerform(userCred, self, "update-storage")
}
func (self *SHost) PerformUpdateStorage(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject,
) (jsonutils.JSONObject, error) {
bs := self.GetBaremetalstorage()
capacity, _ := data.Int("capacity")
zoneId, _ := data.GetString("zone_id")
storageCacheId, _ := data.GetString("storagecache_id")
if bs == nil {
// 1. create storage
storage := SStorage{}
storage.Name = fmt.Sprintf("storage%s", self.GetName())
storage.Capacity = capacity
storage.StorageType = api.STORAGE_BAREMETAL
storage.MediumType = self.StorageType
storage.Cmtbound = 1.0
storage.Status = api.STORAGE_ONLINE
storage.Enabled = tristate.True
storage.ZoneId = zoneId
storage.StoragecacheId = storageCacheId
storage.DomainId = self.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 = self.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, self, &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 = self.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 (self *SHost) GetFetchUrl(disableHttps bool) string {
managerUrl, err := url.Parse(self.ManagerUri)
if err != nil {
log.Errorf("GetFetchUrl fail to parse url: %s", err)
}
if disableHttps {
managerUrl.Scheme = "http"
}
portStr := managerUrl.Port()
var port int
if len(portStr) > 0 {
port, _ = strconv.Atoi(portStr)
} else {
if managerUrl.Scheme == "https" {
port = 443
} else if managerUrl.Scheme == "http" {
port = 80
}
}
return fmt.Sprintf("%s://%s:%d", managerUrl.Scheme, strings.Split(managerUrl.Host, ":")[0], port+40000)
}
func (self *SHost) GetAttachedEnabledHostStorages(storageType []string) []SStorage {
return self._getAttachedStorages(tristate.False, tristate.True, storageType)
}
func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled tristate.TriState, storageType []string) []SStorage {
storages := StorageManager.Query().SubQuery()
hoststorages := HoststorageManager.Query().SubQuery()
q := storages.Query()
q = q.Join(hoststorages, sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")))
if enabled.IsTrue() {
q = q.IsTrue("enabled")
} else if enabled.IsFalse() {
q = q.IsFalse("enabled")
}
if isBaremetal.IsTrue() {
q = q.Equals("storage_type", api.STORAGE_BAREMETAL)
} else if isBaremetal.IsFalse() {
q = q.NotEquals("storage_type", api.STORAGE_BAREMETAL)
}
if len(storageType) > 0 {
q = q.In("storage_type", storageType)
}
q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id))
ret := make([]SStorage, 0)
err := db.FetchModelObjects(StorageManager, q, &ret)
if err != nil {
log.Errorf("GetAttachedStorages fail %s", err)
return nil
}
return ret
}
func (self *SHost) SyncAttachedStorageStatus() {
storages := self.GetAttachedEnabledHostStorages(nil)
if storages != nil {
for _, storage := range storages {
storage.SyncStatusWithHosts()
}
self.ClearSchedDescCache()
}
}
func (self *SHostManager) IsNewNameUnique(name string, userCred mcclient.TokenCredential, kwargs *jsonutils.JSONDict) (bool, error) {
q := self.Query().Equals("name", name)
if kwargs != nil && kwargs.Contains("zone_id") {
zoneId, _ := kwargs.GetString("zone_id")
q.Equals("zone_id", zoneId)
}
cnt, err := q.CountWithError()
if err != nil {
return false, err
}
return cnt == 0, nil
}
func (self *SHostManager) AllowGetPropertyBmStartRegisterScript(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return true
}
func (self *SHostManager) GetPropertyBmStartRegisterScript(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
regionUri, err := auth.GetPublicServiceURL("compute_v2", options.Options.Region, "")
if err != nil {
return nil, err
}
var script string
script += fmt.Sprintf("curl -k -fsSL -H 'X-Auth-Token: %s' %s/misc/bm-prepare-script", userCred.GetTokenString(), regionUri)
res := jsonutils.NewDict()
res.Add(jsonutils.NewString(script), "script")
return res, nil
}
func (self *SHostManager) AllowGetPropertyNodeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) bool {
return userCred.HasSystemAdminPrivilege()
}
func (self *SHostManager) GetPropertyNodeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) (jsonutils.JSONObject, error) {
hosts := self.Query().SubQuery()
q := hosts.Query(hosts.Field("host_type"), sqlchemy.SUM("node_count_total", hosts.Field("node_count")))
return self.getCount(ctx, userCred, q, query)
}
func (self *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(self, q, userCred, query.Filter, filterAny)
if err != nil {
return nil, err
}
q, err = self.ListItemFilter(ctx, q, userCred, query)
if err != nil {
return nil, err
}
rows, err := q.GroupBy("host_type").Rows()
if err != nil {
return nil, err
}
ret := jsonutils.NewDict()
defer rows.Close()
for rows.Next() {
var hostType string
var count int64
err = rows.Scan(&hostType, &count)
if err != nil {
log.Errorf("getCount scan err: %v", err)
return ret, nil
}
ret.Add(jsonutils.NewInt(count), hostType)
}
return ret, nil
}
func (self *SHostManager) AllowGetPropertyHostTypeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) bool {
return userCred.HasSystemAdminPrivilege()
}
func (self *SHostManager) GetPropertyHostTypeCount(ctx context.Context, userCred mcclient.TokenCredential, query api.HostListInput) (jsonutils.JSONObject, error) {
hosts := self.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"))
return self.getCount(ctx, userCred, q, query)
}
func (self *SHostManager) ClearAllSchedDescCache() error {
return self.ClearSchedDescSessionCache("", "")
}
func (self *SHostManager) ClearSchedDescCache(hostId string) error {
return self.ClearSchedDescSessionCache(hostId, "")
}
func (self *SHostManager) ClearSchedDescSessionCache(hostId, sessionId string) error {
s := auth.GetAdminSession(context.Background(), options.Options.Region, "")
return modules.SchedManager.CleanCache(s, hostId, sessionId, false)
}
func (self *SHost) ClearSchedDescCache() error {
return self.ClearSchedDescSessionCache("")
}
func (self *SHost) ClearSchedDescSessionCache(sessionId string) error {
return HostManager.ClearSchedDescSessionCache(self.Id, sessionId)
}
// sync clear sched desc on scheduler side
func (self *SHostManager) SyncClearSchedDescSessionCache(hostId, sessionId string) error {
s := auth.GetAdminSession(context.Background(), options.Options.Region, "")
return modules.SchedManager.CleanCache(s, hostId, sessionId, true)
}
func (self *SHost) SyncCleanSchedDescCache() error {
return self.SyncClearSchedDescSessionCache("")
}
func (self *SHost) SyncClearSchedDescSessionCache(sessionId string) error {
return HostManager.SyncClearSchedDescSessionCache(self.Id, sessionId)
}
func (self *SHost) AllowGetDetailsSpec(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowGetSpec(userCred, self, "spec")
}
func (self *SHost) GetDetailsSpec(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return GetModelSpec(HostManager, self)
}
func (man *SHostManager) GetSpecShouldCheckStatus(query *jsonutils.JSONDict) (bool, error) {
statusCheck := true
if query.Contains("is_empty") {
isEmpty, err := query.Bool("is_empty")
if err != nil {
return statusCheck, err
}
if !isEmpty {
statusCheck = false
}
}
return statusCheck, nil
}
func (self *SHost) GetSpec(statusCheck bool) *jsonutils.JSONDict {
if statusCheck {
if !self.GetEnabled() {
return nil
}
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_RUNNING, api.BAREMETAL_READY}) || self.GetBaremetalServer() != nil || self.IsMaintenance {
return nil
}
if self.MemSize == 0 || self.CpuCount == 0 {
return nil
}
if self.ResourceType == api.HostResourceTypePrepaidRecycle {
cnt, err := self.GetGuestCount()
if err != nil {
return nil
}
if cnt > 0 {
// occupied
return nil
}
}
if len(self.ManagerId) > 0 {
providerObj, _ := CloudproviderManager.FetchById(self.ManagerId)
if providerObj == nil {
return nil
}
provider := providerObj.(*SCloudprovider)
if !provider.IsAvailable() {
return nil
}
}
}
spec := self.GetHardwareSpecification()
specInfo := new(api.HostSpec)
if err := spec.Unmarshal(specInfo); err != nil {
return spec
}
nifs := self.GetNetInterfaces()
var nicCount int
for _, nif := range nifs {
if nif.NicType != api.NIC_TYPE_IPMI {
nicCount++
}
}
specInfo.NicCount = nicCount
var manufacture string
var model string
if self.SysInfo != nil {
manufacture, _ = self.SysInfo.GetString("manufacture")
model, _ = self.SysInfo.GetString("model")
}
if manufacture == "" {
manufacture = "Unknown"
}
if model == "" {
model = "Unknown"
}
specInfo.Manufacture = strings.ReplaceAll(manufacture, " ", "_")
specInfo.Model = strings.ReplaceAll(model, " ", "_")
devices := IsolatedDeviceManager.FindByHost(self.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 (self *SHost) GetHardwareSpecification() *jsonutils.JSONDict {
spec := &api.HostSpec{
Cpu: int(self.CpuCount),
Mem: self.MemSize,
}
if self.StorageInfo != nil {
spec.Disk = GetDiskSpecV2(self.StorageInfo)
spec.Driver = self.StorageDriver
}
ret := spec.JSON(spec)
if self.StorageInfo != nil {
ret.Set("storage_info", self.StorageInfo)
}
return ret
}
type SStorageCapacity struct {
Capacity int64 `json:"capacity,omitzero"`
Used int64 `json:"used_capacity,omitzero"`
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 (self *SHost) GetAttachedLocalStorageCapacity() SStorageCapacity {
ret := SStorageCapacity{}
storages := self.GetAttachedEnabledHostStorages(api.HOST_STORAGE_LOCAL_TYPES)
for _, s := range storages {
ret.Add(s.getStorageCapacity())
}
return ret
}
func (self *SHost) GetAttachedLocalStorages() []SStorage {
return self.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 (self *SHost) GetLeastUsedStorage(backend string) *SStorage {
storages := self.GetAttachedEnabledHostStorages(nil)
if storages != nil {
return ChooseLeastUsedStorage(storages, backend)
}
return nil
}
func (self *SHost) GetWiresQuery() *sqlchemy.SQuery {
return HostwireManager.Query().Equals("host_id", self.Id)
}
func (self *SHost) GetWireCount() (int, error) {
return self.GetWiresQuery().CountWithError()
}
func (self *SHost) GetHostwires() []SHostwire {
hw := make([]SHostwire, 0)
q := self.GetWiresQuery()
err := db.FetchModelObjects(HostwireManager, q, &hw)
if err != nil {
log.Errorf("GetWires error %s", err)
return nil
}
return hw
}
func (self *SHost) getAttachedWires() []SWire {
wires := WireManager.Query().SubQuery()
hostwires := HostwireManager.Query().SubQuery()
q := wires.Query()
q = q.Join(hostwires, sqlchemy.AND(sqlchemy.IsFalse(hostwires.Field("deleted")),
sqlchemy.Equals(hostwires.Field("wire_id"), wires.Field("id"))))
q = q.Filter(sqlchemy.Equals(hostwires.Field("host_id"), self.Id))
ret := make([]SWire, 0)
err := db.FetchModelObjects(WireManager, q, &ret)
if err != nil {
log.Errorf("%s", err)
return nil
}
return ret
}
func (self *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 (self *SHost) GetMasterWire() *SWire {
wires := WireManager.Query().SubQuery()
hostwires := HostwireManager.Query().SubQuery()
q := wires.Query()
q = q.Join(hostwires, sqlchemy.AND(sqlchemy.Equals(hostwires.Field("wire_id"), wires.Field("id")),
sqlchemy.IsFalse(hostwires.Field("deleted"))))
q = q.Filter(sqlchemy.Equals(hostwires.Field("host_id"), self.Id))
q = q.Filter(sqlchemy.IsTrue(hostwires.Field("is_master")))
wire := SWire{}
wire.SetModelManager(WireManager, &wire)
err := q.First(&wire)
if err != nil {
log.Errorf("GetMasterWire fail %s", err)
return nil
}
return &wire
}
func (self *SHost) getHostwires() ([]SHostwire, error) {
hostwires := make([]SHostwire, 0)
q := self.GetWiresQuery()
err := db.FetchModelObjects(HostwireManager, q, &hostwires)
if err != nil {
return nil, err
}
return hostwires, nil
}
func (self *SHost) getHostwiresOfId(wireId string) []SHostwire {
hostwires := make([]SHostwire, 0)
q := self.GetWiresQuery().Equals("wire_id", wireId)
err := db.FetchModelObjects(HostwireManager, q, &hostwires)
if err != nil {
log.Errorf("getHostwiresOfId fail %s", err)
return nil
}
return hostwires
}
func (self *SHost) getHostwireOfIdAndMac(wireId string, mac string) *SHostwire {
hostwire := SHostwire{}
hostwire.SetModelManager(HostwireManager, &hostwire)
q := self.GetWiresQuery().Equals("wire_id", wireId)
q = q.Equals("mac_addr", mac)
err := q.First(&hostwire)
if err != nil {
log.Errorf("getHostwireOfIdAndMac fail %s", err)
return nil
}
return &hostwire
}
func (self *SHost) GetGuestsQuery() *sqlchemy.SQuery {
return GuestManager.Query().Equals("host_id", self.Id)
}
func (self *SHost) GetGuests() ([]SGuest, error) {
q := self.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 (self *SHost) GetKvmGuests() []SGuest {
q := GuestManager.Query().Equals("host_id", self.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 (self *SHost) GetGuestsMasterOnThisHost() []SGuest {
q := self.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 (self *SHost) GetGuestsBackupOnThisHost() []SGuest {
q := GuestManager.Query().Equals("backup_host_id", self.Id)
guests := make([]SGuest, 0)
err := db.FetchModelObjects(GuestManager, q, &guests)
if err != nil {
log.Errorf("GetGuests %s", err)
return nil
}
return guests
}
func (self *SHost) GetGuestCount() (int, error) {
q := self.GetGuestsQuery()
return q.CountWithError()
}
func (self *SHost) GetContainerCount(status []string) (int, error) {
q := self.GetGuestsQuery()
q = q.Filter(sqlchemy.Equals(q.Field("hypervisor"), api.HYPERVISOR_CONTAINER))
if len(status) > 0 {
q = q.In("status", status)
}
return q.CountWithError()
}
func (self *SHost) GetNonsystemGuestCount() (int, error) {
q := self.GetGuestsQuery()
q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("is_system")), sqlchemy.IsFalse(q.Field("is_system"))))
return q.CountWithError()
}
func (self *SHost) GetRunningGuestCount() (int, error) {
q := self.GetGuestsQuery()
q = q.In("status", api.VM_RUNNING_STATUS)
return q.CountWithError()
}
func (self *SHost) GetNotReadyGuestsMemorySize() (int, 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"), self.Id),
sqlchemy.Equals(q.Field("backup_host_id"), self.Id))
q = q.Filter(cond)
q = q.NotEquals("status", api.VM_READY)
stat := SHostGuestResourceUsage{}
err := q.First(&stat)
if err != nil {
return -1, err
}
return stat.GuestVmemSize, nil
}
func (self *SHost) GetRunningGuestMemorySize() int {
res := self.getGuestsResource(api.VM_RUNNING)
if res != nil {
return res.GuestVmemSize
}
return -1
}
func (self *SHost) GetBaremetalnetworksQuery() *sqlchemy.SQuery {
return HostnetworkManager.Query().Equals("baremetal_id", self.Id)
}
func (self *SHost) GetBaremetalnetworks() []SHostnetwork {
q := self.GetBaremetalnetworksQuery()
hns := make([]SHostnetwork, 0)
err := db.FetchModelObjects(HostnetworkManager, q, &hns)
if err != nil {
log.Errorf("GetBaremetalnetworks error: %s", err)
}
return hns
}
func (self *SHost) GetAttach2Network(netId string) *SHostnetwork {
q := self.GetBaremetalnetworksQuery()
netifs := NetInterfaceManager.Query().Equals("baremetal_id", self.Id)
netifs = netifs.Filter(sqlchemy.OR(
sqlchemy.IsNullOrEmpty(netifs.Field("nic_type")),
sqlchemy.NotEquals(netifs.Field("nic_type"), api.NIC_TYPE_IPMI),
))
netifsSub := netifs.SubQuery()
q = q.Join(netifsSub, sqlchemy.Equals(q.Field("mac_addr"), netifsSub.Field("mac")))
q = q.Equals("network_id", netId)
hn := SHostnetwork{}
hn.SetModelManager(HostnetworkManager, &hn)
err := q.First(&hn)
if err != nil {
log.Errorf("GetAttach2Network fail %s", err)
return nil
}
return &hn
}
func (self *SHost) GetNetInterfaces() []SNetInterface {
q := NetInterfaceManager.Query().Equals("baremetal_id", self.Id).Asc("index")
netifs := make([]SNetInterface, 0)
err := db.FetchModelObjects(NetInterfaceManager, q, &netifs)
if err != nil {
log.Errorf("GetNetInterfaces fail %s", err)
return nil
}
return netifs
}
func (self *SHost) GetAdminNetInterface() *SNetInterface {
netif := SNetInterface{}
netif.SetModelManager(NetInterfaceManager, &netif)
q := NetInterfaceManager.Query().Equals("baremetal_id", self.Id).Equals("nic_type", api.NIC_TYPE_ADMIN)
err := q.First(&netif)
if err != nil {
log.Errorf("GetAdminNetInterface fail %s", err)
return nil
}
return &netif
}
func (self *SHost) GetNetInterface(mac string) *SNetInterface {
netif, _ := NetInterfaceManager.FetchByMac(mac)
if netif != nil && netif.BaremetalId == self.Id {
return netif
}
return nil
}
func (self *SHost) DeleteBaremetalnetwork(ctx context.Context, userCred mcclient.TokenCredential, bn *SHostnetwork, reserve bool) {
net := bn.GetNetwork()
bn.Delete(ctx, userCred)
db.OpsLog.LogDetachEvent(ctx, self, net, userCred, nil)
if reserve && net != nil && len(bn.IpAddr) > 0 && regutils.MatchIP4Addr(bn.IpAddr) {
ReservedipManager.ReserveIP(userCred, net, bn.IpAddr, "Delete baremetalnetwork to reserve")
}
}
func (self *SHost) GetHostDriver() IHostDriver {
if !utils.IsInStringArray(self.HostType, api.HOST_TYPES) {
log.Fatalf("Unsupported host type %s", self.HostType)
}
return GetHostDriver(self.HostType)
}
func (manager *SHostManager) getHostsByZoneProvider(zone *SZone, provider *SCloudprovider) ([]SHost, error) {
hosts := make([]SHost, 0)
q := manager.Query()
if zone != nil {
q = q.Equals("zone_id", zone.Id)
}
if provider != nil {
q = q.Equals("manager_id", provider.Id)
}
// exclude prepaid_recycle fake hosts
q = q.NotEquals("resource_type", api.HostResourceTypePrepaidRecycle)
err := db.FetchModelObjects(manager, q, &hosts)
if err != nil {
log.Errorf("%s", err)
return nil, err
}
return hosts, nil
}
func (manager *SHostManager) SyncHosts(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, zone *SZone, hosts []cloudprovider.ICloudHost) ([]SHost, []cloudprovider.ICloudHost, compare.SyncResult) {
key := provider.Id
if zone != nil {
key = fmt.Sprintf("%s-%s", zone.Id, provider.Id)
}
lockman.LockRawObject(ctx, "hosts", key)
defer lockman.ReleaseRawObject(ctx, "hosts", key)
localHosts := make([]SHost, 0)
remoteHosts := make([]cloudprovider.ICloudHost, 0)
syncResult := compare.SyncResult{}
dbHosts, err := manager.getHostsByZoneProvider(zone, provider)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
removed := make([]SHost, 0)
commondb := make([]SHost, 0)
commonext := make([]cloudprovider.ICloudHost, 0)
added := make([]cloudprovider.ICloudHost, 0)
err = compare.CompareSets(dbHosts, hosts, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
for i := 0; i < len(removed); i += 1 {
if removed[i].IsPrepaidRecycleResource() {
continue
}
err = removed[i].syncRemoveCloudHost(ctx, userCred)
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
for i := 0; i < len(commondb); i += 1 {
err = commondb[i].syncWithCloudHost(ctx, userCred, commonext[i], provider)
if err != nil {
syncResult.UpdateError(err)
} else {
localHosts = append(localHosts, commondb[i])
remoteHosts = append(remoteHosts, commonext[i])
syncResult.Update()
}
}
for i := 0; i < len(added); i += 1 {
new, err := manager.NewFromCloudHost(ctx, userCred, added[i], provider, zone)
if err != nil {
syncResult.AddError(err)
} else {
localHosts = append(localHosts, *new)
remoteHosts = append(remoteHosts, added[i])
syncResult.Add()
}
}
return localHosts, remoteHosts, syncResult
}
func (self *SHost) syncRemoveCloudHost(ctx context.Context, userCred mcclient.TokenCredential) error {
lockman.LockObject(ctx, self)
defer lockman.ReleaseObject(ctx, self)
err := self.ValidatePurgeCondition(ctx)
if err != nil {
err = self.purge(ctx, userCred)
if err != nil {
return errors.Wrap(err, "purge")
}
} else {
err = self.RealDelete(ctx, userCred)
}
return err
}
func (self *SHost) syncWithCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost, provider *SCloudprovider) error {
diff, err := db.UpdateWithLock(ctx, self, func() error {
// self.Name = extHost.GetName()
self.Status = extHost.GetStatus()
self.HostStatus = extHost.GetHostStatus()
self.AccessIp = extHost.GetAccessIp()
self.AccessMac = extHost.GetAccessMac()
self.SN = extHost.GetSN()
self.SysInfo = extHost.GetSysInfo()
self.CpuCount = extHost.GetCpuCount()
self.NodeCount = extHost.GetNodeCount()
cpuDesc := extHost.GetCpuDesc()
if len(cpuDesc) > 128 {
cpuDesc = cpuDesc[:128]
}
self.CpuDesc = cpuDesc
self.CpuMhz = extHost.GetCpuMhz()
self.MemSize = extHost.GetMemSizeMB()
self.StorageSize = extHost.GetStorageSizeMB()
self.StorageType = extHost.GetStorageType()
self.HostType = extHost.GetHostType()
self.OvnVersion = extHost.GetOvnVersion()
if cpuCmt := extHost.GetCpuCmtbound(); cpuCmt > 0 {
self.CpuCmtbound = cpuCmt
}
if memCmt := extHost.GetMemCmtbound(); memCmt > 0 {
self.MemCmtbound = memCmt
}
if reservedMem := extHost.GetReservedMemoryMb(); reservedMem > 0 {
self.MemReserved = reservedMem
}
self.IsEmulated = extHost.IsEmulated()
self.SetEnabled(extHost.GetEnabled())
self.IsMaintenance = extHost.GetIsMaintenance()
self.Version = extHost.GetVersion()
return nil
})
if err != nil {
log.Errorf("syncWithCloudZone error %s", err)
return err
}
db.OpsLog.LogSyncUpdate(self, diff, userCred)
if provider != nil {
SyncCloudDomain(userCred, self, provider.GetOwnerId())
self.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
}
if err := self.syncSchedtags(ctx, userCred, extHost); err != nil {
log.Errorf("syncSchedtags fail: %v", err)
return err
}
if err := HostManager.ClearSchedDescCache(self.Id); err != nil {
log.Errorf("ClearSchedDescCache for host %s error %v", self.Name, err)
}
return nil
}
func (self *SHost) syncWithCloudPrepaidVM(extVM cloudprovider.ICloudVM, host *SHost) error {
_, err := self.SaveUpdates(func() error {
self.CpuCount = extVM.GetVcpuCount()
self.MemSize = extVM.GetVmemSizeMB()
self.BillingType = extVM.GetBillingType()
self.ExpiredAt = extVM.GetExpiredAt()
self.ExternalId = host.ExternalId
return nil
})
if err != nil {
log.Errorf("syncWithCloudZone error %s", err)
}
if err := HostManager.ClearSchedDescCache(self.Id); err != nil {
log.Errorf("ClearSchedDescCache for host %s error %v", self.Name, err)
}
return err
}
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(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(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())
log.Errorf(msg)
return nil, fmt.Errorf(msg)
}
wire, err := WireManager.GetOnPremiseWireOfIp(accessIp)
if err != nil {
msg := fmt.Sprintf("fail to find wire for host %s %s: %s", extHost.GetName(), accessIp, err)
log.Errorf(msg)
return nil, fmt.Errorf(msg)
}
izone, _ = wire.GetZone()
}
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 reservedMem := extHost.GetReservedMemoryMb(); reservedMem > 0 {
host.MemReserved = reservedMem
}
host.ManagerId = provider.Id
host.IsEmulated = extHost.IsEmulated()
host.IsMaintenance = extHost.GetIsMaintenance()
host.Version = extHost.GetVersion()
host.IsPublic = false
host.PublicScope = string(rbacutils.ScopeNone)
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 (self *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.TokenCredential, storages []cloudprovider.ICloudStorage, provider *SCloudprovider) ([]SStorage, []cloudprovider.ICloudStorage, compare.SyncResult) {
lockman.LockRawObject(ctx, "storages", self.Id)
defer lockman.ReleaseRawObject(ctx, "storages", self.Id)
localStorages := make([]SStorage, 0)
remoteStorages := make([]cloudprovider.ICloudStorage, 0)
syncResult := compare.SyncResult{}
dbStorages := make([]SStorage, 0)
hostStorages := self.GetHoststorages()
for i := 0; i < len(hostStorages); i += 1 {
storage := hostStorages[i].GetStorage()
if storage == nil {
hostStorages[i].Delete(ctx, userCred)
} else {
dbStorages = append(dbStorages, *storage)
}
}
// dbStorages := self._getAttachedStorages(tristate.None, tristate.None)
removed := make([]SStorage, 0)
commondb := make([]SStorage, 0)
commonext := make([]cloudprovider.ICloudStorage, 0)
added := make([]cloudprovider.ICloudStorage, 0)
err := compare.CompareSets(dbStorages, storages, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
for i := 0; i < len(removed); i += 1 {
log.Infof("host %s not connected with %s any more, to detach...", self.Id, removed[i].Id)
err := self.syncRemoveCloudHostStorage(ctx, userCred, &removed[i])
if errors.Cause(err) == ErrStorageInUse && removed[i].StorageType == api.STORAGE_LOCAL {
removed[i].SetStatus(userCred, api.STORAGE_OFFLINE, "the only host used this local storage has detached")
// prevent generating a delete error for syncResult
continue
}
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
for i := 0; i < len(commondb); i += 1 {
log.Infof("host %s is still connected with %s, to update ...", self.Id, commondb[i].Id)
err := self.syncWithCloudHostStorage(ctx, userCred, &commondb[i], commonext[i], provider)
if err != nil {
syncResult.UpdateError(err)
} else {
localStorages = append(localStorages, commondb[i])
remoteStorages = append(remoteStorages, commonext[i])
syncResult.Update()
}
}
for i := 0; i < len(added); i += 1 {
log.Infof("host %s is found connected with %s, to add ...", self.Id, added[i].GetId())
local, err := self.newCloudHostStorage(ctx, userCred, added[i], provider)
if err != nil {
syncResult.AddError(err)
} else {
localStorages = append(localStorages, *local)
remoteStorages = append(remoteStorages, added[i])
syncResult.Add()
}
}
return localStorages, remoteStorages, syncResult
}
func (self *SHost) syncRemoveCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, localStorage *SStorage) error {
hs := self.GetHoststorageOfId(localStorage.Id)
err := hs.ValidateDeleteCondition(ctx, nil)
if err == nil {
log.Errorf("sync remove hoststorage fail: %s", err)
err = hs.Detach(ctx, userCred)
} else {
}
return err
}
func (self *SHost) syncWithCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, localStorage *SStorage, extStorage cloudprovider.ICloudStorage, provider *SCloudprovider) error {
// do nothing
hs := self.GetHoststorageOfId(localStorage.Id)
err := hs.syncWithCloudHostStorage(userCred, extStorage)
if err != nil {
return err
}
s := hs.GetStorage()
err = s.syncWithCloudStorage(ctx, userCred, extStorage, provider)
return err
}
func (self *SHost) isAttach2Storage(storage *SStorage) bool {
hs := self.GetHoststorageOfId(storage.Id)
return hs != nil
}
func (self *SHost) Attach2Storage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, mountPoint string) error {
if self.isAttach2Storage(storage) {
return nil
}
hs := SHoststorage{}
hs.SetModelManager(HoststorageManager, &hs)
hs.StorageId = storage.Id
hs.HostId = self.Id
hs.MountPoint = mountPoint
err := HoststorageManager.TableSpec().Insert(ctx, &hs)
if err != nil {
return err
}
db.OpsLog.LogAttachEvent(ctx, self, storage, userCred, nil)
return nil
}
func (self *SHost) newCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage, provider *SCloudprovider) (*SStorage, error) {
storageObj, err := db.FetchByExternalIdAndManagerId(StorageManager, extStorage.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", provider.Id)
})
if err != nil {
if err == sql.ErrNoRows {
// no cloud storage found, this may happen for on-premise host
// create the storage right now
zone, _ := self.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 = self.Attach2Storage(ctx, userCred, storage, extStorage.GetMountPoint())
return storage, err
}
func (self *SHost) SyncHostWires(ctx context.Context, userCred mcclient.TokenCredential, wires []cloudprovider.ICloudWire) compare.SyncResult {
lockman.LockRawObject(ctx, "wires", self.Id)
defer lockman.ReleaseRawObject(ctx, "wires", self.Id)
syncResult := compare.SyncResult{}
dbWires := make([]SWire, 0)
hostWires := self.GetHostwires()
for i := 0; i < len(hostWires); i += 1 {
wire := hostWires[i].GetWire()
if wire == nil {
hostWires[i].Delete(ctx, userCred)
} else {
dbWires = append(dbWires, *wire)
}
}
// dbWires := self.getAttachedWires()
removed := make([]SWire, 0)
commondb := make([]SWire, 0)
commonext := make([]cloudprovider.ICloudWire, 0)
added := make([]cloudprovider.ICloudWire, 0)
err := compare.CompareSets(dbWires, wires, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return syncResult
}
for i := 0; i < len(removed); i += 1 {
log.Infof("host %s not connected with %s any more, to detach...", self.Id, removed[i].Id)
err := self.syncRemoveCloudHostWire(ctx, userCred, &removed[i])
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
for i := 0; i < len(commondb); i += 1 {
log.Infof("host %s is still connected with %s, to update...", self.Id, commondb[i].Id)
err := self.syncWithCloudHostWire(commonext[i])
if err != nil {
syncResult.UpdateError(err)
} else {
syncResult.Update()
}
}
for i := 0; i < len(added); i += 1 {
log.Infof("host %s is found connected with %s, to add...", self.Id, added[i].GetId())
err := self.newCloudHostWire(ctx, userCred, added[i])
if err != nil {
syncResult.AddError(err)
} else {
syncResult.Add()
}
}
return syncResult
}
func (self *SHost) syncRemoveCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, localwire *SWire) error {
hws := self.getHostwiresOfId(localwire.Id)
for i := range hws {
err := hws[i].Detach(ctx, userCred)
if err != nil {
return err
}
}
return nil
}
func (self *SHost) syncWithCloudHostWire(extWire cloudprovider.ICloudWire) error {
// do nothing
return nil
}
func (self *SHost) Attach2Wire(ctx context.Context, userCred mcclient.TokenCredential, wire *SWire) error {
hs := SHostwire{}
hs.SetModelManager(HostwireManager, &hs)
hs.WireId = wire.Id
hs.HostId = self.Id
err := HostwireManager.TableSpec().Insert(ctx, &hs)
if err != nil {
return err
}
db.OpsLog.LogAttachEvent(ctx, self, wire, userCred, nil)
return nil
}
func (self *SHost) newCloudHostWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire) error {
wireObj, err := db.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"), self.ManagerId))
})
if err != nil {
log.Errorf("%s", err)
return nil
}
wire := wireObj.(*SWire)
err = self.Attach2Wire(ctx, userCred, wire)
return err
}
type SGuestSyncResult struct {
Local *SGuest
Remote cloudprovider.ICloudVM
IsNew bool
}
func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCredential, iprovider cloudprovider.ICloudProvider, vms []cloudprovider.ICloudVM, syncOwnerId mcclient.IIdentityProvider) ([]SGuestSyncResult, compare.SyncResult) {
lockman.LockRawObject(ctx, "guests", self.Id)
defer lockman.ReleaseRawObject(ctx, "guests", self.Id)
syncVMPairs := make([]SGuestSyncResult, 0)
syncResult := compare.SyncResult{}
dbVMs, err := self.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)
err = compare.CompareSets(dbVMs, vms, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return nil, syncResult
}
skipFunc := func(ext cloudprovider.ICloudVM) (bool, string) {
if len(options.Options.SkipServerBySysTagKeys) == 0 {
return false, ""
}
keys := strings.Split(options.Options.SkipServerBySysTagKeys, ",")
for key := range ext.GetSysTags() {
if utils.IsInStringArray(key, keys) {
return true, key
}
}
return false, ""
}
for i := 0; i < len(removed); i += 1 {
err := removed[i].syncRemoveCloudVM(ctx, userCred)
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
for i := 0; i < len(commondb); i += 1 {
skip, key := skipFunc(commonext[i])
if skip {
log.Infof("skip server %s(%s) sync for delete with system tag key: %s", commonext[i].GetName(), commonext[i].GetGlobalId(), key)
err := commondb[i].syncRemoveCloudVM(ctx, userCred)
if err != nil {
syncResult.DeleteError(err)
continue
}
syncResult.Delete()
continue
}
err := commondb[i].syncWithCloudVM(ctx, userCred, iprovider, self, 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 := skipFunc(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"), self.ManagerId))
})
if err != nil && err != sql.ErrNoRows {
log.Errorf("failed to found guest by externalId %s error: %v", added[i].GetGlobalId(), err)
continue
}
if vm != nil {
guest := vm.(*SGuest)
ihost := added[i].GetIHost()
if ihost == nil {
log.Errorf("failed to found ihost from vm %s", added[i].GetGlobalId())
continue
}
_host, err := db.FetchByExternalIdAndManagerId(HostManager, ihost.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", self.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, self, added[i], syncOwnerId)
if err != nil {
syncResult.AddError(err)
} else {
syncVMPair := SGuestSyncResult{
Local: new,
Remote: added[i],
IsNew: true,
}
syncVMPairs = append(syncVMPairs, syncVMPair)
syncResult.Add()
}
}
return syncVMPairs, syncResult
}
func (self *SHost) getNetworkOfIPOnHost(ipAddr string) (*SNetwork, error) {
netInterfaces := self.GetNetInterfaces()
for _, netInterface := range netInterfaces {
network, err := netInterface.GetCandidateNetworkForIp(nil, rbacutils.ScopeNone, ipAddr)
if err == nil && network != nil {
return network, nil
}
}
return nil, fmt.Errorf("IP %s not reachable on this host", ipAddr)
}
func (self *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) ([]SNetInterface, *SNetwork, 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 := self.GetNetInterfaces()
for i := 0; i < len(netifs); i++ {
if !netifs[i].IsUsableServernic() {
continue
}
if netifs[i].WireId == net.WireId {
matchNetIfs = append(matchNetIfs, netifs[i])
// return &netifs[i], net
}
}
if len(matchNetIfs) > 0 {
return matchNetIfs, net, nil
}
return nil, nil, errors.Errorf("not found matched netinterface by net %q wire %q", net.GetName(), net.WireId)
}
func (self *SHost) GetNetworkWithId(netId string, reserved bool) (*SNetwork, error) {
var q1, q2 *sqlchemy.SQuery
{
networks := NetworkManager.Query()
hostwires := HostwireManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
q1 = networks
q1 = q1.Join(hostwires, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
q1 = q1.Join(hosts, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")))
q1 = q1.Filter(sqlchemy.Equals(networks.Field("id"), netId))
q1 = q1.Filter(sqlchemy.Equals(hosts.Field("id"), self.Id))
}
{
networks := NetworkManager.Query()
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
regions := CloudregionManager.Query().SubQuery()
q2 = networks
q2 = q2.Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id")))
q2 = q2.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id")))
q2 = q2.Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id")))
q2 = q2.Filter(sqlchemy.Equals(networks.Field("id"), netId))
q2 = q2.Filter(
sqlchemy.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)),
),
),
)
}
q := sqlchemy.Union(q1, q2).Query()
net := SNetwork{}
net.SetModelManager(NetworkManager, &net)
err := q.First(&net)
if err != nil {
return nil, err
}
if reserved {
return &net, nil
}
freeCnt, err := net.getFreeAddressCount()
if err != nil {
return nil, err
}
if freeCnt > 0 {
return &net, nil
}
return nil, fmt.Errorf("No IP address")
}
func (manager *SHostManager) FetchHostById(hostId string) *SHost {
host := SHost{}
host.SetModelManager(manager, &host)
err := manager.Query().Equals("id", hostId).First(&host)
if err != nil {
log.Errorf("fetchHostById fail %s", err)
return nil
} else {
return &host
}
}
func (manager *SHostManager) 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(
userCred mcclient.IIdentityProvider,
scope rbacutils.TRbacScope,
rangeObjs []db.IStandaloneModel,
hostStatus, status string,
hostTypes []string,
resourceTypes []string,
providers []string, brands []string, cloudEnv string,
enabled, isBaremetal tristate.TriState,
) *sqlchemy.SQuery {
hosts := manager.Query().SubQuery()
q := hosts.Query(
hosts.Field("mem_size"),
hosts.Field("mem_reserved"),
hosts.Field("mem_cmtbound"),
hosts.Field("cpu_count"),
hosts.Field("cpu_reserved"),
hosts.Field("cpu_cmtbound"),
hosts.Field("storage_size"),
)
if scope != rbacutils.ScopeSystem && userCred != nil {
q = q.Filter(sqlchemy.Equals(hosts.Field("domain_id"), userCred.GetProjectDomainId()))
}
if len(status) > 0 {
q = q.Filter(sqlchemy.Equals(hosts.Field("status"), status))
}
if len(hostStatus) > 0 {
q = q.Filter(sqlchemy.Equals(hosts.Field("host_status"), hostStatus))
}
if !enabled.IsNone() {
cond := sqlchemy.IsFalse
if enabled.Bool() {
cond = sqlchemy.IsTrue
}
q = q.Filter(cond(hosts.Field("enabled")))
}
if !isBaremetal.IsNone() {
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),
))
}
}
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(
userCred mcclient.IIdentityProvider,
scope rbacutils.TRbacScope,
rangeObjs []db.IStandaloneModel,
hostStatus, status string,
hostTypes []string,
resourceTypes []string,
providers []string, brands []string, cloudEnv string,
enabled, isBaremetal tristate.TriState,
) HostsCountStat {
return manager.calculateCount(
manager.totalCountQ(
userCred,
scope,
rangeObjs,
hostStatus,
status,
hostTypes,
resourceTypes,
providers,
brands,
cloudEnv,
enabled,
isBaremetal,
),
)
}
/*
func (self *SHost) GetIZone() (cloudprovider.ICloudZone, error) {
provider, err := self.GetCloudProvider()
if err != nil {
return nil, fmt.Errorf("No cloudprovider for host: %s", err)
}
zone, _ := self.GetZone()
if zone == nil {
return nil, fmt.Errorf("no zone for host???")
}
region, _ := zone.GetRegion()
if region == nil {
return nil, fmt.Errorf("No region for zone???")
}
iregion, err := provider.GetIRegionById(region.ExternalId)
if err != nil {
return nil, fmt.Errorf("fail to find iregion by id %s", err)
}
izone, err := iregion.GetIZoneById(zone.ExternalId)
if err != nil {
return nil, fmt.Errorf("fail to find izone by id %s", err)
}
return izone, nil
}
*/
func (self *SHost) GetIHost() (cloudprovider.ICloudHost, error) {
host, _, err := self.GetIHostAndProvider()
return host, err
}
func (self *SHost) GetIHostAndProvider() (cloudprovider.ICloudHost, cloudprovider.ICloudProvider, error) {
provider, err := self.GetDriver()
if err != nil {
return nil, nil, fmt.Errorf("No cloudprovider for host: %s", err)
}
var iregion cloudprovider.ICloudRegion
if provider.GetFactory().IsOnPremise() {
iregion, err = provider.GetOnPremiseIRegion()
if err != nil {
return nil, nil, errors.Wrapf(err, "provider.GetOnPremiseIRegio")
}
} else {
region, err := self.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)
}
}
ihost, err := iregion.GetIHostById(self.ExternalId)
if err != nil {
return nil, nil, errors.Wrapf(err, "iregion.GetIHostById(%s)", self.ExternalId)
}
return ihost, provider, nil
}
func (self *SHost) GetIRegion() (cloudprovider.ICloudRegion, error) {
provider, err := self.GetDriver()
if err != nil {
return nil, fmt.Errorf("No cloudprovider for host %s: %s", self.Name, err)
}
region, _ := self.GetRegion()
if region == nil {
return nil, fmt.Errorf("failed to find host %s region info", self.Name)
}
iregion, err := provider.GetIRegionById(region.ExternalId)
if err != nil {
msg := fmt.Sprintf("fail to find iregion by id %s: %v", region.ExternalId, err)
return nil, fmt.Errorf(msg)
}
return iregion, nil
}
func (self *SHost) getDiskConfig() jsonutils.JSONObject {
bs := self.GetBaremetalstorage()
if bs != nil {
return bs.Config
}
return nil
}
func (self *SHost) GetBaremetalServer() *SGuest {
if !self.IsBaremetal {
return nil
}
guest := SGuest{}
guest.SetModelManager(GuestManager, &guest)
q := GuestManager.Query().Equals("host_id", self.Id).Equals("hypervisor", api.HOST_TYPE_BAREMETAL)
err := q.First(&guest)
if err != nil {
if err != sql.ErrNoRows {
log.Errorf("query fail %s", err)
}
return nil
}
return &guest
}
func (self *SHost) GetSchedtags() []SSchedtag {
return GetSchedtags(HostschedtagManager, self.Id)
}
type SHostGuestResourceUsage struct {
GuestCount int
GuestVcpuCount int
GuestVmemSize int
}
func (self *SHost) getGuestsResource(status string) *SHostGuestResourceUsage {
guests := GuestManager.Query().SubQuery()
q := guests.Query(sqlchemy.COUNT("guest_count"),
sqlchemy.SUM("guest_vcpu_count", guests.Field("vcpu_count")),
sqlchemy.SUM("guest_vmem_size", guests.Field("vmem_size")))
cond := sqlchemy.OR(sqlchemy.Equals(q.Field("host_id"), self.Id),
sqlchemy.Equals(q.Field("backup_host_id"), self.Id))
q = q.Filter(cond)
if len(status) > 0 {
q = q.Equals("status", status)
}
stat := SHostGuestResourceUsage{}
err := q.First(&stat)
if err != nil {
log.Errorf("%s", err)
return nil
}
return &stat
}
func fetchHostGuestResource(hostIds []string, status string) (map[string]SHostGuestResourceUsage, error) {
guests := GuestManager.Query()
cond := sqlchemy.OR(sqlchemy.In(guests.Field("host_id"), hostIds),
sqlchemy.In(guests.Field("backup_host_id"), hostIds))
guests = guests.Filter(cond)
if len(status) > 0 {
guests = guests.Equals("status", status)
}
sq := guests.SubQuery()
q := sq.Query(
sqlchemy.COUNT("id").Label("guest_count"),
sq.Field("host_id"),
sqlchemy.SUM("guest_vcpu_count", sq.Field("vcpu_count")),
sqlchemy.SUM("guest_vmem_size", sq.Field("vmem_size")),
).GroupBy(sq.Field("host_id"))
stat := []struct {
HostId string
SHostGuestResourceUsage
}{}
err := q.All(&stat)
if err != nil {
return nil, err
}
ret := map[string]SHostGuestResourceUsage{}
for i := range stat {
ret[stat[i].HostId] = stat[i].SHostGuestResourceUsage
}
return ret, nil
}
func fetchHostNics(hostIds []string) (map[string][]*types.SNic, error) {
nicQ := NetInterfaceManager.Query().In("baremetal_id", hostIds).SubQuery()
wires := WireManager.Query().SubQuery()
zones := ZoneManager.Query().SubQuery()
hn := HostnetworkManager.Query().SubQuery()
networks := NetworkManager.Query().SubQuery()
q := nicQ.Query(
nicQ.Field("mac"),
//nicQ.Field("vlan_id"),
nicQ.Field("baremetal_id"),
nicQ.Field("wire_id"),
nicQ.Field("rate"),
nicQ.Field("nic_type"),
nicQ.Field("index"),
nicQ.Field("link_up"),
//nicQ.Field("bridge"),
nicQ.Field("mtu"),
wires.Field("name").Label("wire"),
wires.Field("bandwidth"),
hn.Field("ip_addr"),
networks.Field("guest_gateway").Label("gateway"),
networks.Field("guest_dns").Label("dns"),
networks.Field("guest_domain").Label("domain"),
networks.Field("guest_ntp").Label("ntp"),
networks.Field("guest_ip_mask").Label("masklen"),
networks.Field("name").Label("net"),
networks.Field("id").Label("net_id"),
zones.Field("name").Label("zone"),
)
q = q.LeftJoin(wires, sqlchemy.Equals(wires.Field("id"), nicQ.Field("wire_id")))
q = q.LeftJoin(hn, sqlchemy.AND(
sqlchemy.Equals(nicQ.Field("baremetal_id"), hn.Field("baremetal_id")),
sqlchemy.Equals(nicQ.Field("mac"), hn.Field("mac_addr")),
//sqlchemy.Equals(nicQ.Field("vlan_id"), hn.Field("vlan_id")),
))
q = q.LeftJoin(networks, sqlchemy.Equals(hn.Field("network_id"), networks.Field("id")))
q = q.LeftJoin(zones, sqlchemy.Equals(wires.Field("zone_id"), zones.Field("id")))
nics := []struct {
types.SNic
BaremetalId string
Zone string
}{}
err := q.All(&nics)
if err != nil {
return nil, err
}
ret := map[string][]*types.SNic{}
for i := range nics {
nic := nics[i]
_, ok := ret[nic.BaremetalId]
if !ok {
ret[nic.BaremetalId] = []*types.SNic{}
}
if len(nic.Gateway) > 0 && !regutils.MatchIP4Addr(nic.Gateway) {
nic.Gateway = ""
}
if len(nic.Dns) == 0 && len(nic.Zone) > 0 {
srvs, _ := auth.GetDNSServers(options.Options.Region, nic.Zone)
if len(srvs) > 0 {
nic.Dns = strings.Join(srvs, ",")
} else {
nic.Dns = options.Options.DNSServer
}
}
if len(nic.Domain) == 0 {
nic.Domain = options.Options.DNSDomain
}
if len(nic.Ntp) == 0 && len(nic.Zone) > 0 {
srvs, _ := auth.GetNTPServers(options.Options.Region, nic.Zone)
if len(srvs) > 0 {
nic.Ntp = strings.Join(srvs, ",")
}
}
ret[nic.BaremetalId] = append(ret[nic.BaremetalId], &nic.SNic)
}
return ret, nil
}
func fetchHostStorages(hostIds []string) (map[string]*SStorageCapacity, error) {
hoststorages := HoststorageManager.Query().In("host_id", hostIds).SubQuery()
storageQ := StorageManager.Query().IsTrue("enabled").NotEquals("storage_type", api.STORAGE_BAREMETAL).In("storage_type", api.HOST_STORAGE_LOCAL_TYPES).SubQuery()
disk1 := DiskManager.Query().Equals("status", api.DISK_READY).SubQuery()
readySQ := disk1.Query(
sqlchemy.SUM("sum", disk1.Field("disk_size")).Label("used"),
disk1.Field("storage_id"),
).Equals("status", api.DISK_READY).GroupBy("storage_id").SubQuery()
disk2 := DiskManager.Query().NotEquals("status", api.DISK_READY).SubQuery()
wasteSQ := disk2.Query(
sqlchemy.SUM("sum", disk2.Field("disk_size")).Label("wasted"),
disk2.Field("storage_id"),
).NotEquals("status", api.DISK_READY).GroupBy("storage_id").SubQuery()
q := storageQ.Query(
storageQ.Field("id"),
storageQ.Field("capacity"),
storageQ.Field("reserved"),
hoststorages.Field("host_id"),
storageQ.Field("cmtbound"),
storageQ.Field("actual_capacity_used"),
readySQ.Field("used"),
wasteSQ.Field("wasted"),
)
q = q.Join(hoststorages, sqlchemy.Equals(q.Field("id"), hoststorages.Field("storage_id")))
q = q.LeftJoin(readySQ, sqlchemy.Equals(readySQ.Field("storage_id"), storageQ.Field("id")))
q = q.LeftJoin(wasteSQ, sqlchemy.Equals(wasteSQ.Field("storage_id"), storageQ.Field("id")))
values := []struct {
HostId string
Capacity int64
Reserved int64
Cmtbound float32
ActualCapacityUsed int64
Used int64
Wasted int64
}{}
err := q.All(&values)
if err != nil {
return nil, err
}
ret := map[string]*SStorageCapacity{}
for i := range values {
v := values[i]
_, ok := ret[v.HostId]
if !ok {
ret[v.HostId] = &SStorageCapacity{}
}
capa := SStorageCapacity{}
capa.Capacity = v.Capacity - v.Reserved
capa.Used = v.Used
capa.Wasted = v.Wasted
cmtbound := options.Options.DefaultStorageOvercommitBound
if v.Cmtbound > 0 {
cmtbound = v.Cmtbound
}
capa.VCapacity = int64(float32(capa.Capacity) * cmtbound)
capa.ActualUsed = v.ActualCapacityUsed
ret[v.HostId].Add(capa)
}
return ret, nil
}
func fetchHostSchedtags(hostIds []string) (map[string][]api.SchedtagShortDescDetails, error) {
schedtags := SchedtagManager.Query().SubQuery()
objschedtags := HostschedtagManager.Query().SubQuery()
q := schedtags.Query(
objschedtags.Field("host_id"),
schedtags.Field("id"),
schedtags.Field("name"),
schedtags.Field("default_strategy").Label("default"),
sqlchemy.NewStringField("schedtag").Label("res_name"),
)
q = q.Join(objschedtags, sqlchemy.AND(sqlchemy.Equals(objschedtags.Field("schedtag_id"), schedtags.Field("id")),
sqlchemy.IsFalse(objschedtags.Field("deleted"))))
q = q.Filter(sqlchemy.In(objschedtags.Field("host_id"), hostIds))
tags := []struct {
Id string
HostId string
Name string
ResName string
Default string
}{}
err := q.All(&tags)
if err != nil {
return nil, err
}
ret := map[string][]api.SchedtagShortDescDetails{}
for i := range tags {
_, ok := ret[tags[i].HostId]
if !ok {
ret[tags[i].HostId] = []api.SchedtagShortDescDetails{}
}
tag := api.SchedtagShortDescDetails{}
jsonutils.Update(&tag, tags[i])
ret[tags[i].HostId] = append(ret[tags[i].HostId], tag)
}
return ret, nil
}
/*
func (self *SHost) getMoreDetails(ctx context.Context, out api.HostDetails, showReason bool) api.HostDetails {
server := self.GetBaremetalServer()
if server != nil {
out.ServerId = server.Id
out.Server = server.Name
out.ServerPendingDeleted = server.PendingDeleted
if self.HostType == api.HOST_TYPE_BAREMETAL {
out.ServerIps = strings.Join(server.GetRealIPs(), ",")
}
}
nics := self.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(nicInfos)
out.NicInfo = nicInfos
}
out.Schedtags = GetSchedtagsDetailsToResourceV2(self, ctx)
var usage *SHostGuestResourceUsage
if options.Options.IgnoreNonrunningGuests {
usage = self.getGuestsResource(api.VM_RUNNING)
} else {
usage = self.getGuestsResource("")
}
if usage != nil {
out.CpuCommit = usage.GuestVcpuCount
out.MemCommit = usage.GuestVmemSize
}
containerCount, _ := self.GetContainerCount(nil)
runningContainerCount, _ := self.GetContainerCount(api.VM_RUNNING_STATUS)
guestCount, _ := self.GetGuestCount()
nonesysGuestCnt, _ := self.GetNonsystemGuestCount()
runningGuestCnt, _ := self.GetRunningGuestCount()
out.Guests = guestCount - containerCount
out.NonsystemGuests = nonesysGuestCnt - containerCount
out.RunningGuests = runningGuestCnt - runningContainerCount
totalCpu := self.GetCpuCount()
cpuCommitRate := 0.0
if totalCpu > 0 && usage.GuestVcpuCount > 0 {
cpuCommitRate = float64(usage.GuestVcpuCount) * 1.0 / float64(totalCpu)
}
out.CpuCommitRate = cpuCommitRate
totalMem := self.GetMemSize()
memCommitRate := 0.0
if totalMem > 0 && usage.GuestVmemSize > 0 {
memCommitRate = float64(usage.GuestVmemSize) * 1.0 / float64(totalMem)
}
out.MemCommitRate = memCommitRate
capa := self.GetAttachedLocalStorageCapacity()
out.Storage = capa.Capacity
out.StorageUsed = capa.Used
out.ActualStorageUsed = capa.ActualUsed
out.StorageWaste = capa.Wasted
out.StorageVirtual = capa.VCapacity
out.StorageFree = capa.GetFree()
out.StorageCommitRate = capa.GetCommitRate()
out.Spec = self.GetHardwareSpecification()
// custom cpu mem commit bound
out.CpuCommitBound = self.GetCPUOvercommitBound()
out.MemCommitBound = self.GetMemoryOvercommitBound()
// extra = self.SManagedResourceBase.getExtraDetails(ctx, extra)
out.IsPrepaidRecycle = false
if self.IsPrepaidRecycle() {
out.IsPrepaidRecycle = true
}
if self.IsBaremetal {
out.CanPrepare = true
err := self.canPrepare()
if err != nil {
out.CanPrepare = false
if showReason {
out.PrepareFailReason = err.Error()
}
}
}
if self.EnableHealthCheck && hostHealthChecker != nil {
out.AllowHealthCheck = true
}
if self.GetMetadata(api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN, nil) == "enable" {
out.AutoMigrateOnHostDown = true
}
if self.GetMetadata(api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN, nil) == "enable" {
out.AutoMigrateOnHostShutdown = true
}
if count, rs := self.GetReservedResourceForIsolatedDevice(); rs != nil {
out.ReservedResourceForGpu = *rs
out.IsolatedDeviceCount = count
}
return out
}
*/
func (self *SHost) GetReservedResourceForIsolatedDevice() (int, *api.IsolatedDeviceReservedResourceInput) {
if devs := IsolatedDeviceManager.FindByHost(self.Id); len(devs) == 0 {
return -1, nil
} else {
return len(devs), self.GetDevsReservedResource(devs)
}
}
func (self *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 {
reservedCpu += dev.ReservedCpu
reservedMem += dev.ReservedMemory
reservedStorage += dev.ReservedStorage
}
return &reservedResourceForGpu
}
func (self *SHost) GetMetadataHiddenKeys() []string {
return []string{}
}
func (manager *SHostManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.HostDetails {
rows := make([]api.HostDetails, len(objs))
stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
showReason := false
if query.Contains("show_fail_reason") {
showReason = true
}
var hideCpuTypoInfo = jsonutils.QueryBoolean(query, "hide_cpu_topo_info", false)
hostIds := make([]string, len(objs))
hosts := make([]*SHost, len(objs))
for i := range rows {
rows[i] = api.HostDetails{
EnabledStatusInfrasResourceBaseDetails: stdRows[i],
ManagedResourceInfo: managerRows[i],
ZoneResourceInfo: zoneRows[i],
}
host := objs[i].(*SHost)
hostIds[i] = host.Id
hosts[i] = host
}
baremetalServers, err := fetchBaremetalServer(hostIds)
if err != nil {
log.Errorf("fetchBaremetalServer error: %v", err)
return rows
}
serverIds := []string{}
for _, server := range baremetalServers {
serverIds = append(serverIds, server.Id)
}
serverIps := fetchGuestIPs(serverIds, tristate.False)
status := ""
if options.Options.IgnoreNonrunningGuests {
status = api.VM_RUNNING
}
guestResources, err := fetchHostGuestResource(hostIds, status)
if err != nil {
log.Errorf("fetchHostGuestResource error: %v", err)
return rows
}
metas := []db.SMetadata{}
err = db.Metadata.Query().In("obj_id", hostIds).In("key", []string{api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN, api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN}).All(&metas)
if err != nil {
log.Errorf("query meta error: %v", err)
return rows
}
downMap, shutdownMap := map[string]bool{}, map[string]bool{}
for _, meta := range metas {
switch meta.Key {
case api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN:
downMap[meta.ObjId] = (meta.Value == "enable")
case api.HOSTMETA_AUTO_MIGRATE_ON_HOST_SHUTDOWN:
shutdownMap[meta.ObjId] = (meta.Value == "enable")
}
}
isolatedDevices := IsolatedDeviceManager.FindByHosts(hostIds)
isolatedDeviceMap := map[string][]SIsolatedDevice{}
for i := range isolatedDevices {
_, ok := isolatedDeviceMap[isolatedDevices[i].HostId]
if !ok {
isolatedDeviceMap[isolatedDevices[i].HostId] = []SIsolatedDevice{}
}
isolatedDeviceMap[isolatedDevices[i].HostId] = append(isolatedDeviceMap[isolatedDevices[i].HostId], isolatedDevices[i])
}
schedtags, err := fetchHostSchedtags(hostIds)
if err != nil {
log.Errorf("fetchHostSchedtags error: %v", err)
return rows
}
storages, err := fetchHostStorages(hostIds)
if err != nil {
log.Errorf("host storages error: %v", err)
return rows
}
nics, err := fetchHostNics(hostIds)
if err != nil {
log.Errorf("fetchHostNics error: %v", err)
return rows
}
guestCnts := manager.FetchGuestCnt(hostIds)
for i := range rows {
cnt, ok := guestCnts[hostIds[i]]
if ok {
rows[i].Guests = cnt.GuestCnt
rows[i].RunningGuests = cnt.RunningGuestCnt
rows[i].ReadyGuests = cnt.ReadyGuestCnt
rows[i].OtherGuests = cnt.OtherGuestCnt
rows[i].NonsystemGuests = cnt.NonsystemGuestCnt
rows[i].PendingDeletedGuests = cnt.PendingDeletedGuestCnt
}
if server, ok := baremetalServers[hostIds[i]]; ok {
rows[i].ServerId = server.Id
rows[i].Server = server.Name
rows[i].ServerPendingDeleted = server.PendingDeleted
if hosts[i].HostType == api.HOST_TYPE_BAREMETAL && len(serverIps) > 0 {
if ips, _ := serverIps[server.Id]; len(ips) > 0 {
rows[i].ServerIps = strings.Join(ips, ",")
}
}
}
if hosts[i].EnableHealthCheck && hostHealthChecker != nil {
rows[i].AllowHealthCheck = true
}
rows[i].AutoMigrateOnHostDown = downMap[hostIds[i]]
rows[i].AutoMigrateOnHostShutdown = shutdownMap[hostIds[i]]
if hosts[i].IsBaremetal {
rows[i].CanPrepare = true
if server := baremetalServers[hostIds[i]]; server != nil && server.Status != api.VM_ADMIN {
rows[i].CanPrepare = false
if showReason {
rows[i].PrepareFailReason = fmt.Sprintf("Cannot prepare baremetal in server status %s", server.Status)
}
}
err := hosts[i].canPrepare()
if err != nil && rows[i].CanPrepare {
rows[i].CanPrepare = false
if showReason {
rows[i].PrepareFailReason = err.Error()
}
}
}
if usage, ok := guestResources[hostIds[i]]; ok {
rows[i].CpuCommit = usage.GuestVcpuCount
rows[i].MemCommit = usage.GuestVmemSize
totalCpu := hosts[i].GetCpuCount()
cpuCommitRate := 0.0
if totalCpu > 0 && usage.GuestVcpuCount > 0 {
cpuCommitRate = float64(usage.GuestVcpuCount) * 1.0 / float64(totalCpu)
}
rows[i].CpuCommitRate = cpuCommitRate
totalMem := hosts[i].GetMemSize()
memCommitRate := 0.0
if totalMem > 0 && usage.GuestVmemSize > 0 {
memCommitRate = float64(usage.GuestVmemSize) * 1.0 / float64(totalMem)
}
rows[i].MemCommitRate = memCommitRate
}
if devs, ok := isolatedDeviceMap[hostIds[i]]; ok {
rows[i].IsolatedDeviceCount = len(devs)
rows[i].ReservedResourceForGpu = hosts[i].GetDevsReservedResource(devs)
}
if capa, ok := storages[hostIds[i]]; ok {
rows[i].Storage = capa.Capacity
rows[i].StorageUsed = capa.Used
rows[i].ActualStorageUsed = capa.ActualUsed
rows[i].StorageWaste = capa.Wasted
rows[i].StorageVirtual = capa.VCapacity
rows[i].StorageFree = capa.GetFree()
rows[i].StorageCommitRate = capa.GetCommitRate()
}
rows[i].IsPrepaidRecycle = hosts[i].IsPrepaidRecycle()
rows[i].CpuCommitBound = hosts[i].GetCPUOvercommitBound()
rows[i].MemCommitBound = hosts[i].GetMemoryOvercommitBound()
rows[i].Spec = hosts[i].GetHardwareSpecification()
rows[i].Schedtags, _ = schedtags[hostIds[i]]
rows[i].NicInfo, _ = nics[hostIds[i]]
rows[i].NicCount = len(rows[i].NicInfo)
if hideCpuTypoInfo {
sysInfo, ok := hosts[i].SysInfo.(*jsonutils.JSONDict)
if ok {
sysInfo.Remove("cpu_info")
sysInfo.Remove("topology")
}
delete(rows[i].Metadata, "cpu_info")
delete(rows[i].Metadata, "topology")
}
}
return rows
}
type 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_CONTAINER).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_CONTAINER).All(&guests)
for _, guest := range guests {
_, ok := ret[guest.BackupHostId]
if !ok {
ret[guest.BackupHostId] = &sGuestCnt{}
}
ret[guest.BackupHostId].BackupGuestCnt += 1
}
return ret
}
func fetchBaremetalServer(hostIds []string) (map[string]*SGuest, error) {
guests := []SGuest{}
err := GuestManager.Query().In("host_id", hostIds).Equals("hypervisor", api.HOST_TYPE_BAREMETAL).All(&guests)
if err != nil {
return nil, err
}
ret := map[string]*SGuest{}
for i := range guests {
ret[guests[i].HostId] = &guests[i]
}
return ret, nil
}
func (self *SHost) AllowGetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowGetSpec(userCred, self, "vnc")
}
func (self *SHost) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
retval := jsonutils.NewDict()
retval.Set("host_id", jsonutils.NewString(self.Id))
zone, _ := self.GetZone()
retval.Set("zone", jsonutils.NewString(zone.GetName()))
return retval, nil
}
return jsonutils.NewDict(), nil
}
func (self *SHost) AllowGetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowGetSpec(userCred, self, "ipmi")
}
func (self *SHost) GetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ret, ok := self.IpmiInfo.(*jsonutils.JSONDict)
if !ok {
return nil, httperrors.NewNotFoundError("No ipmi information was found for host %s", self.Name)
}
password, err := ret.GetString("password")
if err != nil {
return nil, httperrors.NewNotFoundError("IPMI has no password information")
}
descryptedPassword, err := utils.DescryptAESBase64(self.Id, password)
if err != nil {
return nil, err
}
ret.Set("password", jsonutils.NewString(descryptedPassword))
return ret, nil
}
func (manager *SHostManager) GetHostsByManagerAndRegion(managerId string, regionId string) []SHost {
zones := ZoneManager.Query().Equals("cloudregion_id", regionId).SubQuery()
hosts := HostManager.Query()
q := hosts.Equals("manager_id", managerId)
q = q.Join(zones, sqlchemy.Equals(zones.Field("id"), hosts.Field("zone_id")))
ret := make([]SHost, 0)
err := db.FetchModelObjects(HostManager, q, &ret)
if err != nil {
log.Errorf("GetHostsByManagerAndRegion fail %s", err)
return nil
}
return ret
}
func (self *SHost) Request(ctx context.Context, userCred mcclient.TokenCredential, method httputils.THttpMethod, url string, headers http.Header, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
s := auth.GetSession(ctx, userCred, "", "")
_, ret, err := s.JSONRequest(self.ManagerUri, "", method, url, headers, body)
return ret, err
}
func (self *SHost) GetLocalStoragecache() *SStoragecache {
localStorages := self.GetAttachedLocalStorages()
for i := 0; i < len(localStorages); i += 1 {
sc := localStorages[i].GetStoragecache()
if sc != nil {
return sc
}
}
return nil
}
func (self *SHost) GetStoragecache() *SStoragecache {
localStorages := self.GetAttachedEnabledHostStorages(nil)
for i := 0; i < len(localStorages); i += 1 {
sc := localStorages[i].GetStoragecache()
if sc != nil {
return sc
}
}
return nil
}
func (self *SHost) PostCreate(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
data jsonutils.JSONObject,
) {
self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
input := api.HostCreateInput{}
err := data.Unmarshal(&input)
if err != nil {
log.Errorf("data.Unmarshal fail %s", err)
return
}
kwargs := data.(*jsonutils.JSONDict)
ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, self.Id)
if err != nil {
log.Errorf("fetchIpmiInfo fail %s", err)
return
}
ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict)
if ipmiInfoJson.Length() > 0 {
_, err := self.SaveUpdates(func() error {
self.IpmiInfo = ipmiInfoJson
return nil
})
if err != nil {
log.Errorf("save updates: %v", err)
} else if len(ipmiInfo.IpAddr) > 0 {
self.setIpmiIp(userCred, ipmiInfo.IpAddr)
}
}
if len(input.AccessIp) > 0 {
self.setAccessIp(userCred, input.AccessIp)
}
if len(input.AccessMac) > 0 {
self.setAccessMac(userCred, input.AccessMac)
}
noProbe := false
if input.NoProbe != nil {
noProbe = *input.NoProbe
}
if len(self.ZoneId) > 0 && self.HostType == api.HOST_TYPE_BAREMETAL && !noProbe {
// ipmiInfo, _ := self.GetIpmiInfo()
if len(ipmiInfo.IpAddr) > 0 {
self.StartBaremetalCreateTask(ctx, userCred, kwargs, "")
}
}
keys := GetHostQuotaKeysFromCreateInput(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)
}
}
func (self *SHost) StartBaremetalCreateTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error {
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCreateTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (manager *SHostManager) ValidateSizeParams(input api.HostSizeAttributes) (api.HostSizeAttributes, error) {
memStr := input.MemSize
if len(memStr) > 0 {
if !regutils.MatchSize(memStr) {
return input, errors.Wrap(httperrors.ErrInputParameter, "Memory size must be number[+unit], like 256M, 1G or 256")
}
memSize, err := fileutils.GetSizeMb(memStr, 'M', 1024)
if err != nil {
return input, errors.Wrap(err, "fileutils.GetSizeMb")
}
input.MemSize = strconv.FormatInt(int64(memSize), 10)
// data.Set("mem_size", jsonutils.NewInt(int64(memSize)))
}
memReservedStr := input.MemReserved
if len(memReservedStr) > 0 {
if !regutils.MatchSize(memReservedStr) {
return input, errors.Wrap(httperrors.ErrInputParameter, "Memory size must be number[+unit], like 256M, 1G or 256")
}
memSize, err := fileutils.GetSizeMb(memReservedStr, 'M', 1024)
if err != nil {
return input, errors.Wrap(err, "fileutils.GetSizeMb")
}
input.MemReserved = strconv.FormatInt(int64(memSize), 10)
// data.Set("mem_reserved", jsonutils.NewInt(int64(memSize)))
}
cpuCacheStr := input.CpuCache
if len(cpuCacheStr) > 0 {
if !regutils.MatchSize(cpuCacheStr) {
return input, errors.Wrapf(httperrors.ErrInputParameter, "Illegal cpu cache size %s", cpuCacheStr)
}
cpuCache, err := fileutils.GetSizeKb(cpuCacheStr, 'K', 1024)
if err != nil {
return input, errors.Wrap(err, "fileutils.GetSizeKb")
}
input.CpuCache = strconv.FormatInt(int64(cpuCache), 10)
// data.Set("cpu_cache", jsonutils.NewInt(int64(cpuCache)))
}
return input, nil
}
func (manager *SHostManager) inputUniquenessCheck(input api.HostAccessAttributes, zoneId string, hostId string) (api.HostAccessAttributes, error) {
for key, val := range map[string]string{
"manager_uri": input.ManagerUri,
"access_ip": input.AccessIp,
} {
if len(val) > 0 {
q := manager.Query().Equals(key, val)
if len(zoneId) > 0 {
q = q.Equals("zone_id", zoneId)
} else {
q = q.IsNullOrEmpty("zone_id")
}
if len(hostId) > 0 {
q = q.NotEquals("id", hostId)
}
cnt, err := q.CountWithError()
if err != nil {
return input, httperrors.NewInternalServerError("check %s duplication fail %s", key, err)
}
if cnt > 0 {
return input, httperrors.NewConflictError("duplicate %s %s", key, val)
}
}
}
accessMac := input.AccessMac
if len(accessMac) > 0 {
accessMac2 := netutils.FormatMacAddr(accessMac)
if len(accessMac2) == 0 {
return input, httperrors.NewInputParameterError("invalid macAddr %s", accessMac)
}
if accessMac2 != api.ACCESS_MAC_ANY {
q := manager.Query().Equals("access_mac", accessMac2)
if len(hostId) > 0 {
q = q.NotEquals("id", hostId)
}
cnt, err := q.CountWithError()
if err != nil {
return input, httperrors.NewInternalServerError("check access_mac duplication fail %s", err)
}
if cnt > 0 {
return input, httperrors.NewConflictError("duplicate access_mac %s", accessMac)
}
input.AccessMac = accessMac2
}
}
return input, nil
}
func (manager *SHostManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input api.HostCreateInput,
) (api.HostCreateInput, error) {
var err error
if len(input.ZoneId) > 0 {
_, input.ZoneResourceInput, err = ValidateZoneResourceInput(userCred, input.ZoneResourceInput)
if err != nil {
return input, errors.Wrap(err, "ValidateZoneResourceInput")
}
}
noProbe := false
if input.NoProbe != nil {
noProbe = *input.NoProbe
}
input.HostAccessAttributes, err = manager.inputUniquenessCheck(input.HostAccessAttributes, input.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)
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 {
var accessNet *SNetwork
accessIpAddr := input.AccessIp // tString("access_ip")
if len(accessIpAddr) > 0 {
net, _ := NetworkManager.GetOnPremiseNetworkOfIP(accessIpAddr, "", tristate.None)
if net == nil {
return input, httperrors.NewInputParameterError("%s is out of network IP ranges", accessIpAddr)
}
accessNet = net
} else {
accessNetStr := input.AccessNet // data.GetString("access_net")
if len(accessNetStr) > 0 {
netObj, err := NetworkManager.FetchByIdOrName(userCred, accessNetStr)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return input, httperrors.NewResourceNotFoundError2("network", accessNetStr)
} else {
return input, httperrors.NewGeneralError(err)
}
}
accessNet = netObj.(*SNetwork)
} else {
accessWireStr := input.AccessWire // data.GetString("access_wire")
if len(accessWireStr) > 0 {
wireObj, err := WireManager.FetchByIdOrName(userCred, accessWireStr)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return input, httperrors.NewResourceNotFoundError2("wire", accessWireStr)
} else {
return input, httperrors.NewGeneralError(err)
}
}
wire := wireObj.(*SWire)
lockman.LockObject(ctx, wire)
defer lockman.ReleaseObject(ctx, wire)
net, err := wire.GetCandidatePrivateNetwork(userCred, 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)
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)
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")
}
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 (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.HostUpdateInput) (api.HostUpdateInput, error) {
var err error
input.HostAccessAttributes, err = HostManager.inputUniquenessCheck(input.HostAccessAttributes, self.ZoneId, self.Id)
if err != nil {
return input, errors.Wrap(err, "inputUniquenessCheck")
}
input.HostSizeAttributes, err = HostManager.ValidateSizeParams(input.HostSizeAttributes)
if err != nil {
return input, errors.Wrap(err, "ValidateSizeParams")
}
ipmiInfo, err := fetchIpmiInfo(input.HostIpmiAttributes, self.Id)
if err != nil {
return input, errors.Wrap(err, "fetchIpmiInfo")
}
ipmiInfoJson := jsonutils.Marshal(ipmiInfo).(*jsonutils.JSONDict)
if ipmiInfoJson.Length() > 0 {
ipmiIpAddr := ipmiInfo.IpAddr
if len(ipmiIpAddr) > 0 {
net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None)
if net == nil {
return input, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr)
}
zoneObj, _ := net.GetZone()
if zoneObj == nil {
return input, httperrors.NewInputParameterError("IPMI network has not zone???")
}
if zoneObj.GetId() != self.ZoneId {
return input, httperrors.NewInputParameterError("New IPMI address located in another zone!")
}
}
val := jsonutils.NewDict()
val.Update(self.IpmiInfo)
val.Update(ipmiInfoJson)
input.IpmiInfo = val
}
input.EnabledStatusInfrasResourceBaseUpdateInput, err = self.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.ValidateUpdateData")
}
if len(input.Name) > 0 {
self.UpdateDnsRecords(false)
}
return input, nil
}
func (self *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SEnabledStatusInfrasResourceBase.PostUpdate(ctx, userCred, query, data)
if data.Contains("cpu_cmtbound") || data.Contains("mem_cmtbound") {
self.ClearSchedDescCache()
}
if self.OvnVersion != "" && self.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",
self.Name, self.Id, err)
return
}
if _, err := db.Update(self, func() error {
self.OvnMappedIpAddr = addr
return nil
}); err != nil {
log.Errorf("host %s(%s): db update vpc mapped addr: %v",
self.Name, self.Id, err)
return
}
}
// update baremetal host related server
if guest := self.GetBaremetalServer(); guest != nil && self.HostType == api.HOST_TYPE_BAREMETAL {
if _, err := db.Update(guest, func() error {
guest.VmemSize = self.MemSize
guest.VcpuCount = self.CpuCount
return nil
}); err != nil {
log.Errorf("baremetal host %s update related server %s spec error: %v", self.GetName(), guest.GetName(), err)
}
}
notSyncConf, _ := data.Bool("not_sync_config")
if !notSyncConf {
if err := self.startSyncConfig(ctx, userCred, "", true); err != nil {
log.Errorf("start sync host %q config after updated", self.GetName())
}
}
}
func (self *SHost) UpdateDnsRecords(isAdd bool) {
for _, netif := range self.GetNetInterfaces() {
self.UpdateDnsRecord(&netif, isAdd)
}
}
func (self *SHost) UpdateDnsRecord(netif *SNetInterface, isAdd bool) {
name := self.GetNetifName(netif)
if len(name) == 0 {
return
}
bn := netif.GetBaremetalNetwork()
if bn == nil {
log.Errorf("Interface %s not enable", netif.GetId())
return
}
net := bn.GetNetwork()
if net == nil {
log.Errorf("BaremetalNetwoke %s not found network", bn.GetId())
}
net._updateDnsRecord(name, bn.IpAddr, isAdd)
}
func (self *SHost) GetNetifName(netif *SNetInterface) string {
if netif.NicType == api.NIC_TYPE_IPMI {
return self.GetName()
} else if netif.NicType == api.NIC_TYPE_ADMIN {
return self.GetName() + "-admin"
}
return ""
}
func fetchIpmiInfo(data api.HostIpmiAttributes, hostId string) (types.SIPMIInfo, error) {
info := types.SIPMIInfo{}
info.Username = data.IpmiUsername
if len(data.IpmiPassword) > 0 {
if len(hostId) > 0 {
value, err := utils.EncryptAESBase64(hostId, data.IpmiPassword)
if err != nil {
log.Errorf("encrypt password failed %s", err)
return info, errors.Wrap(err, "utils.EncryptAESBase64")
}
info.Password = value
} else {
info.Password = data.IpmiPassword
}
}
if len(data.IpmiIpAddr) > 0 && !regutils.MatchIP4Addr(data.IpmiIpAddr) {
msg := fmt.Sprintf("ipmi_ip_addr: %s not valid ipv4 address", data.IpmiIpAddr)
log.Errorf(msg)
return info, errors.Wrap(httperrors.ErrInvalidFormat, msg)
}
info.IpAddr = data.IpmiIpAddr
if data.IpmiPresent != nil {
info.Present = *data.IpmiPresent
}
if data.IpmiLanChannel != nil {
info.LanChannel = *data.IpmiLanChannel
}
if data.IpmiVerified != nil {
info.Verified = *data.IpmiVerified
}
if data.IpmiRedfishApi != nil {
info.RedfishApi = *data.IpmiRedfishApi
}
if data.IpmiCdromBoot != nil {
info.CdromBoot = *data.IpmiCdromBoot
}
if data.IpmiPxeBoot != nil {
info.PxeBoot = *data.IpmiPxeBoot
}
return info, nil
}
func (self *SHost) AllowPerformStart(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "start")
}
func (self *SHost) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !self.IsBaremetal {
return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host")
}
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY}) {
return nil, httperrors.NewInvalidStatusError("Cannot start baremetal with active guest")
}
guest := self.GetBaremetalServer()
if guest != nil {
if self.HostType == api.HOST_TYPE_BAREMETAL && utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) {
return nil, self.InitializedGuestStart(ctx, userCred, guest)
}
// if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) {
// return nil, httperrors.NewBadRequestError("Cannot start baremetal with active guest")
// }
self.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "")
return guest.PerformStart(ctx, userCred, query, data)
}
params := jsonutils.NewDict()
params.Set("force_reboot", jsonutils.NewBool(false))
params.Set("action", jsonutils.NewString("start"))
return self.PerformMaintenance(ctx, userCred, nil, params)
}
func (self *SHost) AllowPerformStop(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "stop")
}
func (self *SHost) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !self.IsBaremetal {
return nil, httperrors.NewBadRequestError("Cannot stop a non-baremetal host")
}
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_RUNNING}) {
return nil, httperrors.NewInvalidStatusError("Cannot stop baremetal with non-active guest")
}
guest := self.GetBaremetalServer()
if guest != nil {
if self.HostType != api.HOST_TYPE_BAREMETAL {
if !utils.IsInStringArray(guest.Status, []string{api.VM_ADMIN}) {
return nil, httperrors.NewBadRequestError("Cannot stop baremetal with active guest")
}
} else {
if utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) {
return nil, self.InitializedGuestStop(ctx, userCred, guest)
}
self.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "")
input := api.ServerStopInput{}
data.Unmarshal(&input)
return guest.PerformStop(ctx, userCred, query, input)
}
}
return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, false, "stop")
}
func (self *SHost) InitializedGuestStart(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error {
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerStartTask", guest, userCred, nil, "", "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SHost) InitializedGuestStop(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error {
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerStopTask", guest, userCred, nil, "", "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SHost) AllowPerformMaintenance(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "maintenance")
}
func (self *SHost) PerformMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
return nil, httperrors.NewInvalidStatusError("Cannot do maintenance in status %s", self.Status)
}
guest := self.GetBaremetalServer()
if guest != nil && !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_ADMIN}) {
return nil, httperrors.NewInvalidStatusError("Cannot do maintenance while guest status %s", guest.Status)
}
params := jsonutils.NewDict()
if guest != nil {
if guest.Status == api.VM_RUNNING {
params.Set("guest_running", jsonutils.NewBool(true))
}
guest.SetStatus(userCred, api.VM_ADMIN, "")
}
if self.Status == api.BAREMETAL_RUNNING && jsonutils.QueryBoolean(data, "force_reboot", false) {
params.Set("force_reboot", jsonutils.NewBool(true))
}
action := "maintenance"
if data.Contains("action") {
action, _ = data.GetString("action")
}
params.Set("action", jsonutils.NewString(action))
self.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "")
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalMaintenanceTask", self, userCred, params, "", "", nil)
if err != nil {
return nil, err
}
task.ScheduleRun(nil)
return nil, nil
}
func (self *SHost) AllowPerformUnmaintenance(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "unmaintenance")
}
func (self *SHost) PerformUnmaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_RUNNING, api.BAREMETAL_READY}) {
return nil, httperrors.NewInvalidStatusError("Cannot do unmaintenance in status %s", self.Status)
}
guest := self.GetBaremetalServer()
if guest != nil && guest.Status != api.VM_ADMIN {
return nil, httperrors.NewInvalidStatusError("Wrong guest status %s", guest.Status)
}
action, _ := data.GetString("action")
if len(action) == 0 {
action = "unmaintenance"
}
guestRunning := self.GetMetadata("__maint_guest_running", userCred)
var startGuest = false
if utils.ToBool(guestRunning) {
startGuest = true
}
return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, startGuest, action)
}
func (self *SHost) StartBaremetalUnmaintenanceTask(ctx context.Context, userCred mcclient.TokenCredential, startGuest bool, action string) error {
self.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "")
params := jsonutils.NewDict()
params.Set("guest_running", jsonutils.NewBool(startGuest))
if len(action) == 0 {
action = "unmaintenance"
}
params.Set("action", jsonutils.NewString(action))
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnmaintenanceTask", self, userCred, params, "", "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SHost) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
guest := self.GetBaremetalServer()
if guest != nil {
return guest.StartSyncstatus(ctx, userCred, parentTaskId)
}
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncStatusTask", self, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SHost) AllowPerformOffline(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "offline")
}
func (self *SHost) PerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.HostOfflineInput) (jsonutils.JSONObject, error) {
if self.HostStatus != api.HOST_OFFLINE {
_, err := self.SaveUpdates(func() error {
self.HostStatus = api.HOST_OFFLINE
if input.UpdateHealthStatus != nil && *input.UpdateHealthStatus {
self.EnableHealthCheck = false
}
// Note: update host status to unknown on host offline
// we did not have host status after host offline
self.Status = api.BAREMETAL_UNKNOWN
return nil
})
if err != nil {
return nil, err
}
db.OpsLog.LogEvent(self, db.ACT_OFFLINE, input.Reason, userCred)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_OFFLINE, input, userCred, true)
ndata := jsonutils.Marshal(self).(*jsonutils.JSONDict)
if len(input.Reason) > 0 {
ndata.Add(jsonutils.NewString(input.Reason), "reason")
}
notifyclient.SystemExceptionNotify(ctx, napi.ActionOffline, HostManager.Keyword(), ndata)
self.SyncAttachedStorageStatus()
}
return nil, nil
}
func (self *SHost) AllowPerformOnline(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "online")
}
func (self *SHost) PerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.HostStatus != api.HOST_ONLINE {
_, err := self.SaveUpdates(func() error {
self.LastPingAt = time.Now()
self.HostStatus = api.HOST_ONLINE
self.EnableHealthCheck = true
if !self.IsMaintaining() {
self.Status = api.BAREMETAL_RUNNING
}
return nil
})
if err != nil {
return nil, err
}
if hostHealthChecker != nil {
hostHealthChecker.WatchHost(context.Background(), self.GetHostnameByName())
}
db.OpsLog.LogEvent(self, db.ACT_ONLINE, "", userCred)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_ONLINE, data, userCred, true)
self.SyncAttachedStorageStatus()
self.StartSyncAllGuestsStatusTask(ctx, userCred)
}
return nil, nil
}
func (self *SHost) AllowPerformAutoMigrateOnHostDown(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "auto-migrate-on-host-down")
}
func (self *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"
}
if input.AutoMigrateOnHostDown == "enable" {
meta[api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN] = "enable"
_, err := self.Request(ctx, userCred, "POST", "/hosts/shutdown-servers-on-host-down",
mcclient.GetTokenHeaders(userCred), nil)
if err != nil {
return nil, err
}
} else if input.AutoMigrateOnHostDown == "disable" {
meta[api.HOSTMETA_AUTO_MIGRATE_ON_HOST_DOWN] = "disable"
}
return nil, self.SetAllMetadata(ctx, meta, userCred)
}
func (self *SHost) StartSyncAllGuestsStatusTask(ctx context.Context, userCred mcclient.TokenCredential) error {
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncAllGuestsStatusTask", self, userCred, nil, "", "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (self *SHost) AllowPerformPing(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "ping")
}
func (self *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SHostPingInput) (jsonutils.JSONObject, error) {
if self.HostType == api.HOST_TYPE_BAREMETAL {
return nil, httperrors.NewNotSupportedError("ping host type %s not support", self.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)
}
}
}
self.SetMetadata(ctx, "root_partition_used_capacity_mb", input.RootPartitionUsedCapacityMb, userCred)
self.SetMetadata(ctx, "memory_used_mb", input.MemoryUsedMb, userCred)
}
if self.HostStatus != api.HOST_ONLINE {
self.PerformOnline(ctx, userCred, query, nil)
} else {
self.SaveUpdates(func() error {
self.LastPingAt = time.Now()
return nil
})
}
result := jsonutils.NewDict()
result.Set("name", jsonutils.NewString(self.GetName()))
dependSvcs := []string{"ntpd", "kafka", "influxdb", "elasticsearch"}
catalog := auth.GetCatalogData(dependSvcs, options.Options.Region)
if catalog == nil {
return nil, fmt.Errorf("Get catalog error")
}
result.Set("catalog", catalog)
appParams := appsrv.AppContextGetParams(ctx)
if appParams != nil {
// skip log&trace, when everything is normal
appParams.SkipTrace = true
appParams.SkipLog = true
}
return result, nil
}
func (self *SHost) AllowPerformPrepare(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "prepare")
}
func (self *SHost) HasBMC() bool {
ipmiInfo, _ := self.GetIpmiInfo()
if ipmiInfo.Username != "" && ipmiInfo.Password != "" {
return true
}
return false
}
func (self *SHost) IsUEFIBoot() bool {
info, _ := self.GetUEFIInfo()
if info == nil {
return false
}
if len(info.PxeBootNum) == 0 {
return false
}
return true
}
func (self *SHost) isRedfishCapable() bool {
ipmiInfo, _ := self.GetIpmiInfo()
if ipmiInfo.Verified && ipmiInfo.RedfishApi {
return true
}
return false
}
func (self *SHost) canPrepare() error {
if !self.IsBaremetal {
return httperrors.NewInvalidStatusError("not a baremetal")
}
if !self.isRedfishCapable() && len(self.AccessMac) == 0 && len(self.Uuid) == 0 {
return httperrors.NewInvalidStatusError("need valid access_mac and uuid to do prepare")
}
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING, api.BAREMETAL_PREPARE_FAIL}) {
return httperrors.NewInvalidStatusError("Cannot prepare baremetal in status %s", self.Status)
}
server := self.GetBaremetalServer()
if server != nil && server.Status != api.VM_ADMIN {
return httperrors.NewInvalidStatusError("Cannot prepare baremetal in server status %s", server.Status)
}
return nil
}
func (self *SHost) PerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := self.canPrepare()
if err != nil {
return nil, err
}
var onfinish string
server := self.GetBaremetalServer()
if server != nil && self.Status == api.BAREMETAL_READY {
onfinish = "shutdown"
}
return nil, self.StartPrepareTask(ctx, userCred, onfinish, "")
}
func (self *SHost) StartPrepareTask(ctx context.Context, userCred mcclient.TokenCredential, onfinish, parentTaskId string) error {
data := jsonutils.NewDict()
if len(onfinish) > 0 {
data.Set("on_finish", jsonutils.NewString(onfinish))
}
self.SetStatus(userCred, api.BAREMETAL_PREPARE, "start prepare task")
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalPrepareTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (self *SHost) AllowPerformIpmiProbe(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "ipmi-probe")
}
func (self *SHost) PerformIpmiProbe(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_READY, api.BAREMETAL_RUNNING, api.BAREMETAL_PROBE_FAIL, api.BAREMETAL_UNKNOWN}) {
return nil, self.StartIpmiProbeTask(ctx, userCred, "")
}
return nil, httperrors.NewInvalidStatusError("Cannot do Ipmi-probe in status %s", self.Status)
}
func (self *SHost) StartIpmiProbeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
data := jsonutils.NewDict()
self.SetStatus(userCred, api.BAREMETAL_START_PROBE, "start ipmi-probe task")
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalIpmiProbeTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (self *SHost) AllowPerformInitialize(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, data jsonutils.JSONObject,
) bool {
return db.IsAdminAllowPerform(userCred, self, "initialize")
}
func (self *SHost) PerformInitialize(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, data jsonutils.JSONObject,
) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(
self.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_PREPARE_FAIL}) {
return nil, httperrors.NewBadRequestError(
"Cannot do initialization in status %s", self.Status)
}
name, err := data.GetString("name")
if err != nil || self.GetBaremetalServer() != nil {
return nil, nil
}
err = db.NewNameValidator(GuestManager, userCred, name, nil)
if err != nil {
return nil, err
}
if self.IpmiInfo == nil || !self.IpmiInfo.Contains("ip_addr") ||
!self.IpmiInfo.Contains("password") {
return nil, httperrors.NewBadRequestError("IPMI infomation not configured")
}
guest := &SGuest{}
guest.Name = name
guest.VmemSize = self.MemSize
guest.VcpuCount = self.CpuCount
guest.DisableDelete = tristate.True
guest.Hypervisor = api.HYPERVISOR_BAREMETAL
guest.HostId = self.Id
guest.ProjectId = userCred.GetProjectId()
guest.DomainId = userCred.GetProjectDomainId()
guest.Status = api.VM_RUNNING
guest.OsType = "Linux"
guest.SetModelManager(GuestManager, guest)
err = GuestManager.TableSpec().Insert(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": self.AccessIp}, userCred)
caps := self.GetAttachedLocalStorageCapacity()
diskConfig := &api.DiskConfig{SizeMb: int(caps.GetFree())}
err = guest.CreateDisksOnHost(ctx, userCred, self, []*api.DiskConfig{diskConfig}, nil, true, true, nil, nil, true)
if err != nil {
log.Errorf("Host perform initialize failed on create disk %s", err)
}
net, err := self.getNetworkOfIPOnHost(self.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, self, &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 (self *SHost) AllowPerformAddNetif(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "add-netif")
}
func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
log.Debugf("add_netif %s", data)
mac, _ := data.GetString("mac")
if len(mac) == 0 || len(netutils.FormatMacAddr(mac)) == 0 {
return nil, httperrors.NewBadRequestError("Invaild mac address")
}
wire, _ := data.GetString("wire")
ipAddr, _ := data.GetString("ip_addr")
rate, _ := data.Int("rate")
nicType, _ := data.GetString("nic_type")
index, _ := data.Int("index")
linkUp, _ := data.GetString("link_up")
mtu, _ := data.Int("mtu")
reset := jsonutils.QueryBoolean(data, "reset", false)
strInterface, _ := data.GetString("interface")
bridge, _ := data.GetString("bridge")
reserve := jsonutils.QueryBoolean(data, "reserve", false)
requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false)
isLinkUp := tristate.None
if linkUp != "" {
if utils.ToBool(linkUp) {
isLinkUp = tristate.True
} else {
isLinkUp = tristate.False
}
}
err := self.addNetif(ctx, userCred, mac, wire, ipAddr, int(rate), nicType, int8(index), isLinkUp,
int16(mtu), reset, strInterface, bridge, reserve, requireDesignatedIp)
return nil, err
}
func (self *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential,
mac string, wire string, ipAddr string,
rate int, nicType string, index int8, linkUp tristate.TriState, mtu int16,
reset bool, strInterface string, bridge string,
reserve bool, requireDesignatedIp bool,
) error {
var sw *SWire
if len(wire) > 0 {
iWire, err := WireManager.FetchByIdOrName(userCred, wire)
if err != nil {
if err == sql.ErrNoRows {
return httperrors.NewResourceNotFoundError2(WireManager.Keyword(), wire)
} else {
return httperrors.NewInternalServerError("find Wire %s error: %s", wire, err)
}
}
sw = iWire.(*SWire)
if len(ipAddr) > 0 {
iIpAddr, err := netutils.NewIPV4Addr(ipAddr)
if err != nil {
return httperrors.NewInputParameterError("invalid ipaddr %s", ipAddr)
}
findAddr := false
swNets, err := sw.getNetworks(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.FetchByMac(mac)
if err != nil {
if err != sql.ErrNoRows {
return httperrors.NewInternalServerError("fail to fetch netif by mac %s: %s", mac, err)
}
// else not found
netif = &SNetInterface{}
netif.Mac = mac
netif.BaremetalId = self.Id
if sw != nil {
netif.WireId = sw.Id
}
netif.Rate = rate
netif.NicType = nicType
if index >= 0 {
netif.Index = index
}
if !linkUp.IsNone() {
netif.LinkUp = linkUp.Bool()
}
netif.Mtu = mtu
err = NetInterfaceManager.TableSpec().Insert(ctx, netif)
if err != nil {
return err
}
} else {
var changed = false
_, err := db.Update(netif, func() error {
if netif.BaremetalId != self.Id {
changed = true
netif.BaremetalId = self.Id
}
if sw != nil && netif.WireId != sw.Id {
changed = true
netif.WireId = sw.Id
}
if rate > 0 && rate != netif.Rate {
netif.Rate = rate
}
if nicType != "" && nicType != netif.NicType {
netif.NicType = nicType
}
if index >= 0 && index != netif.Index {
netif.Index = index
}
if !linkUp.IsNone() && linkUp.Bool() != netif.LinkUp {
netif.LinkUp = linkUp.Bool()
}
if mtu > 0 && mtu != netif.Mtu {
netif.Mtu = mtu
}
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
if changed || reset {
self.DisableNetif(ctx, userCred, netif, false)
}
}
sw = netif.GetWire()
if sw != nil {
if len(strInterface) == 0 {
strInterface = fmt.Sprintf("eth%d", netif.Index)
}
if len(strInterface) > 0 {
if len(bridge) == 0 {
bridge = fmt.Sprintf("br%s", sw.GetName())
}
var isMaster = netif.NicType == api.NIC_TYPE_ADMIN
hw, err := HostwireManager.FetchByHostIdAndMac(self.Id, mac)
if err != nil {
if err != sql.ErrNoRows {
return httperrors.NewInternalServerError("fail to fetch hostwire by mac %s: %s", mac, err)
}
hw = &SHostwire{}
hw.Bridge = bridge
hw.Interface = strInterface
hw.HostId = self.Id
hw.WireId = sw.Id
hw.IsMaster = isMaster
hw.MacAddr = mac
err := HostwireManager.TableSpec().Insert(ctx, hw)
if err != nil {
return err
}
} else {
db.Update(hw, func() error {
hw.Bridge = bridge
hw.Interface = strInterface
// hw.MacAddr = mac
hw.WireId = sw.Id
hw.IsMaster = isMaster
return nil
})
}
}
}
if netif.NicType == api.NIC_TYPE_ADMIN {
err := self.setAccessMac(userCred, netif.Mac)
if err != nil {
return httperrors.NewBadRequestError("%v", err)
}
}
if len(ipAddr) > 0 {
err = self.EnableNetif(ctx, userCred, netif, "", ipAddr, "", "", reserve, requireDesignatedIp)
if err != nil {
return httperrors.NewBadRequestError("%v", err)
}
}
self.ClearSchedDescCache()
return nil
}
func (self *SHost) AllowPerformEnableNetif(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "enable-netif")
}
func (self *SHost) PerformEnableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
log.Debugf("enable_netif %s", data)
mac, _ := data.GetString("mac")
netif := self.GetNetInterface(mac)
if netif == nil {
return nil, httperrors.NewBadRequestError("Interface %s not exist", mac)
}
if !utils.IsInStringArray(netif.NicType, api.NIC_TYPES) {
return nil, httperrors.NewBadRequestError("Only ADMIN and IPMI nic can be enable")
}
network, _ := data.GetString("network")
ipAddr, _ := data.GetString("ip_addr")
allocDir, _ := data.GetString("alloc_dir")
netType, _ := data.GetString("net_type")
reserve := jsonutils.QueryBoolean(data, "reserve", false)
requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false)
err := self.EnableNetif(ctx, userCred, netif, network, ipAddr, allocDir, netType, reserve, requireDesignatedIp)
if err != nil {
return nil, httperrors.NewBadRequestError("%v", err)
}
return nil, nil
}
func (self *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, network, ipAddr, allocDir string, netType string, reserve, requireDesignatedIp bool) error {
bn := netif.GetBaremetalNetwork()
if bn != nil {
log.Debugf("Netif has been attach2network? %s", jsonutils.Marshal(bn))
return nil
}
var net *SNetwork
var err error
if len(ipAddr) > 0 {
net, err = netif.GetCandidateNetworkForIp(userCred, 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 self.ZoneId == "" {
if _, err := self.SaveUpdates(func() error {
self.ZoneId = wire.ZoneId
return nil
}); err != nil {
return errors.Wrapf(err, "set host zone_id %s by wire", wire.ZoneId)
}
}
hw, err := HostwireManager.FetchByHostIdAndMac(self.Id, netif.Mac)
if err != nil {
return err
}
if hw.WireId != wire.Id {
return fmt.Errorf("host not attach to this wire")
}
if net == nil {
if len(network) > 0 {
iNet, err := NetworkManager.FetchByIdOrName(userCred, network)
if err != nil {
return fmt.Errorf("Network %s not found: %s", network, err)
}
net = iNet.(*SNetwork)
if len(net.WireId) == 0 || net.WireId != wire.Id {
return fmt.Errorf("Network %s not reacheable on mac %s", network, netif.Mac)
}
} else {
var netTypes []string
if len(netType) > 0 && netType != api.NETWORK_TYPE_BAREMETAL {
netTypes = []string{netType, api.NETWORK_TYPE_BAREMETAL}
} else {
netTypes = []string{api.NETWORK_TYPE_BAREMETAL}
}
net, err = wire.GetCandidatePrivateNetwork(userCred, 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(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 = self.Attach2Network(ctx, userCred, attachOpt)
if err != nil {
return errors.Wrap(err, "self.Attach2Network")
}
switch netif.NicType {
case api.NIC_TYPE_IPMI:
err = self.setIpmiIp(userCred, bn.IpAddr)
case api.NIC_TYPE_ADMIN:
err = self.setAccessIp(userCred, bn.IpAddr)
}
return err
}
func (self *SHost) AllowPerformDisableNetif(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "disable-netif")
}
func (self *SHost) PerformDisableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
mac, _ := data.GetString("mac")
netif := self.GetNetInterface(mac)
if netif == nil {
return nil, httperrors.NewBadRequestError("Interface %s not exists", mac)
}
reserve := jsonutils.QueryBoolean(data, "reserve", false)
err := self.DisableNetif(ctx, userCred, netif, reserve)
if err != nil {
return nil, httperrors.NewBadRequestError("%v", err)
}
return nil, nil
}
/*
* Disable a net interface, remove IP address if assigned
*/
func (self *SHost) DisableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error {
bn := netif.GetBaremetalNetwork()
var ipAddr string
if bn != nil {
ipAddr = bn.IpAddr
self.UpdateDnsRecord(netif, false)
self.DeleteBaremetalnetwork(ctx, userCred, bn, reserve)
}
var err error
switch netif.NicType {
case api.NIC_TYPE_IPMI:
if ipAddr == self.IpmiIp {
err = self.setIpmiIp(userCred, "")
}
case api.NIC_TYPE_ADMIN:
if ipAddr == self.AccessIp {
err = self.setAccessIp(userCred, "")
}
}
return err
}
type hostAttachNetworkOption struct {
netif *SNetInterface
net *SNetwork
ipAddr string
allocDir string
reserved bool
requireDesignatedIp bool
}
func (self *SHost) IsIpAddrWithinConvertedGuest(ctx context.Context, userCred mcclient.TokenCredential, ipAddr string, netif *SNetInterface) error {
if !self.IsBaremetal {
return httperrors.NewNotAcceptableError("Not a baremetal")
}
if self.HostType == api.HOST_TYPE_KVM {
return httperrors.NewNotAcceptableError("Not being convert to hypervisor")
}
bmServer := self.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 (self *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()
if ipAddr != "" {
// converted baremetal can resuse related guest network ip
if err := self.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)
if err != nil {
return nil, errors.Wrap(err, "net.GetFreeIP")
}
if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp {
return nil, fmt.Errorf("IP address %s is occupied, get %s instead", ipAddr, freeIp)
}
bn := &SHostnetwork{}
bn.BaremetalId = self.Id
bn.NetworkId = net.Id
bn.IpAddr = freeIp
bn.MacAddr = netif.Mac
err = HostnetworkManager.TableSpec().Insert(ctx, bn)
if err != nil {
return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert")
}
db.OpsLog.LogAttachEvent(ctx, self, net, userCred, jsonutils.NewString(freeIp))
self.UpdateDnsRecord(netif, true)
net.UpdateBaremetalNetmap(bn, self.GetNetifName(netif))
return bn, nil
}
func (self *SHost) AllowPerformRemoveNetif(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "remove-netif")
}
func (self *SHost) PerformRemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
mac, _ := data.GetString("mac")
mac = netutils.FormatMacAddr(mac)
if len(mac) == 0 {
return nil, httperrors.NewBadRequestError("Invalid mac address")
}
netif, err := NetInterfaceManager.FetchByMac(mac)
if err != nil {
return nil, httperrors.NewBadRequestError("Fetch netif error %s", err)
}
return nil, self.RemoveNetif(ctx, userCred, netif, jsonutils.QueryBoolean(data, "reserve", false))
}
func (self *SHost) RemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error {
wire := netif.GetWire()
self.DisableNetif(ctx, userCred, netif, reserve)
nicType := netif.NicType
mac := netif.Mac
err := netif.Remove(ctx, userCred)
if err != nil {
return errors.Wrap(err, "netif.Remove")
}
if wire != nil {
others := self.GetNetifsOnWire(wire)
if len(others) == 0 {
hw, _ := HostwireManager.FetchByHostIdAndMac(self.Id, netif.Mac)
if hw != nil {
db.OpsLog.LogDetachEvent(ctx, self, wire, userCred, jsonutils.NewString(fmt.Sprintf("disable netif %s", self.AccessMac)))
log.Debugf("Detach host wire because of remove netif %s", netif.Mac)
err := hw.Delete(ctx, userCred)
if err != nil {
return errors.Wrap(err, "remove host wire")
}
}
}
}
// is this a converted host?
if self.HostType == api.HOST_TYPE_HYPERVISOR && self.IsBaremetal {
guests, err := self.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 nicType == api.NIC_TYPE_ADMIN && self.AccessMac == mac {
err := self.setAccessMac(userCred, "")
if err != nil {
return errors.Wrap(err, "setAccessMac")
}
}
self.ClearSchedDescCache()
return nil
}
func (self *SHost) GetNetifsOnWire(wire *SWire) []SNetInterface {
dest := make([]SNetInterface, 0)
q := NetInterfaceManager.Query()
err := q.Filter(sqlchemy.Equals(q.Field("baremetal_id"), self.Id)).Filter(sqlchemy.Equals(q.Field("wire_id"), wire.Id)).Desc(q.Field("index")).All(&dest)
if err != nil {
log.Errorln(err)
return nil
}
return dest
}
func (self *SHost) AllowPerformSyncstatus(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "syncstatus")
}
func (self *SHost) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.HostType != api.HOST_TYPE_BAREMETAL {
return nil, httperrors.NewBadRequestError("Cannot sync status a non-baremetal host")
}
self.SetStatus(userCred, api.BAREMETAL_SYNCING_STATUS, "")
return nil, self.StartSyncstatus(ctx, userCred, "")
}
func (self *SHost) AllowPerformReset(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "reset")
}
func (self *SHost) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !self.IsBaremetal {
return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host")
}
if self.Status != api.BAREMETAL_RUNNING {
return nil, httperrors.NewBadRequestError("Cannot reset baremetal in status %s", self.Status)
}
guest := self.GetBaremetalServer()
if guest != nil {
if self.HostType == api.HOST_TYPE_BAREMETAL {
if guest.Status != api.VM_ADMIN {
return nil, httperrors.NewBadRequestError("Cannot reset baremetal with active guest")
}
} else {
return guest.PerformReset(ctx, userCred, query, data)
}
}
kwargs := jsonutils.NewDict()
kwargs.Set("force_reboot", jsonutils.JSONTrue)
kwargs.Set("action", jsonutils.NewString("reset"))
return self.PerformMaintenance(ctx, userCred, query, kwargs)
}
func (self *SHost) AllowPerformRemoveAllNetifs(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "remove-all-netifs")
}
func (self *SHost) PerformRemoveAllNetifs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
netifs := self.GetNetInterfaces()
for i := 0; i < len(netifs); i++ {
if !utils.IsInStringArray(netifs[i].NicType, api.NIC_TYPES) {
self.RemoveNetif(ctx, userCred, &netifs[i], false)
}
}
return nil, nil
}
func (self *SHost) AllowPerformEnable(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input apis.PerformEnableInput,
) bool {
return self.SEnabledStatusInfrasResourceBase.AllowPerformEnable(ctx, userCred, query, input)
}
func (self *SHost) PerformEnable(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input apis.PerformEnableInput,
) (jsonutils.JSONObject, error) {
if !self.GetEnabled() {
_, err := self.SEnabledStatusInfrasResourceBase.PerformEnable(ctx, userCred, query, input)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformEnable")
}
self.SyncAttachedStorageStatus()
}
return nil, nil
}
func (self *SHost) AllowPerformDisable(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input apis.PerformDisableInput,
) bool {
return self.SEnabledStatusInfrasResourceBase.AllowPerformDisable(ctx, userCred, query, input)
}
func (self *SHost) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
if self.GetEnabled() {
_, err := self.SEnabledStatusInfrasResourceBase.PerformDisable(ctx, userCred, query, input)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformDisable")
}
self.SyncAttachedStorageStatus()
}
return nil, nil
}
func (self *SHost) AllowPerformCacheImage(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "cache-image")
}
func (self *SHost) PerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.HostType == api.HOST_TYPE_BAREMETAL || self.HostStatus != api.HOST_ONLINE {
return nil, httperrors.NewInvalidStatusError("Cannot perform cache image in status %s", self.Status)
}
imageId, _ := data.GetString("image")
img, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
if err != nil {
log.Errorln(err)
return nil, httperrors.NewNotFoundError("image %s not found", imageId)
}
if len(img.Checksum) != 0 && regutils.MatchUUID(img.Checksum) {
return nil, httperrors.NewInvalidStatusError("Cannot cache image with no checksum")
}
isForce := jsonutils.QueryBoolean(data, "is_force", false)
format, _ := data.GetString("format")
return nil, self.StartImageCacheTask(ctx, userCred, img.Id, format, isForce)
}
func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, format string, isForce bool) error {
var sc *SStoragecache
switch self.HostType {
case api.HOST_TYPE_BAREMETAL:
case api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_ESXI:
sc = self.GetLocalStoragecache()
default:
sc = self.GetStoragecache()
}
if sc == nil {
return errors.Wrap(errors.ErrNotSupported, "No associate storage cache found")
}
return sc.StartImageCacheTask(ctx, userCred, imageId, format, isForce, "")
}
func (self *SHost) AllowPerformConvertHypervisor(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "convert-hypervisor")
}
func (self *SHost) isAlterNameUnique(name string) (bool, error) {
q := HostManager.Query().Equals("name", name).NotEquals("id", self.Id).Equals("zone_id", self.ZoneId)
cnt, err := q.CountWithError()
if err != nil {
return false, err
}
return cnt == 0, nil
}
func (self *SHost) PerformConvertHypervisor(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
hostType, err := data.GetString("host_type")
if err != nil {
return nil, httperrors.NewNotAcceptableError("host_type must be specified")
}
if self.HostType != api.HOST_TYPE_BAREMETAL {
return nil, httperrors.NewNotAcceptableError("Must be a baremetal host")
}
if self.GetBaremetalServer() != nil {
return nil, httperrors.NewNotAcceptableError("Baremetal host is aleady occupied")
}
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
return nil, httperrors.NewNotAcceptableError("Connot convert hypervisor in status %s", self.Status)
}
// check ownership
var ownerId mcclient.IIdentityProvider
hostOwnerId := self.GetOwnerId()
if userCred.GetProjectDomainId() != hostOwnerId.GetProjectDomainId() {
if !db.IsAdminAllowPerform(userCred, self, "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 := self.GetModelManager().ValidateName(name)
if err != nil {
return nil, err
}
uniq, err := self.isAlterNameUnique(name)
if err != nil {
return nil, httperrors.NewInternalServerError("isAlterNameUnique fail %s", err)
}
if !uniq {
return nil, httperrors.NewDuplicateNameError(name, self.Id)
}
}
image, _ := data.GetString("image")
raid, _ := data.GetString("raid")
input, err := driver.PrepareConvert(self, image, raid, data)
if err != nil {
return nil, httperrors.NewNotAcceptableError("Convert error: %s", err.Error())
}
// 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(self, db.ACT_CONVERT_START, "", userCred)
db.OpsLog.LogEvent(guest, db.ACT_CREATE, "Convert hypervisor", userCred)
opts := jsonutils.NewDict()
opts.Set("server_params", params)
opts.Set("server_id", jsonutils.NewString(guest.GetId()))
opts.Set("convert_host_type", jsonutils.NewString(hostType))
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalConvertHypervisorTask", self, adminCred, opts, "", "", nil)
if err != nil {
return nil, err
}
task.ScheduleRun(nil)
self.SetStatus(userCred, api.BAREMETAL_START_CONVERT, "")
return nil, nil
}
func (self *SHost) AllowPerformUndoConvert(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "undo-convert")
}
func (self *SHost) PerformUndoConvert(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !self.IsBaremetal {
return nil, httperrors.NewNotAcceptableError("Not a baremetal")
}
if self.HostType == api.HOST_TYPE_BAREMETAL {
return nil, httperrors.NewNotAcceptableError("Not being convert to hypervisor")
}
if self.GetEnabled() {
return nil, httperrors.NewNotAcceptableError("Host should be disabled")
}
if !utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
return nil, httperrors.NewNotAcceptableError("Cannot unconvert in status %s", self.Status)
}
driver := self.GetDriverWithDefault()
if driver == nil {
return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", self.HostType)
}
err := driver.PrepareUnconvert(self)
if err != nil {
return nil, httperrors.NewNotAcceptableError("%v", err)
}
guests, err := self.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(self, db.ACT_UNCONVERT_START, "", userCred)
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnconvertHypervisorTask", self, userCred, nil, "", "", nil)
if err != nil {
return nil, err
}
task.ScheduleRun(nil)
return nil, nil
}
func (self *SHost) GetDriverWithDefault() IHostDriver {
hostType := self.HostType
if len(hostType) == 0 {
hostType = api.HOST_TYPE_DEFAULT
}
return GetHostDriver(hostType)
}
func (self *SHost) UpdateDiskConfig(userCred mcclient.TokenCredential, layouts []baremetal.Layout) error {
bs := self.GetBaremetalstorage()
if bs != nil {
diff, err := db.Update(bs, func() error {
if len(layouts) != 0 {
bs.Config = jsonutils.Marshal(layouts).(*jsonutils.JSONArray)
var size int64
for i := 0; i < len(layouts); i++ {
size += layouts[i].Size
}
bs.RealCapacity = size
} else {
bs.Config = jsonutils.NewArray()
bs.RealCapacity = bs.GetStorage().Capacity
}
return nil
})
if err != nil {
log.Errorln(err)
return err
}
db.OpsLog.LogEvent(bs, db.ACT_UPDATE, diff, userCred)
}
return nil
}
// 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.GetNetInterfaces()
hostwires, err := host.getHostwires()
if err != nil {
result.Error(errors.Wrapf(err, "unable to getHostwires of host %s", host.GetId()))
return result
}
for i := range vsWires {
vsWire := vsWires[i]
if vsWire.SyncTimes > 0 {
continue
}
netif := host.findNetIfs(netIfs, vsWire.Mac)
if netif == nil {
// do nothing
continue
}
hostwire := host.findHostwire(hostwires, vsWire.WireId, vsWire.Mac)
if hostwire == nil {
hostwire = &SHostwire{
Bridge: vsWire.VsId,
MacAddr: vsWire.Mac,
HostId: host.GetId(),
WireId: vsWire.WireId,
}
hostwire.SetModelManager(HostwireManager, hostwire)
hostwire.MacAddr = vsWire.Mac
err := HostwireManager.TableSpec().Insert(ctx, hostwire)
if err != nil {
result.Error(errors.Wrapf(err, "unable to create hostwire for host %q", host.GetId()))
continue
}
}
if hostwire.Bridge != vsWire.VsId {
db.Update(hostwire, func() error {
hostwire.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) *SNetInterface {
for i := range netIfs {
if netIfs[i].Mac == mac {
return &netIfs[i]
}
}
return nil
}
func (host *SHost) SyncHostExternalNics(ctx context.Context, userCred mcclient.TokenCredential, ihost cloudprovider.ICloudHost) compare.SyncResult {
result := compare.SyncResult{}
netIfs := host.GetNetInterfaces()
extNics, err := ihost.GetIHostNics()
if err != nil {
result.Error(err)
return result
}
disables := make([]*SNetInterface, 0)
enables := make([]cloudprovider.ICloudHostNetInterface, 0)
type sRemoveNetInterface struct {
netif *SNetInterface
reserveIp bool
}
type sAddNetInterface struct {
netif cloudprovider.ICloudHostNetInterface
reserveIp bool
}
removes := make([]sRemoveNetInterface, 0)
adds := make([]sAddNetInterface, 0)
nicMax := len(netIfs)
if nicMax < len(extNics) {
nicMax = len(extNics)
}
for i := 0; i < nicMax; i += 1 {
if i < len(netIfs) && i < len(extNics) {
obn := netIfs[i].GetBaremetalNetwork()
var oip string
if obn != nil {
oip = obn.IpAddr
}
nip := extNics[i].GetIpAddr()
if netIfs[i].Mac == extNics[i].GetMac() {
if oip != nip {
if obn != nil {
disables = append(disables, &netIfs[i])
}
if len(nip) > 0 {
enables = append(enables, extNics[i])
}
} else {
// in sync, sync interface and bridge
hw := host.getHostwireOfIdAndMac(netIfs[i].WireId, netIfs[i].Mac)
if hw != nil && (hw.Bridge != extNics[i].GetBridge() || hw.Interface != extNics[i].GetDevice()) {
db.Update(hw, func() error {
hw.Interface = extNics[i].GetDevice()
// hw.Bridge = extNics[i].GetBridge()
return nil
})
}
}
} else {
reserveIp := false
if len(oip) > 0 && oip == nip {
// # mac change case
reserveIp = true
}
removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: reserveIp})
adds = append(adds, sAddNetInterface{netif: extNics[i], reserveIp: reserveIp})
}
} else if i < len(netIfs) && i >= len(extNics) {
removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: false})
} else if i >= len(netIfs) && i < len(extNics) {
adds = append(adds, sAddNetInterface{netif: extNics[i], reserveIp: false})
}
}
for i := len(removes) - 1; i >= 0; i -= 1 {
err = host.RemoveNetif(ctx, userCred, removes[i].netif, removes[i].reserveIp)
if err != nil {
result.DeleteError(err)
} else {
result.Delete()
}
}
for i := len(disables) - 1; i >= 0; i -= 1 {
err = host.DisableNetif(ctx, userCred, disables[i], false)
if err != nil {
result.DeleteError(err)
} else {
result.Delete()
}
}
for i := 0; i < len(enables); i += 1 {
netif := host.GetNetInterface(enables[i].GetMac())
// always true reserved address pool
err = host.EnableNetif(ctx, userCred, netif, "", enables[i].GetIpAddr(), "", "", true, true)
if err != nil {
result.AddError(err)
} else {
result.Add()
}
}
for i := 0; i < len(adds); i += 1 {
// always try reserved pool
extNic := adds[i].netif
err = host.addNetif(ctx, userCred, extNic.GetMac(), "", extNic.GetIpAddr(), 0, extNic.GetNicType(), extNic.GetIndex(),
extNic.IsLinkUp(), int16(extNic.GetMtu()), false, extNic.GetDevice(), extNic.GetBridge(), true, true)
if err != nil {
result.AddError(err)
} else {
result.Add()
}
}
return result
}
// func (manager *SHostManager) GetEsxiAgentHostId(key string) (string, error) {
// q := HostManager.Query("id")
// q = q.Equals("host_status", HOST_ONLINE)
// q = q.Equals("host_type", HOST_TYPE_HYPERVISOR)
// q = q.IsTrue("enabled")
//
// rows, err := q.Rows()
// if err != nil {
// return "", err
// }
// defer rows.Close()
//
// var hostId string
// hostIds := make([]string, 0)
// for rows.Next() {
// err = rows.Scan(&hostId)
// if err != nil {
// return "", err
// }
// hostIds = append(hostIds, hostId)
// }
//
// ring := hashring.New(hostIds)
// ret, _ := ring.GetNode(key)
// return ret, nil
// }
//
// func (manager *SHostManager) GetEsxiAgentHost(key string) (*SHost, error) {
// hostId, err := manager.GetEsxiAgentHostId(key)
// if err != nil {
// return nil, err
// }
// return manager.FetchHostById(hostId), nil
// }
//
// func (host *SHost) GetEsxiAgentHost() (*SHost, error) {
// return HostManager.GetEsxiAgentHost(host.Id)
// }
func (self *SHost) IsBaremetalAgentReady() bool {
return self.isAgentReady(api.AgentTypeBaremetal)
}
func (self *SHost) BaremetalSyncRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
return self.doAgentRequest(api.AgentTypeBaremetal, ctx, method, url, headers, body)
}
func (self *SHost) IsEsxiAgentReady() bool {
return self.isAgentReady(api.AgentTypeEsxi)
}
func (self *SHost) EsxiRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
return self.doAgentRequest(api.AgentTypeEsxi, ctx, method, url, headers, body)
}
func (self *SHost) isAgentReady(agentType api.TAgentType) bool {
agent := BaremetalagentManager.GetAgent(agentType, self.ZoneId)
if agent == nil {
log.Errorf("%s ready: false", agentType)
return false
}
return true
}
func (self *SHost) doAgentRequest(agentType api.TAgentType, ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
agent := BaremetalagentManager.GetAgent(agentType, self.ZoneId)
if agent == nil {
return nil, fmt.Errorf("no valid %s", agentType)
}
serviceUrl := agent.ManagerUri
if url[0] != '/' && serviceUrl[len(serviceUrl)-1] != '/' {
serviceUrl += "/"
}
url = serviceUrl + url
_, data, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, method, url, headers, body, false)
return data, err
}
func (manager *SHostManager) GetHostByIp(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 (self *SHost) getCloudProviderInfo() SCloudProviderInfo {
var region *SCloudregion
zone, _ := self.GetZone()
if zone != nil {
region, _ = zone.GetRegion()
}
provider := self.GetCloudprovider()
return MakeCloudProviderInfo(region, zone, provider)
}
func (self *SHost) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
desc := self.SEnabledStatusInfrasResourceBase.GetShortDesc(ctx)
info := self.getCloudProviderInfo()
desc.Update(jsonutils.Marshal(&info))
return desc
}
func (self *SHost) MarkGuestUnknown(userCred mcclient.TokenCredential) {
guests, _ := self.GetGuests()
for _, guest := range guests {
guest.SetStatus(userCred, api.VM_UNKNOWN, "host offline")
}
}
func (manager *SHostManager) PingDetectionTask(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
deadline := time.Now().Add(-1 * time.Duration(options.Options.HostOfflineMaxSeconds) * time.Second)
q := manager.Query().Equals("host_status", api.HOST_ONLINE).
Equals("host_type", api.HOST_TYPE_HYPERVISOR)
q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("last_ping_at")),
sqlchemy.LT(q.Field("last_ping_at"), deadline)))
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(userCred)
}()
}
}
func (self *SHost) IsPrepaidRecycleResource() bool {
return self.ResourceType == api.HostResourceTypePrepaidRecycle
}
func (host *SHost) AllowPerformSetSchedtag(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return AllowPerformSetResourceSchedtag(host, ctx, userCred, query, data)
}
func (host *SHost) PerformSetSchedtag(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return PerformSetResourceSchedtag(host, ctx, userCred, query, data)
}
func (host *SHost) GetDynamicConditionInput() *jsonutils.JSONDict {
return jsonutils.Marshal(host).(*jsonutils.JSONDict)
}
func (host *SHost) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) {
ret, err := host.SEnabledStatusInfrasResourceBase.PerformStatus(ctx, userCred, query, input)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformStatus")
}
host.ClearSchedDescCache()
return ret, nil
}
func (host *SHost) GetSchedtagJointManager() ISchedtagJointManager {
return HostschedtagManager
}
func (host *SHost) AllowPerformHostExitMaintenance(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, host, "host maintenance")
}
func (host *SHost) PerformHostExitMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(host.Status, []string{api.BAREMETAL_MAINTAIN_FAIL, api.BAREMETAL_MAINTAINING}) {
return nil, httperrors.NewInvalidStatusError("host status %s can't exit maintenance", host.Status)
}
err := host.SetStatus(userCred, api.HOST_STATUS_RUNNING, "exit maintenance")
if err != nil {
return nil, err
}
return nil, nil
}
func (host *SHost) AllowPerformHostMaintenance(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, host, "host maintenance")
}
func (host *SHost) PerformHostMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if host.HostType != api.HOST_TYPE_HYPERVISOR {
return nil, httperrors.NewBadRequestError("host type %s can't do host maintenance", host.HostType)
}
if host.HostStatus == api.BAREMETAL_START_MAINTAIN {
return nil, httperrors.NewBadRequestError("unsupport on host status %s", host.HostStatus)
}
var preferHostId string
preferHost, _ := data.GetString("prefer_host")
if len(preferHost) > 0 {
iHost, _ := HostManager.FetchByIdOrName(userCred, preferHost)
if iHost == nil {
return nil, httperrors.NewBadRequestError("Host %s not found", preferHost)
}
host := iHost.(*SHost)
preferHostId = host.Id
err := host.IsAssignable(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(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(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++ {
if guests[i].isInReconcile(userCred) {
log.Warningf("guest %s is in reconcile", guests[i].GetName())
continue
}
data := jsonutils.NewDict()
data.Set("purge_backup", jsonutils.JSONTrue)
_, 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,
)
} else {
guests[i].SetMetadata(ctx, "origin_status", guests[i].Status, userCred)
}
}
guests2 := host.GetGuestsBackupOnThisHost()
for i := 0; i < len(guests2); i++ {
if guests2[i].isInReconcile(userCred) {
log.Warningf("guest %s is in reconcile", guests2[i].GetName())
continue
}
data := jsonutils.NewDict()
data.Set("purge", jsonutils.JSONTrue)
data.Set("create", jsonutils.JSONTrue)
_, err := guests2[i].PerformDeleteBackup(ctx, userCred, nil, data)
if err != nil {
db.OpsLog.LogEvent(
&guests2[i], db.ACT_DELETE_BACKUP_FAILED, fmt.Sprintf("PerformDeleteBackup on host down: %s", err), userCred,
)
logclient.AddSimpleActionLog(
&guests2[i], logclient.ACT_DELETE_BACKUP,
fmt.Sprintf("PerformDeleteBackup on host down: %s", err), userCred, false,
)
}
}
}
func (host *SHost) migrateOnHostDown(ctx context.Context, userCred mcclient.TokenCredential) {
if host.GetMetadata(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(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(userCred mcclient.TokenCredential, status string, reason string) error {
err := host.SEnabledStatusInfrasResourceBase.SetStatus(userCred, status, reason)
if err != nil {
return err
}
host.ClearSchedDescCache()
return nil
}
func (host *SHost) StartMaintainTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
host.SetStatus(userCred, api.BAREMETAL_START_MAINTAIN, "start maintenance")
if task, err := taskman.TaskManager.NewTask(ctx, "HostMaintainTask", host, userCred, data, "", "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
}
return nil
}
func (host *SHost) IsMaintaining() bool {
return utils.IsInStringArray(host.Status, []string{api.BAREMETAL_START_MAINTAIN, api.BAREMETAL_MAINTAINING, api.BAREMETAL_MAINTAIN_FAIL})
}
// InstanceGroups returns the enabled group of guest in host and their frequency of occurrence
func (host *SHost) InstanceGroups() ([]SGroup, map[string]int, error) {
q := GuestManager.Query("id")
guestQ := q.Filter(sqlchemy.OR(sqlchemy.Equals(q.Field("host_id"), host.Id),
sqlchemy.Equals(q.Field("backup_host_id"), host.Id))).SubQuery()
groupQ := GroupguestManager.Query().SubQuery()
q = groupQ.Query().Join(guestQ, sqlchemy.Equals(guestQ.Field("id"), groupQ.Field("guest_id")))
groupguests := make([]SGroupguest, 0, 1)
err := db.FetchModelObjects(GroupguestManager, q, &groupguests)
if err != nil {
return nil, nil, err
}
groupIds, groupSet := make([]string, 0, len(groupguests)), make(map[string]int)
for i := range groupguests {
id := groupguests[i].GroupId
if _, ok := groupSet[id]; !ok {
groupIds = append(groupIds, id)
groupSet[id] = 1
continue
}
groupSet[id] += 1
}
if len(groupIds) == 0 {
return []SGroup{}, make(map[string]int), nil
}
groups := make([]SGroup, 0, len(groupIds))
q = GroupManager.Query().In("id", groupIds).IsTrue("enabled")
err = db.FetchModelObjects(GroupManager, q, &groups)
if err != nil {
return nil, nil, err
}
retSet := make(map[string]int)
for i := range groups {
retSet[groups[i].GetId()] = groupSet[groups[i].GetId()]
}
return groups, retSet, nil
}
func (host *SHost) setIpmiIp(userCred mcclient.TokenCredential, ipAddr string) error {
if host.IpmiIp == ipAddr {
return nil
}
diff, err := db.Update(host, func() error {
host.IpmiIp = ipAddr
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred)
return nil
}
func (host *SHost) setAccessIp(userCred mcclient.TokenCredential, ipAddr string) error {
if host.AccessIp == ipAddr {
return nil
}
diff, err := db.Update(host, func() error {
host.AccessIp = ipAddr
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred)
return nil
}
func (host *SHost) setAccessMac(userCred mcclient.TokenCredential, mac string) error {
mac = netutils.FormatMacAddr(mac)
if host.AccessMac == mac {
return nil
}
diff, err := db.Update(host, func() error {
host.AccessMac = mac
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred)
return nil
}
func (host *SHost) GetIpmiInfo() (types.SIPMIInfo, error) {
info := types.SIPMIInfo{}
if host.IpmiInfo != nil {
err := host.IpmiInfo.Unmarshal(&info)
if err != nil {
return info, errors.Wrap(err, "host.IpmiInfo.Unmarshal")
}
}
return info, nil
}
func (host *SHost) GetNics() []*types.SNic {
netifs := host.GetNetInterfaces()
nicInfos := []*types.SNic{}
if netifs != nil && len(netifs) > 0 {
for i := 0; i < len(netifs); i += 1 {
desc := netifs[i].getBaremetalJsonDesc()
if desc == nil {
log.Errorf("netif %s get baremetal desc failed", netifs[i].GetId())
continue
}
nicInfo := new(types.SNic)
desc.Unmarshal(nicInfo)
nicInfos = append(nicInfos, nicInfo)
}
}
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 (self *SHost) AllowGetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowGetSpec(userCred, self, "jnlp")
}
func (self *SHost) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
url := fmt.Sprintf("/baremetals/%s/jnlp", self.Id)
header := mcclient.GetTokenHeaders(userCred)
resp, err := self.BaremetalSyncRequest(ctx, "POST", url, header, nil)
if err != nil {
return nil, errors.Wrap(err, "BaremetalSyncRequest")
}
return resp, nil
}
func (self *SHost) AllowPerformInsertIso(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "insert-iso")
}
func (self *SHost) PerformInsertIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
imageStr, err := data.GetString("image")
image, err := CachedimageManager.getImageInfo(ctx, userCred, imageStr, false)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2("image", imageStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
if image.Status != cloudprovider.IMAGE_STATUS_ACTIVE {
return nil, httperrors.NewInvalidStatusError("Image status is not active")
}
boot := jsonutils.QueryBoolean(data, "boot", false)
return nil, self.StartInsertIsoTask(ctx, userCred, image.Id, boot, "")
}
return nil, httperrors.NewInvalidStatusError("Cannot do insert-iso in status %s", self.Status)
}
func (self *SHost) StartInsertIsoTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, boot bool, parentTaskId string) error {
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(imageId), "image_id")
if boot {
data.Add(jsonutils.JSONTrue, "boot")
}
data.Add(jsonutils.NewString(api.BAREMETAL_CDROM_ACTION_INSERT), "action")
self.SetStatus(userCred, api.BAREMETAL_START_INSERT_ISO, "start insert iso task")
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCdromTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (self *SHost) AllowPerformEjectIso(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "eject-iso")
}
func (self *SHost) PerformEjectIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
return nil, self.StartEjectIsoTask(ctx, userCred, "")
}
return nil, httperrors.NewInvalidStatusError("Cannot do eject-iso in status %s", self.Status)
}
func (self *SHost) StartEjectIsoTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(api.BAREMETAL_CDROM_ACTION_EJECT), "action")
self.SetStatus(userCred, api.BAREMETAL_START_EJECT_ISO, "start eject iso task")
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCdromTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (self *SHost) AllowPerformSyncConfig(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "sync-config")
}
func (self *SHost) PerformSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.HostType != api.HOST_TYPE_BAREMETAL {
return nil, httperrors.NewBadRequestError("Cannot sync config a non-baremetal host")
}
self.SetStatus(userCred, api.BAREMETAL_SYNCING_STATUS, "")
return nil, self.StartSyncConfig(ctx, userCred, "")
}
func (self *SHost) StartSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
return self.startSyncConfig(ctx, userCred, parentTaskId, false)
}
func (self *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", self, 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 (self *SHost) StartSyncTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
if task, err := taskman.TaskManager.NewTask(ctx, "HostSyncTask", self, 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(rbacutils.ScopeDomain, ownerId, zone, nil)
keys := quotas.SDomainRegionalCloudResourceKeys{}
keys.SBaseDomainQuotaKeys = zoneKeys.SBaseDomainQuotaKeys
keys.SRegionalBaseKeys = zoneKeys.SRegionalBaseKeys
return keys
}
func (model *SHost) GetQuotaKeys() quotas.SDomainRegionalCloudResourceKeys {
zone, _ := model.GetZone()
manager := model.GetCloudprovider()
ownerId := model.GetOwnerId()
zoneKeys := fetchZonalQuotaKeys(rbacutils.ScopeDomain, ownerId, zone, manager)
keys := quotas.SDomainRegionalCloudResourceKeys{}
keys.SBaseDomainQuotaKeys = zoneKeys.SBaseDomainQuotaKeys
keys.SRegionalBaseKeys = zoneKeys.SRegionalBaseKeys
keys.SCloudResourceBaseKeys = zoneKeys.SCloudResourceBaseKeys
return keys
}
func (host *SHost) GetUsages() []db.IUsage {
if host.Deleted {
return nil
}
usage := SInfrasQuota{Host: 1}
keys := host.GetQuotaKeys()
usage.SetKeys(keys)
return []db.IUsage{
&usage,
}
}
func (host *SHost) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.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) AllowPerformSetReservedResourceForIsolatedDevice(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, input api.IsolatedDeviceReservedResourceInput,
) bool {
return db.IsDomainAllowPerform(userCred, host, "set-reserved-resource-for-isolated-device")
}
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(userCred mcclient.TokenCredential) error {
if db.IsAdminAllowPerform(userCred, host, "assign-host") {
return nil
} else if db.IsDomainAllowPerform(userCred, host, "assign-host") &&
(userCred.GetProjectDomainId() == host.DomainId ||
host.PublicScope == string(rbacutils.ScopeSystem) ||
(host.PublicScope == string(rbacutils.ScopeDomain) && utils.IsInStringArray(userCred.GetProjectDomainId(), host.GetSharedDomains()))) {
return nil
} else {
return httperrors.NewNotSufficientPrivilegeError("Only system admin can assign host")
}
}
func (self *SHost) PerformProbeIsolatedDevices(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.GetHostDriver().RequestProbeIsolatedDevices(ctx, userCred, self, data)
}
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)
}