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