// 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 ( "bytes" "context" "database/sql" "fmt" "regexp" "strings" "time" "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/billing" "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/util/osprofile" "yunion.io/x/pkg/util/pinyinutils" "yunion.io/x/pkg/util/rbacscope" "yunion.io/x/pkg/util/regutils" "yunion.io/x/pkg/util/timeutils" "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" imageapi "yunion.io/x/onecloud/pkg/apis/image" schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" "yunion.io/x/onecloud/pkg/cloudcommon/cmdline" "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/cloudcommon/policy" "yunion.io/x/onecloud/pkg/cloudcommon/userdata" "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/compute/sshkeys" devtool_utils "yunion.io/x/onecloud/pkg/devtool/utils" "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/image" "yunion.io/x/onecloud/pkg/util/bitmap" "yunion.io/x/onecloud/pkg/util/logclient" "yunion.io/x/onecloud/pkg/util/netutils2" "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/seclib2" "yunion.io/x/onecloud/pkg/util/stringutils2" ) // +onecloud:swagger-gen-model-singular=server // +onecloud:swagger-gen-model-plural=servers type SGuestManager struct { db.SVirtualResourceBaseManager db.SExternalizedResourceBaseManager SDeletePreventableResourceBaseManager SHostResourceBaseManager SBillingResourceBaseManager SNetworkResourceBaseManager SDiskResourceBaseManager SScalingGroupResourceBaseManager db.SMultiArchResourceBaseManager db.SRecordChecksumResourceBaseManager SHostnameResourceBaseManager db.SEncryptedResourceManager } var GuestManager *SGuestManager func init() { GuestManager = &SGuestManager{ SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager( SGuest{}, "guests_tbl", "server", "servers", ), SRecordChecksumResourceBaseManager: *db.NewRecordChecksumResourceBaseManager(), } GuestManager.SetVirtualObject(GuestManager) GuestManager.SetAlias("guest", "guests") GuestManager.NameRequireAscii = false notifyclient.AddNotifyDBHookResources(GuestManager.KeywordPlural(), GuestManager.AliasPlural()) } type SGuest struct { db.SVirtualResourceBase db.SExternalizedResourceBase SBillingResourceBase SDeletePreventableResourceBase db.SMultiArchResourceBase db.SRecordChecksumResourceBase SHostnameResourceBase SHostResourceBase `width:"36" charset:"ascii" nullable:"true" list:"user" get:"user" index:"true"` db.SEncryptedResource // CPU插槽(socket)的数量 CpuSockets int `nullable:"false" default:"1" list:"user" create:"optional"` // CPU核(core)的数量, VcpuCount = CpuSockets * (cores per socket),例如 2颗CPU,每颗CPU8核,则 VcpuCount=2*8=16 VcpuCount int `nullable:"false" default:"1" list:"user" create:"optional"` // 内存大小, 单位MB VmemSize int `nullable:"false" list:"user" create:"required"` // 启动顺序 BootOrder string `width:"8" charset:"ascii" nullable:"true" default:"cdn" list:"user" update:"user" create:"optional"` // 关机操作类型 // example: stop ShutdownBehavior string `width:"16" charset:"ascii" default:"stop" list:"user" update:"user" create:"optional"` // 关机收费模式 // example: keep_charging, stop_charging ShutdownMode string `width:"16" charset:"ascii" default:"keep_charging" list:"user"` // 秘钥对Id KeypairId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 备份机所在宿主机Id BackupHostId string `width:"36" charset:"ascii" nullable:"true" list:"user" get:"user"` BackupGuestStatus string `width:"36" charset:"ascii" nullable:"false" default:"init" list:"user" create:"optional" json:"backup_guest_status"` // 迁移或克隆的速度 ProgressMbps float64 `nullable:"false" default:"0" list:"user" create:"optional" update:"user" log:"skip"` Vga string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` Vdi string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` Machine string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` Bios string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // 操作系统类型 OsType string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` FlavorId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 安全组Id // example: default SecgrpId string `width:"36" charset:"ascii" nullable:"true" list:"user" get:"user" create:"optional"` // 管理员可见安全组Id AdminSecgrpId string `width:"36" charset:"ascii" nullable:"true" list:"domain" get:"domain"` SrcIpCheck tristate.TriState `default:"true" create:"optional" list:"user" update:"user"` SrcMacCheck tristate.TriState `default:"true" create:"optional" list:"user" update:"user"` // 虚拟化技术 // example: kvm Hypervisor string `width:"16" charset:"ascii" nullable:"false" default:"kvm" list:"user" create:"required"` // 套餐名称 InstanceType string `width:"64" charset:"utf8" nullable:"true" list:"user" create:"optional"` SshableLastState tristate.TriState `default:"false" list:"user"` IsDaemon tristate.TriState `default:"false" list:"admin" create:"admin_optional" update:"admin"` // 最大内网带宽 InternetMaxBandwidthOut int `nullable:"true" list:"user" create:"optional"` // 磁盘吞吐量 Throughput int `nullable:"true" list:"user" create:"optional"` QgaStatus string `width:"36" charset:"ascii" nullable:"false" default:"unknown" list:"user" create:"optional"` // power_states limit in [on, off, unknown] PowerStates string `width:"36" charset:"ascii" nullable:"false" default:"unknown" list:"user" create:"optional"` // Used for guest rescue RescueMode bool `nullable:"false" default:"false" list:"user" create:"optional"` } func (manager *SGuestManager) GetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*apis.StatusStatistic, error) { ret, err := manager.SVirtualResourceBaseManager.GetPropertyStatistics(ctx, userCred, query) if err != nil { return nil, err } q := manager.Query() q, err = db.ListItemQueryFilters(manager, ctx, q, userCred, query, policy.PolicyActionList) if err != nil { return nil, err } sq := q.SubQuery() statQ := sq.Query(sqlchemy.SUM("total_cpu_count", sq.Field("vcpu_count")), sqlchemy.SUM("total_mem_size_mb", sq.Field("vmem_size"))) err = statQ.First(ret) if err != nil { return ret, err } diskQ := DiskManager.Query() gdsSQ := GuestdiskManager.Query().SubQuery() diskQ = diskQ.Join(gdsSQ, sqlchemy.Equals(diskQ.Field("id"), gdsSQ.Field("disk_id"))). Join(sq, sqlchemy.Equals(gdsSQ.Field("guest_id"), sq.Field("id"))) diskSQ := diskQ.SubQuery() return ret, diskSQ.Query(sqlchemy.SUM("total_disk_size_mb", diskSQ.Field("disk_size"))).First(ret) } // 云主机实例列表 func (manager *SGuestManager) ListItemFilter( ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ServerListInput, ) (*sqlchemy.SQuery, error) { var err error q, err = manager.SHostResourceBaseManager.ListItemFilter(ctx, q, userCred, query.HostFilterListInput) if err != nil { return nil, errors.Wrap(err, "SHostResourceBaseManager.ListItemFilter") } q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter") } q, err = manager.SDeletePreventableResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DeletePreventableResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "SDeletePreventableResourceBaseManager.ListItemFilter") } q, err = manager.SBillingResourceBaseManager.ListItemFilter(ctx, q, userCred, query.BillingResourceListInput) if err != nil { return nil, errors.Wrap(err, "SBillingResourceBaseManager.ListItemFilter") } q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput) if err != nil { return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter") } q, err = manager.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, query.MultiArchResourceBaseListInput) if err != nil { return nil, errors.Wrap(err, "MultiArchResourceBaseListInput.ListItemFilter") } netQ := GuestnetworkManager.Query("guest_id").Snapshot() netQ, err = manager.SNetworkResourceBaseManager.ListItemFilter(ctx, netQ, userCred, query.NetworkFilterListInput) if err != nil { return nil, errors.Wrap(err, "SNetworkResourceBaseManager.ListItemFilter") } if netQ.IsAltered() { q = q.In("id", netQ.SubQuery()) } //diskQ := GuestdiskManager.Query("guest_id").Snapshot() //diskQ, err = manager.SDiskResourceBaseManager.ListItemFilter(ctx, diskQ, userCred, query.DiskFilterListInput) //if err != nil { // return nil, errors.Wrap(err, "SDiskResourceBaseManager.ListItemFilter") //} //if diskQ.IsAltered() { // q = q.In("id", diskQ.SubQuery()) //} scalingGroupQ := ScalingGroupGuestManager.Query("guest_id").Snapshot() scalingGroupQ, err = manager.SScalingGroupResourceBaseManager.ListItemFilter(ctx, scalingGroupQ, userCred, query.ScalingGroupFilterListInput) if err != nil { return nil, errors.Wrap(err, "SScaligGroupResourceBaseManager.ListItemFilter") } if scalingGroupQ.IsAltered() { q = q.In("id", scalingGroupQ.SubQuery()) } hypervisorList := query.Hypervisor if len(hypervisorList) > 0 { q = q.In("hypervisor", hypervisorList) } resourceTypeStr := query.ResourceType if len(resourceTypeStr) > 0 { hosts := HostManager.Query().SubQuery() subq := hosts.Query(hosts.Field("id")) switch resourceTypeStr { case api.HostResourceTypeShared: subq = subq.Filter( sqlchemy.OR( sqlchemy.IsNullOrEmpty(hosts.Field("resource_type")), sqlchemy.Equals(hosts.Field("resource_type"), resourceTypeStr), ), ) default: subq = subq.Equals("resource_type", resourceTypeStr) } q = q.In("host_id", subq.SubQuery()) } hostFilter := query.GetAllGuestsOnHost if len(hostFilter) > 0 { host, _ := HostManager.FetchByIdOrName(ctx, nil, hostFilter) if host == nil { return nil, httperrors.NewResourceNotFoundError("host %s not found", hostFilter) } q.Filter(sqlchemy.OR(sqlchemy.Equals(q.Field("host_id"), host.GetId()), sqlchemy.Equals(q.Field("backup_host_id"), host.GetId()))) } secgrpFilter := query.SecgroupId if len(secgrpFilter) > 0 { var notIn = false // HACK FOR NOT IN SECGROUP if strings.HasPrefix(secgrpFilter, "!") { secgrpFilter = secgrpFilter[1:] notIn = true } secgrpIds := []string{} secgrps := []SSecurityGroup{} sgq := SecurityGroupManager.Query() sgq = sgq.Filter(sqlchemy.OR(sqlchemy.Equals(sgq.Field("id"), secgrpFilter), sqlchemy.Equals(sgq.Field("name"), secgrpFilter))) if err := db.FetchModelObjects(SecurityGroupManager, sgq, &secgrps); err != nil { return nil, err } if len(secgrps) == 0 { return nil, httperrors.NewResourceNotFoundError("secgroup %s not found", secgrpFilter) } for _, secgrp := range secgrps { secgrpIds = append(secgrpIds, secgrp.Id) } isAdmin := false // admin := (query.VirtualResourceListInput.Admin != nil && *query.VirtualResourceListInput.Admin) allowScope, _ := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList) if allowScope == rbacscope.ScopeSystem || allowScope == rbacscope.ScopeDomain { isAdmin = true } filters := []sqlchemy.ICondition{} if notIn { filters = append(filters, sqlchemy.NotIn(q.Field("id"), GuestsecgroupManager.Query("guest_id").In("secgroup_id", secgrpIds).SubQuery())) filters = append(filters, sqlchemy.NotIn(q.Field("secgrp_id"), secgrpIds)) if isAdmin { filters = append(filters, sqlchemy.NotIn(q.Field("admin_secgrp_id"), secgrpIds)) } q = q.Filter(sqlchemy.AND(filters...)) } else { filters = append(filters, sqlchemy.In(q.Field("id"), GuestsecgroupManager.Query("guest_id").In("secgroup_id", secgrpIds).SubQuery())) filters = append(filters, sqlchemy.In(q.Field("secgrp_id"), secgrpIds)) if isAdmin { filters = append(filters, sqlchemy.In(q.Field("admin_secgrp_id"), secgrpIds)) } q = q.Filter(sqlchemy.OR(filters...)) } } var eipMode string usableServerForEipFilter := query.UsableServerForEip if len(usableServerForEipFilter) > 0 { eipObj, err := ElasticipManager.FetchByIdOrName(ctx, userCred, usableServerForEipFilter) if err != nil { if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError("eip %s not found", usableServerForEipFilter) } return nil, httperrors.NewGeneralError(err) } eip := eipObj.(*SElasticip) if eip.GetProviderName() == api.CLOUD_PROVIDER_AWS { eipMode = api.EIP_MODE_STANDALONE_EIP } if len(eip.NetworkId) > 0 { sq := GuestnetworkManager.Query("guest_id").Equals("network_id", eip.NetworkId).SubQuery() q = q.NotIn("id", sq) if cp := eip.GetCloudprovider(); cp == nil || cp.Provider == api.CLOUD_PROVIDER_ONECLOUD { gnq := GuestnetworkManager.Query().SubQuery() nq := NetworkManager.Query().SubQuery() wq := WireManager.Query().SubQuery() vq := VpcManager.Query().SubQuery() q.Join(gnq, sqlchemy.Equals(gnq.Field("guest_id"), q.Field("id"))) q.Join(nq, sqlchemy.Equals(nq.Field("id"), gnq.Field("network_id"))) q.Join(wq, sqlchemy.Equals(wq.Field("id"), nq.Field("wire_id"))) q.Join(vq, sqlchemy.Equals(vq.Field("id"), wq.Field("vpc_id"))) q.Filter(sqlchemy.IsNullOrEmpty(gnq.Field("eip_id"))) q.Filter(sqlchemy.NotEquals(vq.Field("id"), api.DEFAULT_VPC_ID)) // vpc provider thing will be handled ok below } } hostTable := HostManager.Query().SubQuery() zoneTable := ZoneManager.Query().SubQuery() hostQ := hostTable.Query(hostTable.Field("id")) hostQ = hostQ.Join(zoneTable, sqlchemy.Equals(zoneTable.Field("id"), hostTable.Field("zone_id"))) if eip.ManagerId != "" { hostQ = hostQ.Equals("manager_id", eip.ManagerId) } else { hostQ = hostQ.IsNullOrEmpty("manager_id") } region, err := eip.GetRegion() if err != nil { return nil, httperrors.NewGeneralError(errors.Wrapf(err, "eip.GetRegion")) } regionTable := CloudregionManager.Query().SubQuery() sq := hostQ.Join(regionTable, sqlchemy.Equals(zoneTable.Field("cloudregion_id"), regionTable.Field("id"))). Filter(sqlchemy.Equals(regionTable.Field("id"), region.GetId())).SubQuery() q = q.In("host_id", sq) } if len(query.IpAddrs) > 0 { grpnets := GroupnetworkManager.Query().SubQuery() vipq := GroupguestManager.Query("guest_id") conditions := []sqlchemy.ICondition{} for _, ipAddr := range query.IpAddrs { conditions = append(conditions, sqlchemy.Regexp(grpnets.Field("ip_addr"), ipAddr)) conditions = append(conditions, sqlchemy.Regexp(grpnets.Field("ip6_addr"), ipAddr)) } vipq = vipq.Join(grpnets, sqlchemy.Equals(grpnets.Field("group_id"), vipq.Field("group_id"))).Filter( sqlchemy.OR(conditions...), ) grpeips := ElasticipManager.Query().Equals("associate_type", api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP).SubQuery() conditions = []sqlchemy.ICondition{} for _, ipAddr := range query.IpAddrs { conditions = append(conditions, sqlchemy.Regexp(grpeips.Field("ip_addr"), ipAddr)) } vipeipq := GroupguestManager.Query("guest_id") vipeipq = vipeipq.Join(grpeips, sqlchemy.Equals(grpeips.Field("associate_id"), vipeipq.Field("group_id"))).Filter( sqlchemy.OR(conditions...), ) gnQ := GuestnetworkManager.Query("guest_id") conditions = []sqlchemy.ICondition{} for _, ipAddr := range query.IpAddrs { conditions = append(conditions, sqlchemy.Regexp(gnQ.Field("ip_addr"), ipAddr)) conditions = append(conditions, sqlchemy.Regexp(gnQ.Field("ip6_addr"), ipAddr)) } gn := gnQ.Filter(sqlchemy.OR(conditions...)) guestEipQ := ElasticipManager.Query("associate_id").Equals("associate_type", api.EIP_ASSOCIATE_TYPE_SERVER) conditions = []sqlchemy.ICondition{} for _, ipAddr := range query.IpAddrs { conditions = append(conditions, sqlchemy.Regexp(guestEipQ.Field("ip_addr"), ipAddr)) } guestEip := guestEipQ.Filter(sqlchemy.OR(conditions...)) metadataQ := db.Metadata.Query("obj_id") conditions = []sqlchemy.ICondition{} for _, ipAddr := range query.IpAddrs { conditions = append(conditions, sqlchemy.AND( sqlchemy.Regexp(metadataQ.Field("value"), ipAddr), sqlchemy.Equals(metadataQ.Field("key"), "sync_ips"), sqlchemy.Equals(metadataQ.Field("obj_type"), "server"), )) } metadataQ = metadataQ.Filter(sqlchemy.OR(conditions...)) q = q.Filter(sqlchemy.OR( sqlchemy.In(q.Field("id"), gn.SubQuery()), sqlchemy.In(q.Field("id"), guestEip.SubQuery()), sqlchemy.In(q.Field("id"), vipq.SubQuery()), sqlchemy.In(q.Field("id"), vipeipq.SubQuery()), sqlchemy.In(q.Field("id"), metadataQ.SubQuery()), )) } diskFilter := query.AttachableServersForDisk if len(diskFilter) > 0 { diskI, _ := DiskManager.FetchByIdOrName(ctx, userCred, diskFilter) if diskI == nil { return nil, httperrors.NewResourceNotFoundError("disk %s not found", diskFilter) } disk := diskI.(*SDisk) guestdisks := GuestdiskManager.Query().SubQuery() count, err := guestdisks.Query().Equals("disk_id", disk.Id).CountWithError() if err != nil { return nil, httperrors.NewInternalServerError("checkout guestdisk count fail %s", err) } if count > 0 { sgq := guestdisks.Query(guestdisks.Field("guest_id")).Equals("disk_id", disk.Id).SubQuery() q = q.Filter(sqlchemy.In(q.Field("id"), sgq)) } else { hosts := HostManager.Query().SubQuery() hoststorages := HoststorageManager.Query().SubQuery() storages := StorageManager.Query().SubQuery() sq := hosts.Query(hosts.Field("id")). Join(hoststorages, sqlchemy.Equals(hoststorages.Field("host_id"), hosts.Field("id"))). Join(storages, sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id"))). Filter(sqlchemy.Equals(storages.Field("id"), disk.StorageId)).SubQuery() q = q.In("host_id", sq) } } withEip := (query.WithEip != nil && *query.WithEip) withoutEip := (query.WithoutEip != nil && *query.WithoutEip) || (query.EipAssociable != nil && *query.EipAssociable) if withEip || withoutEip { eips := ElasticipManager.Query().SubQuery() sq := eips.Query(eips.Field("associate_id")).Equals("associate_type", api.EIP_ASSOCIATE_TYPE_SERVER) sq = sq.IsNotNull("associate_id").IsNotEmpty("associate_id") if len(eipMode) > 0 { sq = sq.Equals("mode", eipMode) } if withEip { q = q.In("id", sq) } else if withoutEip { q = q.NotIn("id", sq) } } if query.EipAssociable != nil { sq1 := NetworkManager.Query("id") sq2 := WireManager.Query().SubQuery() sq3 := VpcManager.Query().SubQuery() sq1 = sq1.Join(sq2, sqlchemy.Equals(sq1.Field("wire_id"), sq2.Field("id"))) sq1 = sq1.Join(sq3, sqlchemy.Equals(sq2.Field("vpc_id"), sq3.Field("id"))) cond1 := []string{api.VPC_EXTERNAL_ACCESS_MODE_EIP, api.VPC_EXTERNAL_ACCESS_MODE_EIP_DISTGW} if *query.EipAssociable { sq1 = sq1.Filter(sqlchemy.In(sq3.Field("external_access_mode"), cond1)) } else { sq1 = sq1.Filter(sqlchemy.NotIn(sq3.Field("external_access_mode"), cond1)) } sq := GuestnetworkManager.Query("guest_id").In("network_id", sq1) q = q.In("id", sq) } devTypeQ := func(q *sqlchemy.SQuery, checkType, backup *bool, dType string, conditions []sqlchemy.ICondition) []sqlchemy.ICondition { if checkType != nil { isodev := IsolatedDeviceManager.Query().SubQuery() isodevCons := []sqlchemy.ICondition{sqlchemy.IsNotNull(isodev.Field("guest_id"))} if len(dType) > 0 { isodevCons = append(isodevCons, sqlchemy.Startswith(isodev.Field("dev_type"), dType)) } sgq := isodev.Query(isodev.Field("guest_id")).Filter(sqlchemy.AND(isodevCons...)) cond := sqlchemy.NotIn if *checkType { cond = sqlchemy.In } if dType == "GPU" { sq := ServerSkuManager.Query("name").GT("gpu_count", 0).Distinct().SubQuery() if backup != nil { afterAnd := sqlchemy.AND if *backup { backupCond := sqlchemy.IsNotEmpty(q.Field("backup_host_id")) conditions = append(conditions, afterAnd(cond(q.Field("instance_type"), sq), backupCond)) } else { backupCond := sqlchemy.IsEmpty(q.Field("backup_host_id")) conditions = append(conditions, afterAnd(cond(q.Field("instance_type"), sq), backupCond)) } } else { conditions = append(conditions, cond(q.Field("instance_type"), sq)) } } else { if backup != nil { afterAnd := sqlchemy.AND if *backup { backupCond := sqlchemy.IsNotEmpty(q.Field("backup_host_id")) conditions = append(conditions, afterAnd(cond(q.Field("id"), sgq), backupCond)) } else { backupCond := sqlchemy.IsEmpty(q.Field("backup_host_id")) conditions = append(conditions, afterAnd(cond(q.Field("id"), sgq), backupCond)) } } else { conditions = append(conditions, cond(q.Field("id"), sgq)) } } return conditions } return conditions } conditions := []sqlchemy.ICondition{} if len(query.ServerType) > 0 { var trueVal, falseVal = true, false for _, serverType := range query.ServerType { switch serverType { case "normal": query.Normal = &falseVal query.Backup = &falseVal case "gpu": query.Gpu = &trueVal query.Backup = &falseVal sq := ServerSkuManager.Query("name").IsNotEmpty("gpu_spec").Distinct() conditions = append(conditions, sqlchemy.In(q.Field("instance_type"), sq.SubQuery())) case "backup": query.Gpu = &falseVal query.Backup = &trueVal case "usb": query.Usb = &trueVal query.Backup = &falseVal default: query.CustomDevType = serverType query.Backup = &falseVal } conditions = devTypeQ(q, query.Normal, query.Backup, "", conditions) conditions = devTypeQ(q, query.Gpu, query.Backup, "GPU", conditions) conditions = devTypeQ(q, query.Usb, query.Backup, api.USB_TYPE, conditions) if len(query.CustomDevType) > 0 { ct := true conditions = devTypeQ(q, &ct, query.Backup, query.CustomDevType, conditions) } query.Normal = nil query.Gpu = nil query.Backup = nil } } if len(conditions) > 0 { q = q.Filter(sqlchemy.OR(conditions...)) } groupFilter := query.GroupId if len(groupFilter) != 0 { groupObj, err := GroupManager.FetchByIdOrName(ctx, userCred, groupFilter) if err != nil { return nil, httperrors.NewNotFoundError("group %s not found", groupFilter) } // queryDict.Add(jsonutils.NewString(groupObj.GetId()), "group") ggSub := GroupguestManager.Query("guest_id").Equals("group_id", groupObj.GetId()).SubQuery() q = q.Join(ggSub, sqlchemy.Equals(ggSub.Field("guest_id"), q.Field("id"))) } /*orderByDisk := query.OrderByDisk if orderByDisk == "asc" || orderByDisk == "desc" { guestdisks := GuestdiskManager.Query().SubQuery() disks := DiskManager.Query().SubQuery() guestdiskQ := guestdisks.Query( guestdisks.Field("guest_id"), sqlchemy.SUM("disks_size", disks.Field("disk_size")), ) guestdiskQ = guestdiskQ.LeftJoin(disks, sqlchemy.Equals(guestdiskQ.Field("disk_id"), disks.Field("id"))) guestdiskSQ := guestdiskQ.GroupBy(guestdiskQ.Field("guest_id")).SubQuery() q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(q.Field("id"), guestdiskSQ.Field("guest_id"))) switch orderByDisk { case "asc": q = q.Asc(guestdiskSQ.Field("disks_size")) case "desc": q = q.Desc(guestdiskSQ.Field("disks_size")) } }*/ if len(query.OsType) > 0 { q = q.In("os_type", query.OsType) } if len(query.OsDist) > 0 { metaSQ := db.Metadata.Query().Equals("key", "os_distribution").In("value", query.OsDist).SubQuery() q = q.Join(metaSQ, sqlchemy.Equals(q.Field("id"), metaSQ.Field("obj_id"))) } if len(query.VcpuCount) > 0 { q = q.In("vcpu_count", query.VcpuCount) } if len(query.VmemSize) > 0 { q = q.In("vmem_size", query.VmemSize) } if len(query.BootOrder) > 0 { q = q.In("boot_order", query.BootOrder) } if len(query.Vga) > 0 { q = q.In("vga", query.Vga) } if len(query.Vdi) > 0 { q = q.In("vdi", query.Vdi) } if len(query.Machine) > 0 { q = q.In("machine", query.Machine) } if len(query.Bios) > 0 { q = q.In("bios", query.Bios) } if query.SrcIpCheck != nil { if *query.SrcIpCheck { q = q.IsTrue("src_ip_check") } else { q = q.IsFalse("src_ip_check") } } if query.SrcMacCheck != nil { if *query.SrcMacCheck { q = q.IsTrue("src_mac_check") } else { q = q.IsFalse("src_mac_check") } } if len(query.InstanceType) > 0 { q = q.In("instance_type", query.InstanceType) } if query.WithHost != nil { if *query.WithHost { q = q.IsNotEmpty("host_id") } else { q = q.IsNullOrEmpty("host_id") } } return q, nil } func (manager *SGuestManager) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition { var sq *sqlchemy.SSubQuery if len(like) > 1 { switch { case strings.Contains(like, "."): sq = GuestnetworkManager.Query("guest_id").Contains("ip_addr", like).SubQuery() case strings.Contains(like, ":"): sq = GuestnetworkManager.Query("guest_id").Contains("mac_addr", like).SubQuery() } } if sq != nil { return []sqlchemy.ICondition{sqlchemy.In(q.Field("id"), sq)} } return nil } func (manager *SGuestManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ServerListInput) (*sqlchemy.SQuery, error) { var err error q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput) if err != nil { return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields") } q, err = manager.SHostResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.HostFilterListInput) if err != nil { return nil, errors.Wrap(err, "SHostResourceBaseManager.OrderByExtraFields") } fields := manager.SNetworkResourceBaseManager.GetOrderByFields(query.NetworkFilterListInput) if db.NeedOrderQuery(fields) { netQ := GuestnetworkManager.Query("guest_id", "network_id").SubQuery() q = q.LeftJoin(netQ, sqlchemy.Equals(q.Field("id"), netQ.Field("guest_id"))).Distinct() q, err = manager.SNetworkResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.NetworkFilterListInput) if err != nil { return nil, errors.Wrap(err, "SNetworkResourceBaseManager.OrderByExtraFields") } } if db.NeedOrderQuery([]string{query.OrderByOsDist}) { meta := db.Metadata.Query().Equals("key", "os_distribution").SubQuery() q = q.LeftJoin(meta, sqlchemy.Equals(q.Field("id"), meta.Field("obj_id"))) db.OrderByFields(q, []string{query.OrderByOsDist}, []sqlchemy.IQueryField{meta.Field("value")}) } if db.NeedOrderQuery([]string{query.OrderByDisk}) { guestdisks := GuestdiskManager.Query().SubQuery() disks := DiskManager.Query().SubQuery() guestdiskQ := guestdisks.Query( guestdisks.Field("guest_id"), sqlchemy.SUM("disks_size", disks.Field("disk_size")), ) guestdiskQ = guestdiskQ.LeftJoin(disks, sqlchemy.Equals(guestdiskQ.Field("disk_id"), disks.Field("id"))) guestdiskSQ := guestdiskQ.GroupBy(guestdiskQ.Field("guest_id")).SubQuery() q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(q.Field("id"), guestdiskSQ.Field("guest_id"))) db.OrderByFields(q, []string{query.OrderByDisk}, []sqlchemy.IQueryField{guestdiskSQ.Field("disks_size")}) } if db.NeedOrderQuery([]string{query.OrderByIp}) { guestnet := GuestnetworkManager.Query("guest_id", "ip_addr").SubQuery() q.AppendField(q.QueryFields()...) q.AppendField(guestnet.Field("ip_addr")) q = q.LeftJoin(guestnet, sqlchemy.Equals(q.Field("id"), guestnet.Field("guest_id"))) db.OrderByFields(q, []string{query.OrderByIp}, []sqlchemy.IQueryField{sqlchemy.INET_ATON(q.Field("ip_addr"))}) } return q, nil } func (manager *SGuestManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { var err error q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } q, err = manager.SHostResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } if field == "os_dist" { metaQuery := db.Metadata.Query("obj_id", "value").Equals("key", "os_distribution").SubQuery() q = q.AppendField(metaQuery.Field("value", field)).Distinct() q = q.Join(metaQuery, sqlchemy.Equals(q.Field("id"), metaQuery.Field("obj_id"))) q.GroupBy(metaQuery.Field("value")) return q, nil } guestnets := GuestnetworkManager.Query("guest_id", "network_id").SubQuery() q = q.LeftJoin(guestnets, sqlchemy.Equals(q.Field("id"), guestnets.Field("guest_id"))) q, err = manager.SNetworkResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } guestdisks := GuestdiskManager.Query("guest_id", "disk_id").SubQuery() q = q.LeftJoin(guestdisks, sqlchemy.Equals(q.Field("id"), guestdisks.Field("guest_id"))) q, err = manager.SDiskResourceBaseManager.QueryDistinctExtraField(q, field) if err == nil { return q, nil } return q, httperrors.ErrNotFound } func (manager *SGuestManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) { switch resource { case NetworkManager.Keyword(): guestnets := GuestnetworkManager.Query("guest_id", "network_id").SubQuery() q = q.LeftJoin(guestnets, sqlchemy.Equals(q.Field("id"), guestnets.Field("guest_id"))) return manager.SNetworkResourceBaseManager.QueryDistinctExtraFields(q, resource, fields) } return q, httperrors.ErrNotFound } func (manager *SGuestManager) initHostname() error { guests := []SGuest{} q := manager.Query().IsNullOrEmpty("hostname") err := db.FetchModelObjects(manager, q, &guests) if err != nil { return errors.Wrapf(err, "db.FetchModelObjects") } for i := range guests { db.Update(&guests[i], func() error { hostname, _ := manager.SHostnameResourceBaseManager.ValidateHostname( guests[i].Hostname, guests[i].OsType, api.HostnameInput{ Hostname: guests[i].Name, }, ) guests[i].Hostname = hostname.Hostname return nil }) } return nil } func (manager *SGuestManager) clearSecgroups() error { guests := make([]SGuest, 0, 10) q := manager.Query() q = q.In("hypervisor", []string{api.HYPERVISOR_ESXI, api.HYPERVISOR_NUTANIX}).Filter( sqlchemy.OR( sqlchemy.IsNotEmpty(q.Field("secgrp_id")), sqlchemy.IsNotEmpty(q.Field("admin_secgrp_id")), ), ) err := db.FetchModelObjects(manager, q, &guests) if err != nil { return errors.Wrap(err, "db.FetchModelObjects") } // remove secgroup for esxi nutanix guest for i := range guests { db.Update(&guests[i], func() error { guests[i].SecgrpId = "" guests[i].AdminSecgrpId = "" return nil }) } return nil } func (manager *SGuestManager) initAdminSecgroupId() error { if len(options.Options.DefaultAdminSecurityGroupId) == 0 { return nil } adminSec, _ := SecurityGroupManager.FetchSecgroupById(options.Options.DefaultAdminSecurityGroupId) if adminSec == nil { return nil } adminSecId := adminSec.Id guests := make([]SGuest, 0, 10) q := manager.Query() q = q.In("hypervisor", []string{api.HYPERVISOR_KVM}).IsNullOrEmpty("admin_secgrp_id") err := db.FetchModelObjects(manager, q, &guests) if err != nil { return errors.Wrap(err, "db.FetchModelObjects") } // remove secgroup for esxi nutanix guest for i := range guests { db.Update(&guests[i], func() error { guests[i].AdminSecgrpId = adminSecId return nil }) } return nil } func (manager *SGuestManager) InitializeData() error { if err := manager.initHostname(); err != nil { return errors.Wrap(err, "initHostname") } if err := manager.clearSecgroups(); err != nil { return errors.Wrap(err, "cleanSecgroups") } if err := manager.initAdminSecgroupId(); err != nil { return errors.Wrap(err, "initAdminSecgroupId") } return nil } func (guest *SGuest) GetHypervisor() string { if len(guest.Hypervisor) == 0 { return api.HYPERVISOR_DEFAULT } else { return guest.Hypervisor } } func (guest *SGuest) GetHostType() string { return api.HYPERVISOR_HOSTTYPE[guest.Hypervisor] } func (guest *SGuest) GetDriver() IGuestDriver { hypervisor := guest.GetHypervisor() if !utils.IsInStringArray(hypervisor, api.HYPERVISORS) { log.Fatalf("Unsupported hypervisor %s", hypervisor) } return GetDriver(hypervisor) } func (guest *SGuest) validateDeleteCondition(ctx context.Context, isPurge bool) error { if guest.DisableDelete.IsTrue() { return httperrors.NewInvalidStatusError("Virtual server is locked, cannot delete") } if !isPurge && guest.IsNotDeletablePrePaid() { return httperrors.NewForbiddenError("not allow to delete prepaid server in valid status") } return guest.SVirtualResourceBase.ValidateDeleteCondition(ctx, nil) } func (guest *SGuest) ValidateDeleteCondition(ctx context.Context, info *api.ServerDetails) error { if gotypes.IsNil(info) { info = &api.ServerDetails{} host, err := guest.GetHost() if err != nil { return err } info.HostType = host.HostType info.HostEnabled = host.Enabled.Bool() info.HostStatus = host.HostStatus } if len(info.HostType) > 0 && guest.GetHypervisor() != api.HYPERVISOR_BAREMETAL { if !info.HostEnabled { return httperrors.NewInputParameterError("Cannot delete server on disabled host") } if info.HostStatus != api.HOST_ONLINE { return httperrors.NewInputParameterError("Cannot delete server on offline host") } } return guest.validateDeleteCondition(ctx, false) } func (guest *SGuest) GetDisksQuery() *sqlchemy.SQuery { return GuestdiskManager.Query().Equals("guest_id", guest.Id) } func (guest *SGuest) DiskCount() (int, error) { return guest.GetDisksQuery().CountWithError() } func (guest *SGuest) GetSystemDisk() (*SDisk, error) { q := DiskManager.Query().Equals("disk_type", api.DISK_TYPE_SYS) gs := GuestdiskManager.Query().SubQuery() q = q.Join(gs, sqlchemy.Equals(gs.Field("disk_id"), q.Field("id"))). Filter(sqlchemy.Equals(gs.Field("guest_id"), guest.Id)) count, err := q.CountWithError() if err != nil { return nil, err } if count > 1 { return nil, sqlchemy.ErrDuplicateEntry } if count == 0 { return nil, sql.ErrNoRows } disk := &SDisk{} err = q.First(disk) if err != nil { return nil, errors.Wrap(err, "q.First(disk)") } disk.SetModelManager(DiskManager, disk) return disk, nil } func (self *SGuest) GetDisks() ([]SDisk, error) { gds := GuestdiskManager.Query().SubQuery() sq := DiskManager.Query() q := sq.Join(gds, sqlchemy.Equals(gds.Field("disk_id"), sq.Field("id"))).Filter( sqlchemy.Equals(gds.Field("guest_id"), self.Id), ).Asc(gds.Field("index")) disks := []SDisk{} err := db.FetchModelObjects(DiskManager, q, &disks) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return disks, nil } func (guest *SGuest) GetGuestDisks() ([]SGuestdisk, error) { disks := make([]SGuestdisk, 0) q := guest.GetDisksQuery().Asc("index") err := db.FetchModelObjects(GuestdiskManager, q, &disks) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return disks, nil } func (guest *SGuest) GetGuestDisk(diskId string) *SGuestdisk { guestdisk, err := db.NewModelObject(GuestdiskManager) if err != nil { log.Errorf("new guestdisk model failed: %s", err) return nil } q := guest.GetDisksQuery() err = q.Equals("disk_id", diskId).First(guestdisk) if err != nil { log.Errorf("GetGuestDisk error: %s", err) return nil } return guestdisk.(*SGuestdisk) } func (guest *SGuest) GetNetworksQuery(netId string) *sqlchemy.SQuery { q := GuestnetworkManager.Query().Equals("guest_id", guest.Id) if len(netId) > 0 { q = q.Equals("network_id", netId) } return q } func (guest *SGuest) NetworkCount() (int, error) { return guest.GetNetworksQuery("").CountWithError() } func (guest *SGuest) GetVpc() (*SVpc, error) { q := guest.GetNetworksQuery("") guestnic := &SGuestnetwork{} err := q.First(guestnic) if err != nil { return nil, errors.Wrapf(err, "failed getting guest network of %s(%s)", guest.Name, guest.Id) } guestnic.SetModelManager(GuestnetworkManager, guestnic) network, err := guestnic.GetNetwork() if err != nil { return nil, errors.Wrapf(err, "GetVpc") } vpc, err := network.GetVpc() if err != nil { return nil, errors.Wrapf(err, "GetVpc") } return vpc, nil } func (guest *SGuest) IsOneCloudVpcNetwork() (bool, error) { gns, err := guest.GetNetworks("") if err != nil { return false, errors.Wrap(err, "GetNetworks") } for _, gn := range gns { n, _ := gn.GetNetwork() if n != nil && n.isOneCloudVpcNetwork() { return true, nil } } return false, nil } func (guest *SGuest) GetNetworks(netId string) ([]SGuestnetwork, error) { guestnics := make([]SGuestnetwork, 0) q := guest.GetNetworksQuery(netId).Asc("index") err := db.FetchModelObjects(GuestnetworkManager, q, &guestnics) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return guestnics, nil } func (guest *SGuest) GetSlaveNetworks() ([]SGuestnetwork, error) { guestnics := make([]SGuestnetwork, 0) q := guest.GetNetworksQuery("").IsNotEmpty("team_with") err := db.FetchModelObjects(GuestnetworkManager, q, &guestnics) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return guestnics, nil } func (guest *SGuest) ConvertEsxiNetworks(targetGuest *SGuest) error { gns, err := guest.GetNetworks("") if err != nil { return err } var i int for ; i < len(gns); i++ { _, err = db.Update(&gns[i], func() error { gns[i].GuestId = targetGuest.Id if gns[i].Driver != "e1000" && gns[i].Driver != "vmxnet3" { gns[i].Driver = "e1000" } return nil }) if err != nil { log.Errorf("update guestnetworks failed %s", err) break } } if err != nil { for j := 0; j < i; j++ { _, err = db.Update(&gns[j], func() error { gns[j].GuestId = guest.Id return nil }) if err != nil { log.Errorf("update guestnetworks failed %s", err) break } } } return err } func (guest *SGuest) getGuestnetworkByIpOrMac(ipAddr string, ip6Addr string, macAddr string) (*SGuestnetwork, error) { q := guest.GetNetworksQuery("") if len(ipAddr) > 0 { q = q.Equals("ip_addr", ipAddr) } if len(ip6Addr) > 0 { addr, err := netutils.NewIPV6Addr(ip6Addr) if err == nil { q = q.Equals("ip6_addr", addr.String()) } } if len(macAddr) > 0 { macAddr = netutils2.FormatMac(macAddr) q = q.Equals("mac_addr", macAddr) } guestnic := SGuestnetwork{} err := q.First(&guestnic) if err != nil { return nil, err } guestnic.SetModelManager(GuestnetworkManager, &guestnic) return &guestnic, nil } func (guest *SGuest) GetGuestnetworkByIp(ipAddr string) (*SGuestnetwork, error) { return guest.getGuestnetworkByIpOrMac(ipAddr, "", "") } func (guest *SGuest) GetGuestnetworkByIp6(ip6Addr string) (*SGuestnetwork, error) { return guest.getGuestnetworkByIpOrMac("", ip6Addr, "") } func (guest *SGuest) GetGuestnetworkByMac(macAddr string) (*SGuestnetwork, error) { return guest.getGuestnetworkByIpOrMac("", "", macAddr) } func (guest *SGuest) IsNetworkAllocated() bool { guestnics, err := guest.GetNetworks("") if err != nil { return false } for _, gn := range guestnics { if !gn.IsAllocated() { return false } } return true } func (guest *SGuest) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { if len(guest.SecgrpId) > 0 && len(options.Options.DefaultAdminSecurityGroupId) > 0 { adminSec, _ := SecurityGroupManager.FetchSecgroupById(options.Options.DefaultAdminSecurityGroupId) if adminSec != nil { guest.AdminSecgrpId = adminSec.Id } } guest.HostId = "" err := guest.SEncryptedResource.CustomizeCreate(ctx, userCred, ownerId, data, "server-"+pinyinutils.Text2Pinyin(guest.Name)) if err != nil { return errors.Wrap(err, "EncryptResourceBase.CustomizeCreate") } return guest.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } func (guest *SGuest) GetCloudproviderId() string { host, _ := guest.GetHost() if host != nil { return host.GetCloudproviderId() } return "" } func (guest *SGuest) GetHost() (*SHost, error) { if len(guest.HostId) > 0 && regutils.MatchUUID(guest.HostId) { host, err := HostManager.FetchById(guest.HostId) if err != nil { return nil, err } return host.(*SHost), nil } return nil, fmt.Errorf("empty host id") } func (guest *SGuest) SetHostId(userCred mcclient.TokenCredential, hostId string) error { if guest.HostId != hostId { diff, err := db.Update(guest, func() error { guest.HostId = hostId return nil }) if err != nil { return err } db.OpsLog.LogEvent(guest, db.ACT_UPDATE, diff, userCred) } return nil } func (guest *SGuest) SetHostIdWithBackup(userCred mcclient.TokenCredential, master, slave string) error { diff, err := db.Update(guest, func() error { guest.HostId = master guest.BackupHostId = slave return nil }) if err != nil { return err } db.OpsLog.LogEvent(guest, db.ACT_UPDATE, diff, userCred) return err } func (guest *SGuest) ValidateResizeDisk(disk *SDisk, storage *SStorage) error { return guest.GetDriver().ValidateResizeDisk(guest, disk, storage) } func ValidateMemData(vmemSize int, driver IGuestDriver) (int, error) { if vmemSize > 0 { maxVmemGb := driver.GetMaxVMemSizeGB() if vmemSize < 8 || vmemSize > maxVmemGb*1024 { return 0, httperrors.NewInputParameterError("Memory size must be 8MB ~ %d GB", maxVmemGb) } } return vmemSize, nil } func ValidateCpuData(vcpuCount int, driver IGuestDriver) (int, error) { maxVcpuCount := driver.GetMaxVCpuCount() if vcpuCount < 1 || vcpuCount > maxVcpuCount { return 0, httperrors.NewInputParameterError("CPU core count must be 1 ~ %d", maxVcpuCount) } return vcpuCount, nil } func ValidateMemCpuData(vmemSize, vcpuCount int, hypervisor string) (int, int, error) { if len(hypervisor) == 0 { hypervisor = api.HYPERVISOR_DEFAULT } driver := GetDriver(hypervisor) var err error vmemSize, err = ValidateMemData(vmemSize, driver) if err != nil { return 0, 0, err } vcpuCount, err = ValidateCpuData(vcpuCount, driver) if err != nil { return 0, 0, err } return vmemSize, vcpuCount, nil } func (self *SGuest) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerUpdateInput) (api.ServerUpdateInput, error) { if len(input.Name) > 0 && len(input.Name) < 2 { return input, httperrors.NewInputParameterError("name is too short") } // validate Hostname if len(input.Hostname) > 0 { if !regutils.MatchDomainName(input.Hostname) { return input, httperrors.NewInputParameterError("hostname should be a legal domain name") } } var err error input, err = self.GetDriver().ValidateUpdateData(ctx, self, userCred, input) if err != nil { return input, errors.Wrap(err, "GetDriver().ValidateUpdateData") } input.VirtualResourceBaseUpdateInput, err = self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput) if err != nil { return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData") } return input, nil } func serverCreateInput2ComputeQuotaKeys(input api.ServerCreateInput, ownerId mcclient.IIdentityProvider) SComputeResourceKeys { // input.Hypervisor must be set brand := guessBrandForHypervisor(input.Hypervisor) keys := GetDriver(input.Hypervisor).GetComputeQuotaKeys( rbacscope.ScopeProject, ownerId, brand, ) if len(input.PreferHost) > 0 { hostObj, _ := HostManager.FetchById(input.PreferHost) host := hostObj.(*SHost) zone, _ := host.GetZone() keys.ZoneId = zone.Id keys.RegionId = zone.CloudregionId } else if len(input.PreferZone) > 0 { zoneObj, _ := ZoneManager.FetchById(input.PreferZone) zone := zoneObj.(*SZone) keys.ZoneId = zone.Id keys.RegionId = zone.CloudregionId } else if len(input.PreferWire) > 0 { wireObj, _ := WireManager.FetchById(input.PreferWire) wire := wireObj.(*SWire) zone, _ := wire.GetZone() keys.ZoneId = zone.Id keys.RegionId = zone.CloudregionId } else if len(input.PreferRegion) > 0 { regionObj, _ := CloudregionManager.FetchById(input.PreferRegion) keys.RegionId = regionObj.GetId() } return keys } func (manager *SGuestManager) BatchPreValidate( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict, count int, ) error { input, err := manager.validateCreateData(ctx, userCred, ownerId, query, data) if err != nil { return errors.Wrap(err, "manager.validateCreateData") } if input.IsSystem == nil || !(*input.IsSystem) { err := manager.checkCreateQuota(ctx, userCred, ownerId, *input, input.Backup, count) if err != nil { return errors.Wrap(err, "manager.checkCreateQuota") } } return nil } func parseInstanceSnapshot(ctx context.Context, input *api.ServerCreateInput) (*api.ServerCreateInput, error) { ispi, err := InstanceSnapshotManager.FetchByIdOrName(ctx, nil, input.InstanceSnapshotId) if err == sql.ErrNoRows { return nil, httperrors.NewBadRequestError("can't find instance snapshot %s", input.InstanceSnapshotId) } if err != nil { return nil, httperrors.NewInternalServerError("fetch instance snapshot error %s", err) } isp := ispi.(*SInstanceSnapshot) if isp.Status != api.INSTANCE_SNAPSHOT_READY { return nil, httperrors.NewBadRequestError("Instance snapshot not ready") } input, err = isp.ToInstanceCreateInput(input) if len(input.Disks) == 0 { return nil, httperrors.NewInputParameterError("there are no disks in this instance snapshot, try another one") } return input, nil } func parseInstanceBackup(ctx context.Context, input *api.ServerCreateInput) (*api.ServerCreateInput, error) { ispi, err := InstanceBackupManager.FetchByIdOrName(ctx, nil, input.InstanceBackupId) if err == sql.ErrNoRows { return nil, httperrors.NewBadRequestError("can't find instance backup %s", input.InstanceBackupId) } if err != nil { return nil, httperrors.NewInternalServerError("fetch instance backup error %s", err) } isp := ispi.(*SInstanceBackup) if isp.Status != api.INSTANCE_BACKUP_STATUS_READY && isp.Status != api.INSTANCE_BACKUP_STATUS_RECOVERY { return nil, httperrors.NewBadRequestError("Instance backup not ready") } input, err = isp.ToInstanceCreateInput(input) if len(input.Disks) == 0 { return nil, httperrors.NewInputParameterError("there are no disks in this instance backup, try another one") } return input, nil } func (manager *SGuestManager) ExpandBatchCreateData( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict, index int, ) (*api.ServerCreateInput, error) { input, err := cmdline.FetchServerCreateInputByJSON(data) if err != nil { return nil, err } for i := range input.Networks { if index < len(input.Networks[i].Macs) { input.Networks[i].Mac = input.Networks[i].Macs[index] } if index < len(input.Networks[i].Addresses) { input.Networks[i].Address = input.Networks[i].Addresses[index] } if index < len(input.Networks[i].Addresses6) { input.Networks[i].Address6 = input.Networks[i].Addresses6[index] } } log.Debugf("ExpandBatchCreateData %s", jsonutils.Marshal(input)) return input, nil } func (manager *SGuestManager) validateCreateData( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*api.ServerCreateInput, error) { // TODO: 定义 api.ServerCreateInput 的 Unmarshal 函数,直接通过 data.Unmarshal(input) 解析参数 input, err := cmdline.FetchServerCreateInputByJSON(data) if err != nil { return nil, err } if len(input.Metadata) > 20 { return nil, httperrors.NewInputParameterError("metdata must less then 20") } if len(input.InstanceSnapshotId) > 0 { inputMem := input.VmemSize inputCpu := input.VcpuCount inputInstaceType := input.InstanceType input, err = parseInstanceSnapshot(ctx, input) if err != nil { return nil, err } // keep input cpu mem flavor if inputMem > 0 { input.VmemSize = inputMem } if inputMem > 0 { input.VcpuCount = inputCpu } if len(inputInstaceType) > 0 { input.InstanceType = inputInstaceType } } else if len(input.InstanceBackupId) > 0 { inputMem := input.VmemSize inputCpu := input.VcpuCount inputInstaceType := input.InstanceType input, err = parseInstanceBackup(ctx, input) if err != nil { return nil, err } // keep input cpu mem flavor if inputMem > 0 { input.VmemSize = inputMem } if inputMem > 0 { input.VcpuCount = inputCpu } if len(inputInstaceType) > 0 { input.InstanceType = inputInstaceType } } resetPassword := true if input.ResetPassword != nil { resetPassword = *input.ResetPassword } passwd := input.Password if len(passwd) > 0 { err = seclib2.ValidatePassword(passwd) if err != nil { return nil, err } resetPassword = true input.ResetPassword = &resetPassword } if resetPassword && len(input.LoginAccount) > 0 { if len(input.LoginAccount) > 32 { return nil, httperrors.NewInputParameterError("login_account is longer than 32 chars") } if err := manager.ValidateNameLoginAccount(input.LoginAccount); err != nil { return nil, err } } // check group if input.InstanceGroupIds != nil && len(input.InstanceGroupIds) != 0 { newGroupIds := make([]string, len(input.InstanceGroupIds)) for index, id := range input.InstanceGroupIds { model, err := GroupManager.FetchByIdOrName(ctx, userCred, id) if err != nil { return nil, httperrors.NewResourceNotFoundError("no such group %s", id) } newGroupIds[index] = model.GetId() } // list of id or name ==> ids input.InstanceGroupIds = newGroupIds } // check that all image of disk is the part of guest imgae, if use guest image to create guest err = manager.checkGuestImage(ctx, input) if err != nil { return nil, errors.Wrap(err, "checkGuestImage") } var hypervisor string // var rootStorageType string var osProf osprofile.SOSProfile hypervisor = input.Hypervisor if hypervisor != api.HYPERVISOR_CONTAINER { if len(input.Disks) == 0 && input.Cdrom == "" { return nil, httperrors.NewInputParameterError("No bootable disk information provided") } var imgProperties map[string]string var imgEncryptKeyId string if len(input.Disks) > 0 { diskConfig := input.Disks[0] diskConfig, err = parseDiskInfo(ctx, userCred, diskConfig) if err != nil { return nil, httperrors.NewInputParameterError("Invalid root image: %s", err) } input.Disks[0] = diskConfig imgEncryptKeyId = diskConfig.ImageEncryptKeyId imgProperties = diskConfig.ImageProperties if imgProperties[imageapi.IMAGE_DISK_FORMAT] == "iso" { return nil, httperrors.NewInputParameterError("System disk does not support iso image, please consider using cdrom parameter") } } if input.Cdrom != "" { cdromStr := input.Cdrom image, err := parseIsoInfo(ctx, userCred, cdromStr) if err != nil { return nil, httperrors.NewInputParameterError("parse cdrom device info error %s", err) } input.Cdrom = image.Id if len(imgProperties) == 0 { imgProperties = image.Properties } } // check boot indexes bm := bitmap.NewBitMap(128) if input.CdromBootIndex != nil && *input.CdromBootIndex >= 0 { bm.Set(int64(*input.CdromBootIndex)) } for i := 0; i < len(input.Disks); i++ { if input.Disks[i].BootIndex != nil && *input.Disks[i].BootIndex >= 0 { if bm.Has(int64(*input.Disks[i].BootIndex)) { return nil, httperrors.NewInputParameterError("duplicate boot index %d", *input.Disks[i].BootIndex) } bm.Set(int64(*input.Disks[i].BootIndex)) } } if arch := imgProperties["os_arch"]; strings.Contains(arch, "aarch") || strings.Contains(arch, "arm") { input.OsArch = apis.OS_ARCH_AARCH64 } var imgSupportUEFI *bool if desc, ok := imgProperties[imageapi.IMAGE_UEFI_SUPPORT]; ok { support := desc == "true" imgSupportUEFI = &support } if input.OsArch == apis.OS_ARCH_AARCH64 { // arm image supports UEFI by default support := true imgSupportUEFI = &support } switch { case imgSupportUEFI != nil && *imgSupportUEFI: if len(input.Bios) == 0 { input.Bios = "UEFI" } else if input.Bios != "UEFI" { return nil, httperrors.NewInputParameterError("UEFI image requires UEFI boot mode") } default: // not UEFI or not detectable if input.Bios == "UEFI" { return nil, httperrors.NewInputParameterError("UEFI boot mode requires UEFI image") } } if len(imgProperties) == 0 { imgProperties = map[string]string{"os_type": "Linux"} } input.DisableUsbKbd = imgProperties[imageapi.IMAGE_DISABLE_USB_KBD] == "true" imgIsWindows := imgProperties[imageapi.IMAGE_OS_TYPE] == "Windows" hasGpuVga := func() bool { for i := 0; i < len(input.IsolatedDevices); i++ { if input.IsolatedDevices[i].DevType == api.GPU_VGA_TYPE { return true } } return false }() if imgIsWindows && hasGpuVga && input.Bios != "UEFI" { return nil, httperrors.NewInputParameterError("Windows use gpu vga requires UEFI image") } if vdi, ok := imgProperties[imageapi.IMAGE_VDI_PROTOCOL]; ok && len(vdi) > 0 && len(input.Vdi) == 0 { input.Vdi = vdi } if input.EncryptKeyId == nil && len(imgEncryptKeyId) > 0 { input.EncryptKeyId = &imgEncryptKeyId } if input.EncryptKeyId != nil || input.EncryptKeyNew != nil { input.EncryptedResourceCreateInput, err = manager.SEncryptedResourceManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EncryptedResourceCreateInput) if err != nil { return nil, errors.Wrap(err, "SEncryptedResourceManager.ValidateCreateData") } if len(imgEncryptKeyId) > 0 { if imgEncryptKeyId != *input.EncryptKeyId { return nil, errors.Wrap(httperrors.ErrConflict, "encryption key inconsist with image") } } } osType := input.OsType osProf, err = osprofile.GetOSProfileFromImageProperties(imgProperties, hypervisor) if err != nil { return nil, httperrors.NewInputParameterError("Invalid root image: %s", err) } if len(osProf.Hypervisor) > 0 && len(hypervisor) == 0 { hypervisor = osProf.Hypervisor input.Hypervisor = hypervisor } if len(osProf.OSType) > 0 && len(osType) == 0 { osType = osProf.OSType input.OsType = osType } input.OsProfile = jsonutils.Marshal(osProf) } input, err = ValidateScheduleCreateData(ctx, userCred, input, hypervisor) if err != nil { return nil, err } optionSystemHypervisor := []string{api.HYPERVISOR_KVM, api.HYPERVISOR_ESXI} if !utils.IsInStringArray(input.Hypervisor, optionSystemHypervisor) && len(input.Disks[0].ImageId) == 0 && len(input.Disks[0].SnapshotId) == 0 && input.Cdrom == "" { return nil, httperrors.NewBadRequestError("Miss operating system???") } if input.Hypervisor == api.HYPERVISOR_KVM { if input.IsDaemon == nil && options.Options.SetKVMServerAsDaemonOnCreate { setDaemon := true input.IsDaemon = &setDaemon } } hypervisor = input.Hypervisor if hypervisor != api.HYPERVISOR_CONTAINER { // support sku here var sku *SServerSku skuName := input.InstanceType if len(skuName) > 0 { provider := GetDriver(input.Hypervisor).GetProvider() sku, err = ServerSkuManager.FetchSkuByNameAndProvider(skuName, provider, true) if err != nil { return nil, err } input.InstanceType = sku.Name input.VmemSize = sku.MemorySizeMB input.VcpuCount = sku.CpuCoreCount } else { vmemSize, vcpuCount, err := ValidateMemCpuData(input.VmemSize, input.VcpuCount, input.Hypervisor) if err != nil { return nil, err } if vmemSize == 0 { return nil, httperrors.NewMissingParameterError("vmem_size") } if vcpuCount == 0 { vcpuCount = 1 } input.VmemSize = vmemSize input.VcpuCount = vcpuCount } dataDiskDefs := []*api.DiskConfig{} if sku != nil && sku.AttachedDiskCount > 0 { if sku.AttachedDiskSizeGB == 0 { return nil, httperrors.NewInputParameterError("sku %s not indicate attached disk size", sku.Name) } if len(sku.AttachedDiskType) == 0 { return nil, httperrors.NewInputParameterError("sku %s not indicate attached disk backend", sku.Name) } for i := 0; i < sku.AttachedDiskCount; i += 1 { dataDisk := &api.DiskConfig{ SizeMb: sku.AttachedDiskSizeGB * 1024, Backend: strings.ToLower(sku.AttachedDiskType), } dataDiskDefs = append(dataDiskDefs, dataDisk) } } // start from data disk disks := input.Disks for idx := 1; idx < len(disks); idx += 1 { dataDiskDefs = append(dataDiskDefs, disks[idx]) } rootDiskConfig, err := parseDiskInfo(ctx, userCred, disks[0]) if err != nil { return nil, httperrors.NewGeneralError(err) // should no error } // validate root disk config { if rootDiskConfig.NVMEDevice != nil { return nil, httperrors.NewBadRequestError("NVMe device can't assign as root disk") } if input.ResourceType != api.HostResourceTypePrepaidRecycle { if len(rootDiskConfig.Backend) == 0 { defaultStorageType, _ := data.GetString("default_storage_type") if len(defaultStorageType) > 0 { rootDiskConfig.Backend = defaultStorageType } else { rootDiskConfig.Backend = GetDriver(hypervisor).GetDefaultSysDiskBackend() } } sysMinDiskMB := GetDriver(hypervisor).GetMinimalSysDiskSizeGb() * 1024 if rootDiskConfig.SizeMb != api.DISK_SIZE_AUTOEXTEND && rootDiskConfig.SizeMb < sysMinDiskMB { rootDiskConfig.SizeMb = sysMinDiskMB } } if len(rootDiskConfig.Driver) == 0 { rootDiskConfig.Driver = osProf.DiskDriver } log.Debugf("ROOT DISK: %#v", rootDiskConfig) input.Disks[0] = rootDiskConfig if sku != nil { if len(rootDiskConfig.OsArch) > 0 && len(sku.CpuArch) > 0 { if !strings.Contains(rootDiskConfig.OsArch, sku.CpuArch) { return nil, httperrors.NewConflictError("root disk image(%s) and sku(%s) architecture mismatch", rootDiskConfig.OsArch, sku.CpuArch) } } } } for i := 0; i < len(dataDiskDefs); i += 1 { diskConfig, err := parseDiskInfo(ctx, userCred, dataDiskDefs[i]) if err != nil { return nil, httperrors.NewInputParameterError("parse disk description error %s", err) } if diskConfig.DiskType == api.DISK_TYPE_SYS { log.Warningf("Snapshot error: disk index %d > 0 but disk type is %s", i+1, api.DISK_TYPE_SYS) diskConfig.DiskType = api.DISK_TYPE_DATA } if len(diskConfig.Backend) == 0 { diskConfig.Backend = rootDiskConfig.Backend } if len(diskConfig.Driver) == 0 { diskConfig.Driver = osProf.DiskDriver } if diskConfig.NVMEDevice != nil { if input.Backup { return nil, httperrors.NewBadRequestError("Cannot create backup with isolated device") } devConfig, err := IsolatedDeviceManager.parseDeviceInfo(userCred, diskConfig.NVMEDevice) if err != nil { return nil, httperrors.NewInputParameterError("parse isolated device description error %s", err) } err = IsolatedDeviceManager.isValidNVMEDeviceInfo(devConfig) if err != nil { return nil, err } diskConfig.NVMEDevice = devConfig diskConfig.Driver = api.DISK_DRIVER_VFIO diskConfig.Backend = api.STORAGE_NVME_PT } input.Disks[i+1] = diskConfig } if len(input.Duration) > 0 { /*if !userCred.IsAllow(rbacutils.ScopeSystem, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionPerform, "renew") { return nil, httperrors.NewForbiddenError("only admin can create prepaid resource") }*/ if input.ResourceType == api.HostResourceTypePrepaidRecycle { return nil, httperrors.NewConflictError("cannot create prepaid server on prepaid resource type") } billingCycle, err := billing.ParseBillingCycle(input.Duration) if err != nil { return nil, httperrors.NewInputParameterError("invalid duration %s", input.Duration) } if input.BillingType == billing_api.BILLING_TYPE_POSTPAID { if !GetDriver(hypervisor).IsSupportPostpaidExpire() { return nil, httperrors.NewBadRequestError("guest %s unsupport postpaid expire", hypervisor) } } else { if !GetDriver(hypervisor).IsSupportedBillingCycle(billingCycle) { return nil, httperrors.NewInputParameterError("unsupported duration %s", input.Duration) } } if len(input.BillingType) == 0 { input.BillingType = billing_api.BILLING_TYPE_PREPAID } input.BillingCycle = billingCycle.String() // expired_at will be set later by callback // data.Add(jsonutils.NewTimeString(billingCycle.EndAt(time.Time{})), "expired_at") input.Duration = billingCycle.String() } } // HACK: if input networks is empty, add one random network config if len(input.Networks) == 0 { input.Networks = append(input.Networks, &api.NetworkConfig{Exit: false}) } netArray := input.Networks defaultGwCnt := 0 firstExit := -1 for idx := 0; idx < len(netArray); idx += 1 { netConfig, err := parseNetworkInfo(ctx, userCred, netArray[idx]) if err != nil { return nil, httperrors.NewInputParameterError("parse network description error %s", err) } err = isValidNetworkInfo(ctx, userCred, netConfig, "") if err != nil { return nil, err } if len(netConfig.Driver) == 0 { netConfig.Driver = osProf.NetDriver } if netConfig.SriovDevice != nil { if input.Backup { return nil, httperrors.NewBadRequestError("Cannot create backup with isolated device") } devConfig, err := IsolatedDeviceManager.parseDeviceInfo(userCred, netConfig.SriovDevice) if err != nil { return nil, httperrors.NewInputParameterError("parse isolated device description error %s", err) } err = IsolatedDeviceManager.isValidNicDeviceInfo(devConfig) if err != nil { return nil, err } netConfig.SriovDevice = devConfig netConfig.Driver = api.NETWORK_DRIVER_VFIO } netConfig.Project = ownerId.GetProjectId() netConfig.Domain = ownerId.GetProjectDomainId() if netConfig.IsDefault { defaultGwCnt++ } if firstExit < 0 && netConfig.Exit { firstExit = idx } input.Networks[idx] = netConfig } // check default gateway if defaultGwCnt == 0 { defIdx := 0 if firstExit >= 0 { // there is a exit network, make it the default defIdx = firstExit } // make the first nic as default input.Networks[defIdx].IsDefault = true } else if defaultGwCnt > 1 { return nil, errors.Wrapf(httperrors.ErrInputParameter, "more than 1 nic(%d) assigned as default gateway", defaultGwCnt) } isoDevArray := input.IsolatedDevices for idx := 0; idx < len(isoDevArray); idx += 1 { // .Contains(fmt.Sprintf("isolated_device.%d", idx)); idx += 1 { if input.Backup { return nil, httperrors.NewBadRequestError("Cannot create backup with isolated device") } devConfig, err := IsolatedDeviceManager.parseDeviceInfo(userCred, isoDevArray[idx]) if err != nil { return nil, httperrors.NewInputParameterError("parse isolated device description error %s", err) } err = IsolatedDeviceManager.isValidDeviceInfo(devConfig) if err != nil { return nil, err } input.IsolatedDevices[idx] = devConfig } nvidiaVgpuCnt := 0 gpuCnt := 0 for i := 0; i < len(input.IsolatedDevices); i++ { if input.IsolatedDevices[i].DevType == api.LEGACY_VGPU_TYPE { nvidiaVgpuCnt += 1 } else if utils.IsInStringArray(input.IsolatedDevices[i].DevType, api.VALID_GPU_TYPES) { gpuCnt += 1 } } if nvidiaVgpuCnt > 1 { return nil, httperrors.NewBadRequestError("Nvidia vgpu count exceed > 1") } if nvidiaVgpuCnt > 0 && gpuCnt > 0 { return nil, httperrors.NewBadRequestError("Nvidia vgpu can't passthrough with other gpus") } keypairId := input.KeypairId if len(keypairId) > 0 { keypairObj, err := KeypairManager.FetchByIdOrName(ctx, userCred, keypairId) if err != nil { return nil, httperrors.NewResourceNotFoundError("Keypair %s not found", keypairId) } input.KeypairId = keypairObj.GetId() } secGrpIds := []string{} for _, secgroup := range input.Secgroups { secGrpObj, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, secgroup) if err != nil { return nil, httperrors.NewResourceNotFoundError("Secgroup %s not found", secgroup) } if !utils.IsInStringArray(secGrpObj.GetId(), secGrpIds) { secGrpIds = append(secGrpIds, secGrpObj.GetId()) } } if len(secGrpIds) > 0 { input.SecgroupId = secGrpIds[0] input.Secgroups = secGrpIds[1:] } else if input.SecgroupId != "" { secGrpId := input.SecgroupId secGrpObj, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, secGrpId) if err != nil { return nil, httperrors.NewResourceNotFoundError("Secgroup %s not found", secGrpId) } input.SecgroupId = secGrpObj.GetId() } else { input.SecgroupId = options.Options.DefaultSecurityGroupId } maxSecgrpCount := GetDriver(hypervisor).GetMaxSecurityGroupCount() if maxSecgrpCount == 0 { //esxi 不支持安全组 input.SecgroupId = "" input.Secgroups = []string{} } else if len(input.Secgroups)+1 > maxSecgrpCount { return nil, httperrors.NewInputParameterError("%s shall bind up to %d security groups", hypervisor, maxSecgrpCount) } preferRegionId, _ := data.GetString("prefer_region_id") if err := manager.validateEip(ctx, userCred, input, preferRegionId, input.PreferManager); err != nil { return nil, err } /* TODO group for idx := 0; data.Contains(fmt.Sprintf("srvtag.%d", idx)); idx += 1 { }*/ if input.ResourceType != api.HostResourceTypePrepaidRecycle { input, err = GetDriver(hypervisor).ValidateCreateData(ctx, userCred, input) if err != nil { return nil, err } } input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput) if err != nil { return nil, err } name := input.Name if len(name) == 0 { name = input.GenerateName } input.HostnameInput, err = manager.SHostnameResourceBaseManager.ValidateHostname(name, input.OsType, input.HostnameInput) if err != nil { return nil, err } // validate UserData if err := userdata.ValidateUserdata(input.UserData, input.OsType); err != nil { return nil, httperrors.NewInputParameterError("Invalid userdata: %v", err) } err = manager.ValidatePolicyDefinitions(ctx, userCred, ownerId, query, input) if err != nil { return nil, err } input.ProjectId = ownerId.GetProjectId() input.ProjectDomainId = ownerId.GetProjectDomainId() return input, nil } func (manager *SGuestManager) ValidatePolicyDefinitions(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.ServerCreateInput) error { definitions, err := PolicyDefinitionManager.GetAvailablePolicyDefinitions(ctx, userCred) if err != nil { return httperrors.NewGeneralError(err) } for i := range definitions { switch definitions[i].Category { case api.POLICY_DEFINITION_CATEGORY_CLOUDREGION: if len(input.PreferRegion) == 0 { return httperrors.NewMissingParameterError(fmt.Sprintf("policy definition %s require prefer_region_id parameter", definitions[i].Name)) } if definitions[i].Parameters == nil { return httperrors.NewPolicyDefinitionError("invalid parameters for policy definition %s", definitions[i].Name) } regionDefinitions := api.SCloudregionPolicyDefinitions{} definitions[i].Parameters.Unmarshal(®ionDefinitions) regions := []string{} for _, region := range regionDefinitions.Cloudregions { regions = append(regions, region.Id) regions = append(regions, region.Name) } isIn := utils.IsInStringArray(input.PreferRegion, regions) switch definitions[i].Condition { case api.POLICY_DEFINITION_CONDITION_IN: if !isIn { return httperrors.NewPolicyDefinitionError("policy definition %s require cloudregion in %s", definitions[i].Name, definitions[i].Parameters) } case api.POLICY_DEFINITION_CONDITION_NOT_IN: if isIn { return httperrors.NewPolicyDefinitionError("policy definition %s require cloudregion not in %s", definitions[i].Name, definitions[i].Parameters) } default: return httperrors.NewPolicyDefinitionError("invalid policy definition %s(%s) condition %s", definitions[i].Name, definitions[i].Id, definitions[i].Condition) } case api.POLICY_DEFINITION_CATEGORY_TAG: tags := []string{} if definitions[i].Parameters == nil { return httperrors.NewPolicyDefinitionError("invalid parameters for policy definition %s", definitions[i].Name) } definitions[i].Parameters.Unmarshal(&tags, "tags") metadataKeys := []string{} for k, _ := range input.Metadata { metadataKeys = append(metadataKeys, strings.TrimPrefix(k, db.USER_TAG_PREFIX)) } for _, tag := range tags { isIn := utils.IsInStringArray(tag, metadataKeys) switch definitions[i].Condition { case api.POLICY_DEFINITION_CONDITION_CONTAINS: if !isIn { return httperrors.NewPolicyDefinitionError("policy definition %s require must contains tag %s", definitions[i].Name, tag) } case api.POLICY_DEFINITION_CONDITION_EXCEPT: if isIn { return httperrors.NewPolicyDefinitionError("policy definition %s require except tag %s", definitions[i].Name, tag) } default: return httperrors.NewPolicyDefinitionError("invalid policy definition %s(%s) condition %s", definitions[i].Name, definitions[i].Id, definitions[i].Condition) } } default: return httperrors.NewPolicyDefinitionError("invalid category %s for policy definition %s(%s)", definitions[i].Category, definitions[i].Name, definitions[i].Id) } } return nil } func (manager *SGuestManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { input, err := manager.validateCreateData(ctx, userCred, ownerId, query, data) if err != nil { return nil, err } return input.JSON(input), nil } // 创建虚拟机实例 func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, oinput api.ServerCreateInput) (*jsonutils.JSONDict, error) { input, err := manager.validateCreateData(ctx, userCred, ownerId, query, oinput.JSON(oinput)) if err != nil { return nil, err } if input.IsSystem == nil || !(*input.IsSystem) { err = manager.checkCreateQuota(ctx, userCred, ownerId, *input, input.Backup, 1) if err != nil { return nil, err } } return input.JSON(input), nil } func (manager *SGuestManager) validateEip(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput, preferRegionId string, preferManagerId string) error { if input.PublicIpBw > 0 { if !GetDriver(input.Hypervisor).IsSupportPublicIp() { return httperrors.NewNotImplementedError("public ip not supported for %s", input.Hypervisor) } if len(input.PublicIpChargeType) == 0 { input.PublicIpChargeType = string(cloudprovider.ElasticipChargeTypeByTraffic) } if !utils.IsInStringArray(input.PublicIpChargeType, []string{ string(cloudprovider.ElasticipChargeTypeByTraffic), string(cloudprovider.ElasticipChargeTypeByBandwidth), }) { return httperrors.NewInputParameterError("invalid public_ip_charge_type %s", input.PublicIpChargeType) } return nil } eipStr := input.Eip eipBw := input.EipBw if len(eipStr) > 0 || eipBw > 0 { if !GetDriver(input.Hypervisor).IsSupportEip() { return httperrors.NewNotImplementedError("eip not supported for %s", input.Hypervisor) } if len(eipStr) > 0 { eipObj, err := ElasticipManager.FetchByIdOrName(ctx, userCred, eipStr) if err != nil { if err == sql.ErrNoRows { return httperrors.NewResourceNotFoundError2(ElasticipManager.Keyword(), eipStr) } else { return httperrors.NewGeneralError(err) } } eip := eipObj.(*SElasticip) if eip.Status != api.EIP_STATUS_READY { return httperrors.NewInvalidStatusError("eip %s status invalid %s", eipStr, eip.Status) } if eip.IsAssociated() { return httperrors.NewResourceBusyError("eip %s has been associated", eipStr) } input.Eip = eipObj.GetId() eipCloudprovider := eip.GetCloudprovider() if eipCloudprovider != nil { if len(preferManagerId) > 0 && preferManagerId != eipCloudprovider.Id { return httperrors.NewConflictError("cannot assoicate with eip %s: different cloudprovider", eipStr) } input.PreferManager = eipCloudprovider.Id } eipRegion, err := eip.GetRegion() if err != nil { return httperrors.NewGeneralError(errors.Wrapf(err, "eip.GetRegion")) } // preferRegionId, _ := data.GetString("prefer_region_id") if len(preferRegionId) > 0 && preferRegionId != eipRegion.Id { return httperrors.NewConflictError("cannot assoicate with eip %s: different region", eipStr) } input.PreferRegion = eipRegion.Id } else { // create new eip } } return nil } func (self *SGuest) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { self.SVirtualResourceBase.PostUpdate(ctx, userCred, query, data) if len(self.ExternalId) > 0 && (data.Contains("name") || data.Contains("__meta__") || data.Contains("description")) || data.Contains("hostname") { err := self.StartRemoteUpdateTask(ctx, userCred, false, "") if err != nil { log.Errorf("StartRemoteUpdateTask fail: %s", err) } } if port, err := data.Int("ssh_port"); err != nil { err := self.SetSshPort(ctx, userCred, int(port)) if err != nil { log.Errorf("unable to set sshport for guest %s", self.GetId()) } } } func (manager *SGuestManager) checkCreateQuota( ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ServerCreateInput, hasBackup bool, count int, ) error { req, regionReq := getGuestResourceRequirements(ctx, userCred, input, ownerId, count, hasBackup) log.Debugf("computeQuota: %s", jsonutils.Marshal(req)) log.Debugf("regionQuota: %s", jsonutils.Marshal(regionReq)) err := quotas.CheckSetPendingQuota(ctx, userCred, &req) if err != nil { return errors.Wrap(err, "quotas.CheckSetPendingQuota") } err = quotas.CheckSetPendingQuota(ctx, userCred, ®ionReq) if err != nil { return errors.Wrap(err, "quotas.CheckSetPendingQuota") } return nil } func (self *SGuest) checkUpdateQuota(ctx context.Context, userCred mcclient.TokenCredential, vcpuCount int, vmemSize int) (quotas.IQuota, error) { req := SQuota{} if vcpuCount > 0 && vcpuCount > int(self.VcpuCount) { req.Cpu = vcpuCount - int(self.VcpuCount) } if vmemSize > 0 && vmemSize > self.VmemSize { req.Memory = vmemSize - self.VmemSize } keys, err := self.GetQuotaKeys() if err != nil { return nil, errors.Wrap(err, "self.GetQuotaKeys") } req.SetKeys(keys) err = quotas.CheckSetPendingQuota(ctx, userCred, &req) if err != nil { return nil, errors.Wrap(err, "quotas.CheckSetPendingQuota") } return &req, nil } func getGuestResourceRequirements( ctx context.Context, userCred mcclient.TokenCredential, input api.ServerCreateInput, ownerId mcclient.IIdentityProvider, count int, hasBackup bool, ) (SQuota, SRegionQuota) { vcpuCount := input.VcpuCount if vcpuCount == 0 { vcpuCount = 1 } vmemSize := input.VmemSize diskSize := 0 for _, diskConfig := range input.Disks { if diskConfig.DiskId != "" { // disk has been created, ignore resource requirement continue } diskSize += diskConfig.SizeMb } devCount := len(input.IsolatedDevices) eNicCnt := 0 iNicCnt := 0 eBw := 0 iBw := 0 for _, netConfig := range input.Networks { if IsExitNetworkInfo(ctx, userCred, netConfig) { eNicCnt += 1 eBw += netConfig.BwLimit } else { iNicCnt += 1 iBw += netConfig.BwLimit } if netConfig.SriovDevice != nil { devCount += 1 } } if hasBackup { vcpuCount = vcpuCount * 2 vmemSize = vmemSize * 2 diskSize = diskSize * 2 } eipCnt := 0 eipBw := input.EipBw if eipBw > 0 { eipCnt = 1 } req := SQuota{ Count: count, Cpu: int(vcpuCount) * count, Memory: int(vmemSize) * count, Storage: diskSize * count, IsolatedDevice: devCount * count, } regionReq := SRegionQuota{ Port: iNicCnt * count, Eport: eNicCnt * count, //Bw: iBw * count, //Ebw: eBw * count, Eip: eipCnt * count, } keys := serverCreateInput2ComputeQuotaKeys(input, ownerId) req.SetKeys(keys) regionReq.SetKeys(keys.SRegionalCloudResourceKeys) return req, regionReq } func (guest *SGuest) getGuestBackupResourceRequirements(ctx context.Context, userCred mcclient.TokenCredential) SQuota { guestDisksSize := guest.getDiskSize() return SQuota{ Count: 1, Cpu: int(guest.VcpuCount), Memory: guest.VmemSize, Storage: guestDisksSize, } } func (guest *SGuest) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { guest.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data) tags := []string{"cpu_bound", "io_bound", "io_hardlimit"} appTags := make([]string, 0) for _, tag := range tags { if data.Contains(tag) { appTags = append(appTags, tag) } } guest.setApptags(ctx, appTags, userCred) guest.SetCreateParams(ctx, userCred, data) osProfileJson, _ := data.Get("__os_profile__") if osProfileJson != nil { guest.setOSProfile(ctx, userCred, osProfileJson) } if jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_MEMCLEAN, false) { guest.SetMetadata(ctx, api.VM_METADATA_ENABLE_MEMCLEAN, "true", userCred) } if jsonutils.QueryBoolean(data, imageapi.IMAGE_DISABLE_USB_KBD, false) { guest.SetMetadata(ctx, imageapi.IMAGE_DISABLE_USB_KBD, "true", userCred) } matcherJson, _ := data.Get(api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER) if matcherJson != nil { guest.SetMetadata(ctx, api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER, matcherJson, userCred) } userData, _ := data.GetString("user_data") if len(userData) > 0 { guest.setUserData(ctx, userCred, userData) } if guest.GetDriver().GetMaxSecurityGroupCount() > 0 { secgroups, _ := jsonutils.GetStringArray(data, "secgroups") for _, secgroupId := range secgroups { if secgroupId != guest.SecgrpId { gs := SGuestsecgroup{} gs.SecgroupId = secgroupId gs.GuestId = guest.Id GuestsecgroupManager.TableSpec().Insert(ctx, &gs) } } } else { db.Update(guest, func() error { guest.SecgrpId = "" return nil }) } } func (guest *SGuest) setApptags(ctx context.Context, appTags []string, userCred mcclient.TokenCredential) { err := guest.SetMetadata(ctx, api.VM_METADATA_APP_TAGS, strings.Join(appTags, ","), userCred) if err != nil { log.Errorln(err) } } func (guest *SGuest) SetCreateParams(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) { // delete deploy files info createParams := data.(*jsonutils.JSONDict).CopyExcludes("deploy_configs") err := guest.SetMetadata(ctx, api.VM_METADATA_CREATE_PARAMS, createParams.String(), userCred) if err != nil { log.Errorf("Server %s SetCreateParams: %v", guest.Name, err) } } func (guest *SGuest) GetCreateParams(ctx context.Context, userCred mcclient.TokenCredential) (*api.ServerCreateInput, error) { input := new(api.ServerCreateInput) data := guest.GetMetadataJson(ctx, api.VM_METADATA_CREATE_PARAMS, userCred) if data == nil { return nil, fmt.Errorf("Not found %s %s in metadata", guest.Name, api.VM_METADATA_CREATE_PARAMS) } err := data.Unmarshal(input) return input, err } func (manager *SGuestManager) SetPropertiesWithInstanceSnapshot( ctx context.Context, userCred mcclient.TokenCredential, ispId string, items []db.IModel, ) { misp, err := InstanceSnapshotManager.FetchById(ispId) if err == nil { isp := misp.(*SInstanceSnapshot) for i := 0; i < len(items); i++ { guest := items[i].(*SGuest) if isp.ServerMetadata != nil { metadata := make(map[string]interface{}, 0) isp.ServerMetadata.Unmarshal(metadata) if passwd, ok := metadata["passwd"]; ok { delete(metadata, "passwd") metadata["login_key"], _ = utils.EncryptAESBase64(guest.Id, passwd.(string)) } metadata[api.BASE_INSTANCE_SNAPSHOT_ID] = isp.Id guest.SetAllMetadata(ctx, metadata, userCred) } } } } func (manager *SGuestManager) OnCreateComplete(ctx context.Context, items []db.IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data []jsonutils.JSONObject) { input := api.ServerCreateInput{} data[0].Unmarshal(&input) if len(input.InstanceSnapshotId) > 0 { manager.SetPropertiesWithInstanceSnapshot(ctx, userCred, input.InstanceSnapshotId, items) } pendingUsage, pendingRegionUsage := getGuestResourceRequirements(ctx, userCred, input, ownerId, len(items), input.Backup) err := RunBatchCreateTask(ctx, items, userCred, data, pendingUsage, pendingRegionUsage, "GuestBatchCreateTask", input.ParentTaskId) if err != nil { for i := range items { guest := items[i].(*SGuest) guest.SetStatus(ctx, userCred, api.VM_CREATE_FAILED, err.Error()) } } } func (guest *SGuest) GetGroups() []SGroupguest { guestgroups := make([]SGroupguest, 0) q := GroupguestManager.Query().Equals("guest_id", guest.Id) err := db.FetchModelObjects(GroupguestManager, q, &guestgroups) if err != nil { log.Errorf("GetGroups fail %s", err) return nil } return guestgroups } func (self *SGuest) getBandwidth(isExit bool) int { bw := 0 networks, err := self.GetNetworks("") if err != nil { return bw } if networks != nil && len(networks) > 0 { for i := 0; i < len(networks); i += 1 { if networks[i].IsExit() == isExit { bw += networks[i].getBandwidth() } } } return bw } func (self *SGuest) getExtBandwidth() int { return self.getBandwidth(true) } func (self *SGuest) moreExtraInfo( ctx context.Context, out api.ServerDetails, userCred mcclient.TokenCredential, query jsonutils.JSONObject, fields stringutils2.SSortedStrings, isList bool, ) api.ServerDetails { // extra.Add(jsonutils.NewInt(int64(self.getExtBandwidth())), "ext_bw") if isList { if query.Contains("group") { groupId, _ := query.GetString("group") q := GroupguestManager.Query().Equals("group_id", groupId).Equals("guest_id", self.Id) var groupGuest SGroupguest err := q.First(&groupGuest) if err == nil { out.AttachTime = groupGuest.CreatedAt } } } else { out.Networks = self.getNetworksDetails() out.VirtualIps = strings.Join(self.getVirtualIPs(), ",") out.SecurityRules = self.getSecurityGroupsRules() osName := self.GetOS() if len(osName) > 0 { out.OsName = osName if len(self.OsType) == 0 { out.OsType = osName } } if userCred.HasSystemAdminPrivilege() { out.AdminSecurityRules = self.getAdminSecurityRules() } } out.IsPrepaidRecycle = self.IsPrepaidRecycle() if len(self.BackupHostId) > 0 && (len(fields) == 0 || fields.Contains("backup_host_name") || fields.Contains("backup_host_status")) { backupHost := HostManager.FetchHostById(self.BackupHostId) if backupHost != nil { if len(fields) == 0 || fields.Contains("backup_host_name") { out.BackupHostName = backupHost.Name } if len(fields) == 0 || fields.Contains("backup_host_status") { out.BackupHostStatus = backupHost.HostStatus } out.BackupGuestSyncStatus = self.GetGuestBackupMirrorJobStatus(ctx, userCred) } } if len(fields) == 0 || fields.Contains("can_recycle") { err := self.CanPerformPrepaidRecycle() if err == nil { out.CanRecycle = true } } if len(fields) == 0 || fields.Contains("auto_delete_at") { if self.PendingDeleted { pendingDeletedAt := self.PendingDeletedAt.Add(time.Second * time.Duration(options.Options.PendingDeleteExpireSeconds)) out.AutoDeleteAt = pendingDeletedAt } } out.CdromSupport, _ = self.GetDriver().IsSupportCdrom(self) out.FloppySupport, _ = self.GetDriver().IsSupportFloppy(self) out.MonitorUrl = self.GetDriver().FetchMonitorUrl(ctx, self) return out } func (self *SGuestManager) GetMetadataHiddenKeys() []string { return []string{ api.VM_METADATA_CREATE_PARAMS, } } func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) { var err error q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) if err != nil { return nil, err } // exportKeys, _ := query.GetString("export_keys") // keys := strings.Split(exportKeys, ",") // guest_id as filter key if keys.Contains("ips") { guestIpsQuery := GuestnetworkManager.Query("guest_id").GroupBy("guest_id") guestIpsQuery.AppendField(sqlchemy.GROUP_CONCAT("concat_ip_addr", guestIpsQuery.Field("ip_addr"))) ipsSubQuery := guestIpsQuery.SubQuery() q.LeftJoin(ipsSubQuery, sqlchemy.Equals(q.Field("id"), ipsSubQuery.Field("guest_id"))) q.AppendField(ipsSubQuery.Field("concat_ip_addr")) } if keys.Contains("disk") { guestDisksQuery := GuestdiskManager.Query("guest_id", "disk_id").GroupBy("guest_id") diskQuery := DiskManager.Query("id", "disk_size").SubQuery() guestDisksQuery.Join(diskQuery, sqlchemy.Equals(diskQuery.Field("id"), guestDisksQuery.Field("disk_id"))) guestDisksQuery.AppendField(sqlchemy.SUM("disk_size", diskQuery.Field("disk_size"))) guestDisksSubQuery := guestDisksQuery.SubQuery() q.LeftJoin(guestDisksSubQuery, sqlchemy.Equals(q.Field("id"), guestDisksSubQuery.Field("guest_id"))) q.AppendField(guestDisksSubQuery.Field("disk_size")) } if keys.Contains("eip") { eipsQuery := ElasticipManager.Query("associate_id", "ip_addr").Equals("associate_type", "server").GroupBy("associate_id") eipsSubQuery := eipsQuery.SubQuery() q.LeftJoin(eipsSubQuery, sqlchemy.Equals(q.Field("id"), eipsSubQuery.Field("associate_id"))) q.AppendField(eipsSubQuery.Field("ip_addr", "eip")) } if keys.ContainsAny(manager.SHostResourceBaseManager.GetExportKeys()...) { q, err = manager.SHostResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) if err != nil { return nil, errors.Wrap(err, "SHostResourceBaseManager.ListItemExportKeys") } } return q, nil } func (manager *SGuestManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict { res := manager.SVirtualResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap) // exportKeys, _ := query.GetString("export_keys") // keys := strings.Split(exportKeys, ",") if ips, ok := rowMap["concat_ip_addr"]; ok && len(ips) > 0 { res.Set("ips", jsonutils.NewString(ips)) } if eip, ok := rowMap["eip"]; ok && len(eip) > 0 { res.Set("eip", jsonutils.NewString(eip)) } if disk, ok := rowMap["disk_size"]; ok { res.Set("disk", jsonutils.NewString(disk)) } if host, ok := rowMap["host"]; ok && len(host) > 0 { res.Set("host", jsonutils.NewString(host)) } if zone, ok := rowMap["zone"]; ok && len(zone) > 0 { res.Set("zone", jsonutils.NewString(zone)) } if region, ok := rowMap["region"]; ok && len(region) > 0 { res.Set("region", jsonutils.NewString(region)) } if manager, ok := rowMap["manager"]; ok && len(manager) > 0 { res.Set("manager", jsonutils.NewString(manager)) } if keys.Contains("tenant") { if projectId, ok := rowMap["tenant_id"]; ok { tenant, err := db.TenantCacheManager.FetchTenantById(ctx, projectId) if err == nil { res.Set("tenant", jsonutils.NewString(tenant.GetName())) } } } if keys.Contains("os_distribution") { if osType, ok := rowMap["os_type"]; ok { res.Set("os_distribution", jsonutils.NewString(osType)) } } return res } func (self *SGuest) getNetworksDetails() string { guestnets, err := self.GetNetworks("") if err != nil { return "" } var buf bytes.Buffer for _, nic := range guestnets { buf.WriteString(nic.GetDetailedString()) buf.WriteString("\n") } return buf.String() } func (self *SGuest) GetCdrom() *SGuestcdrom { return self.getCdrom(false, 0) } func (self *SGuest) GetCdromByOrdinal(ordinal int64) *SGuestcdrom { return self.getCdrom(false, ordinal) } func (self *SGuest) getCdrom(create bool, ordinal int64) *SGuestcdrom { cdrom := SGuestcdrom{} cdrom.SetModelManager(GuestcdromManager, &cdrom) err := GuestcdromManager.Query().Equals("id", self.Id).Equals("ordinal", ordinal).First(&cdrom) if err != nil { if err == sql.ErrNoRows { if create { cdrom.Id = self.Id cdrom.Ordinal = int(ordinal) err = GuestcdromManager.TableSpec().Insert(context.TODO(), &cdrom) if err != nil { log.Errorf("insert cdrom fail %s", err) return nil } return &cdrom } else { return nil } } else { log.Errorf("getCdrom query fail %s", err) return nil } } else { return &cdrom } } func (self *SGuest) getCdroms() ([]SGuestcdrom, error) { cdroms := make([]SGuestcdrom, 0) q := GuestcdromManager.Query().Equals("id", self.Id) err := db.FetchModelObjects(GuestcdromManager, q, &cdroms) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return cdroms, nil } func (self *SGuest) getFloppys() ([]SGuestfloppy, error) { floppys := make([]SGuestfloppy, 0) q := GuestFloppyManager.Query().Equals("id", self.Id) err := db.FetchModelObjects(GuestFloppyManager, q, &floppys) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return floppys, nil } func (self *SGuest) getFloppy(create bool, ordinal int64) *SGuestfloppy { floppy := SGuestfloppy{} floppy.SetModelManager(GuestFloppyManager, &floppy) err := GuestFloppyManager.Query().Equals("id", self.Id).Equals("ordinal", ordinal).First(&floppy) if err != nil { if err == sql.ErrNoRows { if create { floppy.Id = self.Id floppy.Ordinal = int(ordinal) err = GuestFloppyManager.TableSpec().Insert(context.TODO(), &floppy) if err != nil { log.Errorf("insert cdrom fail %s", err) return nil } return &floppy } else { return nil } } else { log.Errorf("getFloppy query fail %s", err) return nil } } else { return &floppy } } func (self *SGuest) getKeypair() *SKeypair { if len(self.KeypairId) > 0 { keypair, _ := KeypairManager.FetchById(self.KeypairId) if keypair != nil { return keypair.(*SKeypair) } } return nil } func (self *SGuest) getKeypairName() string { keypair := self.getKeypair() if keypair != nil { return keypair.Name } return "" } func (self *SGuest) getNotifyIps() string { ips := self.GetRealIPs() vips := self.getVirtualIPs() if vips != nil { ips = append(ips, vips...) } return strings.Join(ips, ",") } /* func (self *SGuest) GetRealIPs() []string { guestnets, err := self.GetNetworks("") if err != nil { return nil } ips := make([]string, 0) for _, nic := range guestnets { if !nic.Virtual { ips = append(ips, nic.IpAddr) } } return ips } */ func (self *SGuest) IsExitOnly() bool { for _, ip := range self.GetRealIPs() { addr, _ := netutils.NewIPV4Addr(ip) if !netutils.IsExitAddress(addr) { return false } } return true } func (self *SGuest) getVirtualIPs() []string { ips := make([]string, 0) for _, guestgroup := range self.GetGroups() { group := guestgroup.GetGroup() groupnets, err := group.GetNetworks() if err != nil { continue } for _, groupnetwork := range groupnets { ips = append(ips, groupnetwork.IpAddr) } } return ips } func (self *SGuest) GetPrivateIPs() []string { ips := self.GetRealIPs() for i := len(ips) - 1; i >= 0; i-- { ipAddr, err := netutils.NewIPV4Addr(ips[i]) if err != nil { log.Errorf("guest %s(%s) has bad ipv4 address (%s): %v", self.Name, self.Id, ips[i], err) continue } if !netutils.IsPrivate(ipAddr) { ips = append(ips[:i], ips[i+1:]...) } } return ips } func (self *SGuest) getIPs() []string { ips := self.GetRealIPs() vips := self.getVirtualIPs() ips = append(ips, vips...) /*eip, _ := self.GetEip() if eip != nil { ips = append(ips, eip.IpAddr) }*/ return ips } func (self *SGuest) getZone() (*SZone, error) { host, err := self.GetHost() if err != nil { return nil, err } return host.GetZone() } func (self *SGuest) getRegion() (*SCloudregion, error) { zone, err := self.getZone() if err != nil { return nil, err } return zone.GetRegion() } func (self *SGuest) GetOS() string { if len(self.OsType) > 0 { return self.OsType } return self.GetMetadata(context.Background(), "os_name", nil) } func (self *SGuest) IsLinux() bool { os := self.GetOS() if strings.HasPrefix(strings.ToLower(os), "lin") { return true } else { return false } } func (self *SGuest) IsWindows() bool { os := self.GetOS() if strings.HasPrefix(strings.ToLower(os), "win") { return true } else { return false } } func (self *SGuest) getSecgroupJson() ([]*api.SecgroupJsonDesc, error) { ret := []*api.SecgroupJsonDesc{} secgroups, err := self.GetSecgroups() if err != nil { return nil, errors.Wrap(err, "GetSecgroups") } for _, secGrp := range secgroups { ret = append(ret, secGrp.getDesc()) } return ret, nil } func (self *SGuest) GetSecgroups() ([]SSecurityGroup, error) { secgrpQuery := SecurityGroupManager.Query() secgrpQuery.Filter( sqlchemy.OR( sqlchemy.Equals(secgrpQuery.Field("id"), self.SecgrpId), sqlchemy.In(secgrpQuery.Field("id"), GuestsecgroupManager.Query("secgroup_id").Equals("guest_id", self.Id).SubQuery()), ), ) secgroups := []SSecurityGroup{} err := db.FetchModelObjects(SecurityGroupManager, secgrpQuery, &secgroups) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return secgroups, nil } func (self *SGuest) getAdminSecgroup() *SSecurityGroup { secGrp, _ := SecurityGroupManager.FetchSecgroupById(self.AdminSecgrpId) return secGrp } func (self *SGuest) getAdminSecgroupName() string { secgrp := self.getAdminSecgroup() if secgrp != nil { return secgrp.GetName() } return "" } // 获取多个安全组规则,优先级降序排序 func (self *SGuest) getSecurityGroupsRules() string { secgroups, _ := self.GetSecgroups() secgroupids := []string{} for _, secgroup := range secgroups { secgroupids = append(secgroupids, secgroup.Id) } q := SecurityGroupRuleManager.Query() q.Filter(sqlchemy.In(q.Field("secgroup_id"), secgroupids)).Desc(q.Field("priority"), q.Field("action")) secrules := []SSecurityGroupRule{} if err := db.FetchModelObjects(SecurityGroupRuleManager, q, &secrules); err != nil { log.Errorf("Get security group rules error: %v", err) return "" } rules := []string{} for _, rule := range secrules { rules = append(rules, rule.String()) } return strings.Join(rules, SECURITY_GROUP_SEPARATOR) } func (self *SGuest) getAdminSecurityRules() string { secgrp := self.getAdminSecgroup() if secgrp != nil { ret, _ := secgrp.getSecurityRuleString() return ret } return "" } func (self *SGuest) IsFailureStatus() bool { return strings.Index(self.Status, "fail") >= 0 } var ( lostNamePattern = regexp.MustCompile(`-lost@\d{8}$`) ) func (self *SGuest) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) { host, err := self.GetHost() if err != nil { return nil, errors.Wrapf(err, "GetHost") } provider, err := host.GetDriver(ctx) if err != nil { return nil, errors.Wrapf(err, "host.GetDriver") } if provider.GetFactory().IsOnPremise() { return provider.GetOnPremiseIRegion() } return host.GetIRegion(ctx) } func (self *SGuest) SyncRemoveCloudVM(ctx context.Context, userCred mcclient.TokenCredential, check bool) error { lockman.LockObject(ctx, self) defer lockman.ReleaseObject(ctx, self) if self.BillingType == billing_api.BILLING_TYPE_PREPAID { diff, err := db.Update(self, func() error { self.BillingType = billing_api.BILLING_TYPE_POSTPAID self.ExpiredAt = time.Time{} return nil }) if err != nil { return err } db.OpsLog.LogSyncUpdate(self, diff, userCred) } if self.IsFailureStatus() { return nil } iregion, err := self.GetIRegion(ctx) if err != nil { return err } if check { iVM, err := iregion.GetIVMById(self.ExternalId) if err == nil { //漂移归位 if hostId := iVM.GetIHostId(); len(hostId) > 0 { host, err := db.FetchByExternalIdAndManagerId(HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { host, _ := self.GetHost() if host != nil { return q.Equals("manager_id", host.ManagerId) } return q }) if err == nil { _, err = db.Update(self, func() error { self.HostId = host.GetId() self.Status = iVM.GetStatus() self.PowerStates = iVM.GetPowerStates() self.InferPowerStates() return nil }) return err } } } else if errors.Cause(err) != cloudprovider.ErrNotFound { return errors.Wrap(err, "GetIVMById") } } if self.Status != api.VM_UNKNOWN { self.SetStatus(ctx, userCred, api.VM_UNKNOWN, "Sync lost") } if options.Options.EnableSyncPurge { log.Debugf("purge removed resource %s", self.Name) err := self.purge(ctx, userCred) if err != nil { return err } notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Obj: self, Action: notifyclient.ActionSyncDelete, }) return nil } if !lostNamePattern.MatchString(self.Name) { db.Update(self, func() error { self.Name = fmt.Sprintf("%s-lost@%s", self.Name, timeutils.ShortDate(time.Now())) return nil }) } return nil } func (guest *SGuest) SyncAllWithCloudVM(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, extVM cloudprovider.ICloudVM, syncStatus bool) error { if host == nil { return errors.Error("guest has no host") } provider := host.GetCloudprovider() if provider == nil { return errors.Error("host has no provider") } driver, err := provider.GetProvider(ctx) if err != nil { return errors.Wrap(err, "provider.GetProvider") } err = guest.syncWithCloudVM(ctx, userCred, driver, host, extVM, provider.GetOwnerId(), syncStatus) if err != nil { return errors.Wrap(err, "guest.syncWithCloudVM") } syncVMPeripherals(ctx, userCred, guest, extVM, host, provider, driver) return nil } func (g *SGuest) SyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, extVM cloudprovider.IOSInfo) error { return g.GetDriver().SyncOsInfo(ctx, userCred, g, extVM) } func (g *SGuest) SyncHostname(ext cloudprovider.ICloudVM) { hostname := pinyinutils.Text2Pinyin(ext.GetHostname()) if len(hostname) > 128 { hostname = hostname[:128] } if len(hostname) > 0 { g.Hostname = hostname } } func (g *SGuest) syncWithCloudVM(ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, host *SHost, extVM cloudprovider.ICloudVM, syncOwnerId mcclient.IIdentityProvider, syncStatus bool) error { recycle := false if provider.GetFactory().IsSupportPrepaidResources() && g.IsPrepaidRecycle() { recycle = true } diff, err := db.UpdateWithLock(ctx, g, func() error { if options.Options.EnableSyncName && !recycle { newName, _ := db.GenerateAlterName(g, extVM.GetName()) if len(newName) > 0 && newName != g.Name { g.Name = newName } } g.SyncHostname(extVM) if !g.IsFailureStatus() && syncStatus { g.Status = extVM.GetStatus() g.PowerStates = extVM.GetPowerStates() g.InferPowerStates() } g.VcpuCount = extVM.GetVcpuCount() g.CpuSockets = extVM.GetCpuSockets() g.BootOrder = extVM.GetBootOrder() g.Vga = extVM.GetVga() g.Vdi = extVM.GetVdi() if len(extVM.GetOsArch()) > 0 { g.OsArch = extVM.GetOsArch() } if len(g.OsType) == 0 { g.OsType = string(extVM.GetOsType()) } if len(g.Bios) == 0 { g.Bios = string(extVM.GetBios()) } g.Machine = extVM.GetMachine() if !recycle { g.HostId = host.Id } g.InternetMaxBandwidthOut = extVM.GetInternetMaxBandwidthOut() g.Throughput = extVM.GetThroughput() instanceType := extVM.GetInstanceType() if len(instanceType) > 0 { g.InstanceType = instanceType } memSizeMb := extVM.GetVmemSizeMB() if g.VmemSize == 0 || g.VmemSize != memSizeMb { if memSizeMb > 0 { g.VmemSize = memSizeMb } else { sku, _ := ServerSkuManager.FetchSkuByNameAndProvider(instanceType, provider.GetFactory().GetName(), false) if sku != nil && sku.MemorySizeMB > 0 { g.VmemSize = sku.MemorySizeMB } } } g.Hypervisor = extVM.GetHypervisor() if len(extVM.GetDescription()) > 0 { g.Description = extVM.GetDescription() } g.IsEmulated = extVM.IsEmulated() if provider.GetFactory().IsSupportPrepaidResources() && !recycle { g.BillingType = extVM.GetBillingType() g.ExpiredAt = extVM.GetExpiredAt() if g.GetDriver().IsSupportSetAutoRenew() { g.AutoRenew = extVM.IsAutoRenew() } } return nil }) if err != nil { log.Errorf("%s", err) return err } db.OpsLog.LogSyncUpdate(g, diff, userCred) if len(diff) > 0 { notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Obj: g, Action: notifyclient.ActionSyncUpdate, }) } g.SyncOsInfo(ctx, userCred, extVM) if account := host.GetCloudaccount(); account != nil { syncVirtualResourceMetadata(ctx, userCred, g, extVM, account.ReadOnly) } if cloudprovider := host.GetCloudprovider(); cloudprovider != nil { SyncCloudProject(ctx, userCred, g, syncOwnerId, extVM, cloudprovider) } if provider.GetFactory().IsSupportPrepaidResources() && recycle { vhost, _ := g.GetHost() err = vhost.syncWithCloudPrepaidVM(extVM, host) if err != nil { return err } } return nil } func (manager *SGuestManager) newCloudVM(ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, host *SHost, extVM cloudprovider.ICloudVM, syncOwnerId mcclient.IIdentityProvider) (*SGuest, error) { guest := SGuest{} guest.SetModelManager(manager, &guest) guest.Status = extVM.GetStatus() guest.PowerStates = extVM.GetPowerStates() guest.InferPowerStates() guest.ExternalId = extVM.GetGlobalId() guest.VcpuCount = extVM.GetVcpuCount() guest.CpuSockets = extVM.GetCpuSockets() guest.BootOrder = extVM.GetBootOrder() guest.Vga = extVM.GetVga() guest.Vdi = extVM.GetVdi() guest.OsArch = extVM.GetOsArch() guest.OsType = string(extVM.GetOsType()) guest.Bios = string(extVM.GetBios()) guest.Machine = extVM.GetMachine() guest.Hypervisor = extVM.GetHypervisor() hostname := extVM.GetHostname() if len(hostname) == 0 { hostname = extVM.GetName() } guest.Hostname = pinyinutils.Text2Pinyin(hostname) guest.InternetMaxBandwidthOut = extVM.GetInternetMaxBandwidthOut() guest.Throughput = extVM.GetThroughput() guest.Description = extVM.GetDescription() guest.IsEmulated = extVM.IsEmulated() if provider.GetFactory().IsSupportPrepaidResources() { guest.BillingType = extVM.GetBillingType() if expired := extVM.GetExpiredAt(); !expired.IsZero() { guest.ExpiredAt = expired } if guest.GetDriver().IsSupportSetAutoRenew() { guest.AutoRenew = extVM.IsAutoRenew() } } if createdAt := extVM.GetCreatedAt(); !createdAt.IsZero() { guest.CreatedAt = createdAt } guest.HostId = host.Id instanceType := extVM.GetInstanceType() /*zoneExtId, err := metaData.GetString("zone_ext_id") if err != nil { log.Errorf("get zone external id fail %s", err) } isku, err := ServerSkuManager.FetchByZoneExtId(zoneExtId, instanceType) if err != nil { log.Errorf("get sku zone %s instance type %s fail %s", zoneExtId, instanceType, err) } else { guest.SkuId = isku.GetId() }*/ if len(instanceType) > 0 { guest.InstanceType = instanceType } if extVM.GetHypervisor() == api.HYPERVISOR_AWS { sku, err := ServerSkuManager.FetchSkuByNameAndProvider(instanceType, api.CLOUD_PROVIDER_AWS, false) if err == nil { guest.VmemSize = sku.MemorySizeMB } else { guest.VmemSize = extVM.GetVmemSizeMB() } } else { guest.VmemSize = extVM.GetVmemSizeMB() } var err = func() error { lockman.LockRawObject(ctx, manager.Keyword(), "name") defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name") newName, err := db.GenerateName(ctx, manager, syncOwnerId, extVM.GetName()) if err != nil { return errors.Wrapf(err, "db.GenerateName") } guest.Name = newName return manager.TableSpec().Insert(ctx, &guest) }() if err != nil { return nil, errors.Wrapf(err, "Insert") } guest.SyncOsInfo(ctx, userCred, extVM) syncVirtualResourceMetadata(ctx, userCred, &guest, extVM, false) if cloudprovider := host.GetCloudprovider(); cloudprovider != nil { SyncCloudProject(ctx, userCred, &guest, syncOwnerId, extVM, cloudprovider) } db.OpsLog.LogEvent(&guest, db.ACT_CREATE, guest.GetShortDesc(ctx), userCred) if guest.Status == api.VM_RUNNING { db.OpsLog.LogEvent(&guest, db.ACT_START, guest.GetShortDesc(ctx), userCred) } notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Obj: &guest, Action: notifyclient.ActionSyncCreate, }) if guest.GetDriver().GetMaxSecurityGroupCount() == 0 { db.Update(&guest, func() error { guest.SecgrpId = "" return nil }) } if guest.Status == api.VM_RUNNING { db.OpsLog.LogEvent(&guest, db.ACT_START, guest.GetShortDesc(ctx), userCred) } return &guest, nil } func (manager *SGuestManager) TotalCount( ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, status []string, hypervisors []string, includeSystem bool, pendingDelete bool, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, since *time.Time, policyResult rbacutils.SPolicyResult, ) SGuestCountStat { return usageTotalGuestResouceCount(ctx, scope, ownerId, rangeObjs, status, hypervisors, includeSystem, pendingDelete, hostTypes, resourceTypes, providers, brands, cloudEnv, since, policyResult) } func (self *SGuest) detachNetworks(ctx context.Context, userCred mcclient.TokenCredential, gns []SGuestnetwork, reserve bool) error { err := GuestnetworkManager.DeleteGuestNics(ctx, userCred, gns, reserve) if err != nil { return err } host, _ := self.GetHost() if host != nil { host.ClearSchedDescCache() // ignore error } return nil } func (self *SGuest) getAttach2NetworkCount(net *SNetwork) (int, error) { q := GuestnetworkManager.Query() q = q.Equals("guest_id", self.Id).Equals("network_id", net.Id) return q.CountWithError() } func (self *SGuest) getUsableNicIndex() int { nics, err := self.GetNetworks("") if err != nil { return -1 } maxIndex := len(nics) for i := 0; i <= maxIndex; i++ { found := true for j := range nics { if nics[j].Index == i { found = false break } } if found { return i } } panic(fmt.Sprintf("cannot find usable nic index for guest %s(%s)", self.Name, self.Id)) } func (self *SGuest) setOSProfile(ctx context.Context, userCred mcclient.TokenCredential, profile jsonutils.JSONObject) error { return self.SetMetadata(ctx, "__os_profile__", profile, userCred) } func (self *SGuest) GetOSProfile() osprofile.SOSProfile { osName := self.GetOS() osProf := osprofile.GetOSProfile(osName, self.Hypervisor) val := self.GetMetadata(context.Background(), "__os_profile__", nil) if len(val) > 0 { jsonVal, _ := jsonutils.ParseString(val) if jsonVal != nil { jsonVal.Unmarshal(&osProf) } } return osProf } // Summary of network address allocation strategy // // # IpAddr when specified must be part of the network // // Use IpAddr without checking if it's already allocated when UseDesignatedIP // is true. See b31bc7fa ("feature: 1. baremetal server reuse host ip...") // // Try IpAddr from reserved pool when allowed by TryReserved. Otherwise // fallback to usual allocation method (AllocDir). Error when // RequireDesignatedIP is true and the allocated address does not match IpAddr type Attach2NetworkArgs struct { Network *SNetwork IpAddr string Ip6Addr string AllocDir api.IPAllocationDirection TryReserved bool RequireDesignatedIP bool UseDesignatedIP bool RequireIPv6 bool BwLimit int NicDriver string NumQueues int RxTrafficLimit int64 TxTrafficLimit int64 NicConfs []SNicConfig Virtual bool IsDefault bool PendingUsage quotas.IQuota } func (args *Attach2NetworkArgs) onceArgs(i int) attach2NetworkOnceArgs { if i < 0 || i > len(args.NicConfs)-1 { return attach2NetworkOnceArgs{} } r := attach2NetworkOnceArgs{ network: args.Network, ipAddr: args.IpAddr, ip6Addr: args.Ip6Addr, allocDir: args.AllocDir, tryReserved: args.TryReserved, requireDesignatedIP: args.RequireDesignatedIP, useDesignatedIP: args.UseDesignatedIP, requireIPv6: args.RequireIPv6, bwLimit: args.BwLimit, nicDriver: args.NicDriver, numQueues: args.NumQueues, txTrafficLimit: args.TxTrafficLimit, rxTrafficLimit: args.RxTrafficLimit, nicConf: args.NicConfs[i], virtual: args.Virtual, isDefault: args.IsDefault, pendingUsage: args.PendingUsage, } if i > 0 { r.ipAddr = "" r.ip6Addr = "" r.bwLimit = 0 r.virtual = true r.tryReserved = false r.requireDesignatedIP = false r.useDesignatedIP = false r.nicConf = args.NicConfs[i] r.nicDriver = "" r.numQueues = 1 r.isDefault = false r.requireIPv6 = false } return r } type attach2NetworkOnceArgs struct { network *SNetwork ipAddr string ip6Addr string allocDir api.IPAllocationDirection tryReserved bool requireDesignatedIP bool useDesignatedIP bool requireIPv6 bool bwLimit int nicDriver string numQueues int nicConf SNicConfig teamWithMac string rxTrafficLimit int64 txTrafficLimit int64 virtual bool isDefault bool pendingUsage quotas.IQuota } func (self *SGuest) Attach2Network( ctx context.Context, userCred mcclient.TokenCredential, args Attach2NetworkArgs, ) ([]SGuestnetwork, error) { log.Debugf("Attach2Network %s", jsonutils.Marshal(args)) onceArgs := args.onceArgs(0) firstNic, err := self.attach2NetworkOnce(ctx, userCred, onceArgs) if err != nil { return nil, errors.Wrap(err, "self.attach2NetworkOnce") } retNics := []SGuestnetwork{*firstNic} if len(args.NicConfs) > 1 { firstMac, _ := netutils.ParseMac(firstNic.MacAddr) for i := 1; i < len(args.NicConfs); i += 1 { onceArgs := args.onceArgs(i) onceArgs.nicDriver = firstNic.Driver onceArgs.teamWithMac = firstNic.MacAddr if onceArgs.nicConf.Mac == "" { onceArgs.nicConf.Mac = firstMac.Add(i).String() } gn, err := self.attach2NetworkOnce(ctx, userCred, onceArgs) if err != nil { return retNics, errors.Wrap(err, "self.attach2NetworkOnce") } retNics = append(retNics, *gn) } } return retNics, nil } func (self *SGuest) attach2NetworkOnce( ctx context.Context, userCred mcclient.TokenCredential, args attach2NetworkOnceArgs, ) (*SGuestnetwork, error) { var ( index = args.nicConf.Index nicDriver = args.nicDriver ) if index < 0 { index = self.getUsableNicIndex() } if nicDriver == "" { osProf := self.GetOSProfile() nicDriver = osProf.NetDriver } newArgs := newGuestNetworkArgs{ guest: self, network: args.network, index: index, ipAddr: args.ipAddr, ip6Addr: args.ip6Addr, allocDir: args.allocDir, tryReserved: args.tryReserved, requireDesignatedIP: args.requireDesignatedIP, useDesignatedIP: args.useDesignatedIP, requireIPv6: args.requireIPv6, ifname: args.nicConf.Ifname, macAddr: args.nicConf.Mac, bwLimit: args.bwLimit, nicDriver: nicDriver, numQueues: args.numQueues, teamWithMac: args.teamWithMac, rxTrafficLimit: args.rxTrafficLimit, txTrafficLimit: args.txTrafficLimit, virtual: args.virtual, isDefault: args.isDefault, } lockman.LockClass(ctx, QuotaManager, self.ProjectId) defer lockman.ReleaseClass(ctx, QuotaManager, self.ProjectId) guestnic, err := GuestnetworkManager.newGuestNetwork(ctx, userCred, newArgs) if err != nil { return nil, errors.Wrap(err, "GuestnetworkManager.newGuestNetwork") } var ( network = args.network pendingUsage = args.pendingUsage teamWithMac = args.teamWithMac ) network.updateDnsRecord(guestnic, true) network.updateGuestNetmap(guestnic) if pendingUsage != nil && len(teamWithMac) == 0 { cancelUsage := SRegionQuota{} if network.IsExitNetwork() { cancelUsage.Eport = 1 } else { cancelUsage.Port = 1 } keys, err := self.GetRegionalQuotaKeys() if err != nil { log.Warningf("self.GetRegionalQuotaKeys fail %s", err) } cancelUsage.SetKeys(keys) err = quotas.CancelPendingUsage(ctx, userCred, pendingUsage, &cancelUsage, true) if err != nil { log.Warningf("QuotaManager.CancelPendingUsage fail %s", err) } } db.OpsLog.LogAttachEvent(ctx, self, network, userCred, guestnic.GetShortDesc(ctx)) return guestnic, nil } type sRemoveGuestnic struct { nic *SGuestnetwork reserve bool } type sAddGuestnic struct { index int nic cloudprovider.ICloudNic net *SNetwork reserve bool } func getCloudNicNetwork(ctx context.Context, vnic cloudprovider.ICloudNic, host *SHost, ipList []string, index int) (*SNetwork, error) { vnetId := vnic.GetINetworkId() if len(vnetId) == 0 { if vnic.InClassicNetwork() { region, _ := host.GetRegion() cloudprovider := host.GetCloudprovider() vpc, err := VpcManager.GetOrCreateVpcForClassicNetwork(ctx, cloudprovider, region) if err != nil { return nil, errors.Wrap(err, "NewVpcForClassicNetwork") } zone, _ := host.GetZone() wire, err := WireManager.GetOrCreateWireForClassicNetwork(ctx, vpc, zone) if err != nil { return nil, errors.Wrap(err, "NewWireForClassicNetwork") } return NetworkManager.GetOrCreateClassicNetwork(ctx, wire) } ip := vnic.GetIP() if len(ip) == 0 { if index < len(ipList) { ip = ipList[index] } if len(ip) == 0 { return nil, fmt.Errorf("Cannot find inetwork for vnics %s: no ip", vnic.GetMAC()) } } // find network by IP return host.getNetworkOfIPOnHost(ctx, ip) } localNetObj, err := db.FetchByExternalIdAndManagerId(NetworkManager, vnetId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { // vpc := VpcManager.Query().SubQuery() wire := WireManager.Query().SubQuery() return q.Join(wire, sqlchemy.Equals(q.Field("wire_id"), wire.Field("id"))). Filter(sqlchemy.Equals(wire.Field("manager_id"), host.ManagerId)) }) if err != nil { return nil, errors.Wrapf(err, "Cannot find network of external_id %s", vnetId) } localNet := localNetObj.(*SNetwork) return localNet, nil } func (self *SGuest) SyncVMNics( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, vnics []cloudprovider.ICloudNic, ipList []string, ) compare.SyncResult { result := compare.SyncResult{} nics, err := self.GetNetworks("") if err != nil { result.Error(err) return result } removed := make([]SGuestnetwork, 0) commondb := make([]SGuestnetwork, 0) commonext := make([]cloudprovider.ICloudNic, 0) added := make([]cloudprovider.ICloudNic, 0) set := compare.SCompareSet{ DBFunc: "GetMAC", DBSet: nics, ExtFunc: "GetMAC", ExtSet: vnics, } err = compare.CompareSetsFunc(set, &removed, &commondb, &commonext, &added, nil) if err != nil { result.Error(errors.Wrapf(err, "compare.CompareSets")) return result } log.Debugf("SyncVMNics: removed: %d common: %d add: %d", len(removed), len(commondb), len(added)) for i := 0; i < len(removed); i += 1 { err = self.detachNetworks(ctx, userCred, []SGuestnetwork{removed[i]}, false) if err != nil { result.DeleteError(err) continue } result.Delete() } for i := 0; i < len(commondb); i += 1 { err := NetworkAddressManager.syncGuestnetworkICloudNic(ctx, userCred, &commondb[i], commonext[i]) if err != nil { result.UpdateError(err) continue } _, err = db.Update(&commondb[i], func() error { network, _ := commondb[i].GetNetwork() ip := commonext[i].GetIP() ip6 := commonext[i].GetIP6() if len(ip) > 0 { if !network.Contains(ip) { localNet, err := getCloudNicNetwork(ctx, commonext[i], host, ipList, i) if err != nil { return errors.Wrapf(err, "getCloudNicNetwork") } commondb[i].NetworkId = localNet.Id commondb[i].IpAddr = ip } else { commondb[i].IpAddr = ip commondb[i].Ip6Addr = ip6 } } commondb[i].Driver = commonext[i].GetDriver() return nil }) if err != nil { result.UpdateError(errors.Wrapf(err, "db.Update")) continue } result.Update() } syncIps := make([]string, 0) for i := 0; i < len(added); i += 1 { localNet, err := getCloudNicNetwork(ctx, added[i], host, ipList, i) if err != nil { log.Errorf("SyncVMNics getCloudNicNetwork add fail: %s", err) if ip := added[i].GetIP(); len(ip) > 0 { syncIps = append(syncIps, ip) } result.AddError(err) continue } nicConf := SNicConfig{ Mac: added[i].GetMAC(), Index: -1, Ifname: "", } ip := added[i].GetIP() // vmware, may be sync fix ip if len(ip) == 0 && len(ipList) > 0 { ip = ipList[0] } // always try allocate from reserved pool guestnetworks, err := self.Attach2Network(ctx, userCred, Attach2NetworkArgs{ Network: localNet, IpAddr: ip, Ip6Addr: added[i].GetIP6(), NicDriver: added[i].GetDriver(), TryReserved: true, AllocDir: api.IPAllocationDefault, RequireDesignatedIP: true, // UseDesignatedIP: true, NicConfs: []SNicConfig{nicConf}, }) if err != nil { result.AddError(err) continue } if len(ipList) > 0 { // shift ipList = ipList[1:] } result.Add() for i := range guestnetworks { guestnetwork := &guestnetworks[i] if NetworkAddressManager.syncGuestnetworkICloudNic( ctx, userCred, guestnetwork, added[i]); err != nil { result.AddError(err) } } } if len(syncIps) > 0 { self.SetMetadata(ctx, "sync_ips", strings.Join(syncIps, ","), userCred) } else { self.SetMetadata(ctx, "sync_ips", "None", userCred) } return result } func (self *SGuest) IsAttach2Disk(disk *SDisk) (bool, error) { return self.isAttach2Disk(disk) } func (self *SGuest) isAttach2Disk(disk *SDisk) (bool, error) { q := GuestdiskManager.Query().Equals("disk_id", disk.Id).Equals("guest_id", self.Id) cnt, err := q.CountWithError() if err != nil { return false, err } return cnt > 0, nil } func (self *SGuest) getDiskIndex() int8 { guestdisks, _ := self.GetGuestDisks() var max uint for i := 0; i < len(guestdisks); i++ { if uint(guestdisks[i].Index) > max { max = uint(guestdisks[i].Index) } } idxs := make([]int, max+1) for i := 0; i < len(guestdisks); i++ { idxs[guestdisks[i].Index] = 1 } // find first idx not set for i := 0; i < len(idxs); i++ { if idxs[i] != 1 { return int8(i) } } return int8(max + 1) } func (self *SGuest) AttachDisk(ctx context.Context, disk *SDisk, userCred mcclient.TokenCredential, driver string, cache string, mountpoint string, bootIndex *int8) error { return self.attach2Disk(ctx, disk, userCred, driver, cache, mountpoint, bootIndex) } func (self *SGuest) attach2Disk(ctx context.Context, disk *SDisk, userCred mcclient.TokenCredential, driver string, cache string, mountpoint string, bootIndex *int8) error { attached, err := self.isAttach2Disk(disk) if err != nil { return errors.Wrap(err, "isAttach2Disk") } if attached { return fmt.Errorf("Guest has been attached to disk") } if len(driver) == 0 { // depends the last disk of this guest existingDisks, _ := self.GetGuestDisks() if len(existingDisks) > 0 { prevDisk := existingDisks[len(existingDisks)-1] if prevDisk.Driver == api.DISK_DRIVER_IDE { driver = api.DISK_DRIVER_VIRTIO } else { driver = prevDisk.Driver } } else { osProf := self.GetOSProfile() driver = osProf.DiskDriver } } guestdisk := SGuestdisk{} guestdisk.SetModelManager(GuestdiskManager, &guestdisk) guestdisk.DiskId = disk.Id guestdisk.GuestId = self.Id lockman.LockObject(ctx, self) defer lockman.ReleaseObject(ctx, self) guestdisk.Index = self.getDiskIndex() if bootIndex != nil { guestdisk.BootIndex = *bootIndex } else { guestdisk.BootIndex = -1 } err = guestdisk.DoSave(ctx, driver, cache, mountpoint) if err == nil { db.OpsLog.LogAttachEvent(ctx, self, disk, userCred, nil) } return err } func (self *SGuest) SyncVMDisks( ctx context.Context, userCred mcclient.TokenCredential, provider cloudprovider.ICloudProvider, host *SHost, vdisks []cloudprovider.ICloudDisk, syncOwnerId mcclient.IIdentityProvider, ) compare.SyncResult { lockman.LockRawObject(ctx, self.Id, DiskManager.Keyword()) defer lockman.ReleaseRawObject(ctx, self.Id, DiskManager.Keyword()) result := compare.SyncResult{} dbDisks, err := self.GetDisks() if err != nil { result.Error(errors.Wrapf(err, "GetDisks")) return result } removed := make([]SDisk, 0) commondb := make([]SDisk, 0) commonext := make([]cloudprovider.ICloudDisk, 0) added := make([]cloudprovider.ICloudDisk, 0) err = compare.CompareSets(dbDisks, vdisks, &removed, &commondb, &commonext, &added) if err != nil { result.Error(errors.Wrapf(err, "compare.CompareSets")) return result } for i := 0; i < len(removed); i += 1 { self.DetachDisk(ctx, &removed[i], userCred) result.Delete() } for i := 0; i < len(commondb); i += 1 { if commondb[i].PendingDeleted != self.PendingDeleted { //避免主机正常,磁盘在回收站的情况 db.Update(&commondb[i], func() error { commondb[i].PendingDeleted = self.PendingDeleted return nil }) } commondb[i].SyncCloudProjectId(userCred, self.GetOwnerId()) result.Update() } for i := 0; i < len(added); i += 1 { disk, err := DiskManager.findOrCreateDisk(ctx, userCred, provider, added[i], -1, self.GetOwnerId(), host.ManagerId) if err != nil { result.AddError(errors.Wrapf(err, "findOrCreateDisk(%s)", added[i].GetGlobalId())) continue } disk.SyncCloudProjectId(userCred, self.GetOwnerId()) err = self.attach2Disk(ctx, disk, userCred, added[i].GetDriver(), added[i].GetCacheMode(), added[i].GetMountpoint(), nil) if err != nil { result.AddError(err) continue } result.Add() } err = self.fixSysDiskIndex() if err != nil { result.Error(errors.Wrapf(err, "fixSysDiskIndex")) } return result } func (self *SGuest) fixSysDiskIndex() error { disks := DiskManager.Query().SubQuery() sysQ := GuestdiskManager.Query().Equals("guest_id", self.Id) sysQ = sysQ.Join(disks, sqlchemy.Equals(disks.Field("id"), sysQ.Field("disk_id"))).Filter(sqlchemy.Equals(disks.Field("disk_type"), api.DISK_TYPE_SYS)) sysDisk := &SGuestdisk{} sysDisk.SetModelManager(GuestdiskManager, sysDisk) err := sysQ.First(sysDisk) if err != nil { if errors.Cause(err) == sql.ErrNoRows { return nil } return err } if sysDisk.Index == 0 { return nil } q := GuestdiskManager.Query().Equals("guest_id", self.Id).Equals("index", 0) firstDisk := &SGuestdisk{} firstDisk.SetModelManager(GuestdiskManager, firstDisk) err = q.First(firstDisk) if err != nil { return err } _, err = db.Update(firstDisk, func() error { firstDisk.Index = sysDisk.Index return nil }) if err != nil { return err } _, err = db.Update(sysDisk, func() error { sysDisk.Index = 0 return nil }) return err } func filterGuestByRange(q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string) *sqlchemy.SQuery { hosts := HostManager.Query().SubQuery() subq := hosts.Query(hosts.Field("id")) subq = AttachUsageQuery(subq, hosts, hostTypes, resourceTypes, providers, brands, cloudEnv, rangeObjs) q = q.In("host_id", subq.SubQuery()) return q } type SGuestCountStat struct { TotalGuestCount int TotalCpuCount int TotalMemSize int TotalDiskSize int TotalIsolatedCount int TotalBackupGuestCount int TotalBackupCpuCount int TotalBackupMemSize int TotalBackupDiskSize int } func usageTotalGuestResouceCount( ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, status []string, hypervisors []string, includeSystem bool, pendingDelete bool, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, since *time.Time, policyResult rbacutils.SPolicyResult, ) SGuestCountStat { q, guests := _guestResourceCountQuery( ctx, scope, ownerId, rangeObjs, status, hypervisors, pendingDelete, hostTypes, resourceTypes, providers, brands, cloudEnv, since, policyResult, ) if !includeSystem { q = q.Filter(sqlchemy.OR( sqlchemy.IsNull(guests.Field("is_system")), sqlchemy.IsFalse(guests.Field("is_system")))) } stat := SGuestCountStat{} row := q.Row() err := q.Row2Struct(row, &stat) if err != nil { log.Errorf("%s", err) } stat.TotalCpuCount += stat.TotalBackupCpuCount stat.TotalMemSize += stat.TotalBackupMemSize stat.TotalDiskSize += stat.TotalBackupDiskSize return stat } func _guestResourceCountQuery( ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, status []string, hypervisors []string, pendingDelete bool, hostTypes []string, resourceTypes []string, providers []string, brands []string, cloudEnv string, since *time.Time, policyResult rbacutils.SPolicyResult, ) (*sqlchemy.SQuery, *sqlchemy.SSubQuery) { guestdisks := GuestdiskManager.Query().SubQuery() disks := DiskManager.Query().SubQuery() diskQuery := guestdisks.Query(guestdisks.Field("guest_id"), sqlchemy.SUM("guest_disk_size", disks.Field("disk_size"))) diskQuery = diskQuery.Join(disks, sqlchemy.Equals(guestdisks.Field("disk_id"), disks.Field("id"))) diskQuery = diskQuery.GroupBy(guestdisks.Field("guest_id")) diskSubQuery := diskQuery.SubQuery() backupDiskQuery := guestdisks.Query(guestdisks.Field("guest_id"), sqlchemy.SUM("guest_disk_size", disks.Field("disk_size"))) backupDiskQuery = backupDiskQuery.LeftJoin(disks, sqlchemy.Equals(guestdisks.Field("disk_id"), disks.Field("id"))) backupDiskQuery = backupDiskQuery.Filter(sqlchemy.IsNotEmpty(disks.Field("backup_storage_id"))) backupDiskQuery = backupDiskQuery.GroupBy(guestdisks.Field("guest_id")) diskBackupSubQuery := backupDiskQuery.SubQuery() // diskBackupSubQuery := diskQuery.IsNotEmpty("backup_storage_id").SubQuery() isolated := IsolatedDeviceManager.Query().SubQuery() isoDevQuery := isolated.Query(isolated.Field("guest_id"), sqlchemy.COUNT("device_sum")) isoDevQuery = isoDevQuery.Filter(sqlchemy.IsNotNull(isolated.Field("guest_id"))) isoDevQuery = isoDevQuery.GroupBy(isolated.Field("guest_id")) isoDevSubQuery := isoDevQuery.SubQuery() var gq *sqlchemy.SQuery if since != nil && !since.IsZero() { gq = GuestManager.RawQuery() } else { gq = GuestManager.Query() } if len(rangeObjs) > 0 || len(hostTypes) > 0 || len(resourceTypes) > 0 || len(providers) > 0 || len(brands) > 0 || len(cloudEnv) > 0 { gq = filterGuestByRange(gq, rangeObjs, hostTypes, resourceTypes, providers, brands, cloudEnv) } switch scope { case rbacscope.ScopeSystem: case rbacscope.ScopeDomain: gq = gq.Filter(sqlchemy.Equals(gq.Field("domain_id"), ownerId.GetProjectDomainId())) case rbacscope.ScopeProject: gq = gq.Filter(sqlchemy.Equals(gq.Field("tenant_id"), ownerId.GetProjectId())) } if len(status) > 0 { gq = gq.Filter(sqlchemy.In(gq.Field("status"), status)) } if len(hypervisors) > 0 { gq = gq.Filter(sqlchemy.In(gq.Field("hypervisor"), hypervisors)) } if pendingDelete { gq = gq.Filter(sqlchemy.IsTrue(gq.Field("pending_deleted"))) } else { gq = gq.Filter(sqlchemy.OR(sqlchemy.IsNull(gq.Field("pending_deleted")), sqlchemy.IsFalse(gq.Field("pending_deleted")))) } if since != nil && !since.IsZero() { gq = gq.Filter(sqlchemy.GT(gq.Field("created_at"), *since)) } gq = db.ObjectIdQueryWithPolicyResult(ctx, gq, GuestManager, policyResult) guests := gq.SubQuery() guestBackupSubQuery := GuestManager.Query( "id", "vcpu_count", "vmem_size", ).IsNotEmpty("backup_host_id").SubQuery() q := guests.Query(sqlchemy.COUNT("total_guest_count"), sqlchemy.SUM("total_cpu_count", guests.Field("vcpu_count")), sqlchemy.SUM("total_mem_size", guests.Field("vmem_size")), sqlchemy.SUM("total_disk_size", diskSubQuery.Field("guest_disk_size")), sqlchemy.SUM("total_isolated_count", isoDevSubQuery.Field("device_sum")), sqlchemy.SUM("total_backup_disk_size", diskBackupSubQuery.Field("guest_disk_size")), sqlchemy.SUM("total_backup_cpu_count", guestBackupSubQuery.Field("vcpu_count")), sqlchemy.SUM("total_backup_mem_size", guestBackupSubQuery.Field("vmem_size")), sqlchemy.COUNT("total_backup_guest_count", guestBackupSubQuery.Field("id")), ) q = q.LeftJoin(guestBackupSubQuery, sqlchemy.Equals(guestBackupSubQuery.Field("id"), guests.Field("id"))) q = q.LeftJoin(diskSubQuery, sqlchemy.Equals(diskSubQuery.Field("guest_id"), guests.Field("id"))) q = q.LeftJoin(diskBackupSubQuery, sqlchemy.Equals(diskBackupSubQuery.Field("guest_id"), guests.Field("id"))) q = q.LeftJoin(isoDevSubQuery, sqlchemy.Equals(isoDevSubQuery.Field("guest_id"), guests.Field("id"))) return q, guests } func (self *SGuest) getDefaultNetworkConfig() *api.NetworkConfig { netConf := api.NetworkConfig{} netConf.BwLimit = options.Options.DefaultBandwidth osProf := self.GetOSProfile() netConf.Driver = osProf.NetDriver return &netConf } func (self *SGuest) CreateNetworksOnHost( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netArray []*api.NetworkConfig, pendingUsage, pendingUsageZone quotas.IQuota, candidateNets []*schedapi.CandidateNet, ) error { if len(netArray) == 0 { netConfig := self.getDefaultNetworkConfig() _, err := self.attach2RandomNetwork(ctx, userCred, host, netConfig, pendingUsage) return errors.Wrap(err, "self.attach2RandomNetwork") } for idx := range netArray { netConfig, err := parseNetworkInfo(ctx, userCred, netArray[idx]) if err != nil { return errors.Wrapf(err, "parseNetworkInfo at %d", idx) } var candidateNet *schedapi.CandidateNet if len(candidateNets) > idx { candidateNet = candidateNets[idx] } networkIds := []string{} if candidateNet != nil { networkIds = candidateNet.NetworkIds } if idx == 0 && netConfig.NumQueues == 0 { numQueues := self.VcpuCount / 2 if numQueues > 16 { numQueues = 16 } netConfig.NumQueues = numQueues } gns, err := self.attach2NetworkDesc(ctx, userCred, host, netConfig, pendingUsage, networkIds) if err != nil { return errors.Wrap(err, "self.attach2NetworkDesc") } if netConfig.SriovDevice != nil { err = self.allocSriovNicDevice(ctx, userCred, host, &gns[0], netConfig, pendingUsageZone) if err != nil { return errors.Wrap(err, "self.allocSriovNicDevice") } } } return nil } func (self *SGuest) allocSriovNicDevice( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, gn *SGuestnetwork, netConfig *api.NetworkConfig, pendingUsageZone quotas.IQuota, ) error { net, err := gn.GetNetwork() if err != nil { return errors.Wrapf(err, "GetNetwork") } netConfig.SriovDevice.NetworkIndex = &gn.Index netConfig.SriovDevice.WireId = net.WireId err = self.createIsolatedDeviceOnHost(ctx, userCred, host, netConfig.SriovDevice, pendingUsageZone) if err != nil { return errors.Wrap(err, "self.createIsolatedDeviceOnHost") } dev, err := self.GetIsolatedDeviceByNetworkIndex(gn.Index) if err != nil { return errors.Wrap(err, "self.GetIsolatedDeviceByNetworkIndex") } if dev.OvsOffloadInterface != "" { _, err = db.Update(gn, func() error { gn.Ifname = dev.OvsOffloadInterface return nil }) if err != nil { return errors.Wrap(err, "update sriov network ifname") } } return nil } func (self *SGuest) attach2NetworkDesc( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig, pendingUsage quotas.IQuota, candiateNetIds []string, ) ([]SGuestnetwork, error) { var gns []SGuestnetwork var errs []error tryNetworkIds := []string{} if len(netConfig.Network) > 0 { tryNetworkIds = append(tryNetworkIds, netConfig.Network) } if len(candiateNetIds) > 0 { // suggestion by scheduler tryNetworkIds = append(tryNetworkIds, candiateNetIds...) } if len(tryNetworkIds) > 0 { for _, tryNetwork := range tryNetworkIds { var err error netConfig.Network = tryNetwork gns, err = self.attach2NamedNetworkDesc(ctx, userCred, host, netConfig, pendingUsage) if err == nil { return gns, nil } errs = append(errs, err) } return nil, errors.NewAggregate(errs) } else { netConfig.Network = "" return self.attach2RandomNetwork(ctx, userCred, host, netConfig, pendingUsage) } } func (self *SGuest) attach2NamedNetworkDesc(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig, pendingUsage quotas.IQuota) ([]SGuestnetwork, error) { driver := self.GetDriver() net, nicConfs, allocDir, reuseAddr, err := driver.GetNamedNetworkConfiguration(self, ctx, userCred, host, netConfig) if err != nil { if errors.Cause(err) == sql.ErrNoRows { return nil, errors.Wrapf(httperrors.ErrResourceNotReady, "Network not avaiable on host %q", host.GetName()) } else { return nil, errors.Wrapf(err, "GetNamedNetworkConfiguration on host %q", host.GetName()) } } if net != nil { if len(nicConfs) == 0 { return nil, fmt.Errorf("no available network interface?") } var sriovWires []string if netConfig.SriovDevice != nil { if netConfig.SriovDevice.Id != "" { idev, err := IsolatedDeviceManager.FetchById(netConfig.SriovDevice.Id) if err != nil { return nil, errors.Wrap(err, "fetch isolated device") } dev, _ := idev.(*SIsolatedDevice) sriovWires = []string{dev.WireId} } else { wires, err := IsolatedDeviceManager.FindUnusedNicWiresByModel(netConfig.SriovDevice.Model) if err != nil { return nil, errors.Wrap(err, "FindUnusedNicWiresByModel") } sriovWires = wires } vpc, err := net.GetVpc() if err != nil { return nil, errors.Wrap(err, "attach2NamedNetworkDesc get vpc by network") } if vpc.Id == api.DEFAULT_VPC_ID && !utils.IsInStringArray(net.WireId, sriovWires) { return nil, fmt.Errorf("no available sriov nic for wire %s", net.WireId) } } gn, err := self.Attach2Network(ctx, userCred, Attach2NetworkArgs{ Network: net, PendingUsage: pendingUsage, IpAddr: netConfig.Address, Ip6Addr: netConfig.Address6, RequireIPv6: netConfig.RequireIPv6, NicDriver: netConfig.Driver, NumQueues: netConfig.NumQueues, BwLimit: netConfig.BwLimit, RxTrafficLimit: netConfig.RxTrafficLimit, TxTrafficLimit: netConfig.TxTrafficLimit, Virtual: netConfig.Vip, TryReserved: netConfig.Reserved, AllocDir: allocDir, RequireDesignatedIP: netConfig.RequireDesignatedIP, UseDesignatedIP: reuseAddr, NicConfs: nicConfs, IsDefault: netConfig.IsDefault, }) if err != nil { return nil, errors.Wrap(err, "Attach2Network fail") } else { return gn, nil } } else { return nil, fmt.Errorf("Network %s not available", netConfig.Network) } } func (self *SGuest) attach2RandomNetwork(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig, pendingUsage quotas.IQuota) ([]SGuestnetwork, error) { driver := self.GetDriver() return driver.Attach2RandomNetwork(self, ctx, userCred, host, netConfig, pendingUsage) } func (self *SGuest) CreateDisksOnHost( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, disks []*api.DiskConfig, pendingUsage quotas.IQuota, inheritBilling bool, isWithServerCreate bool, candidateDisks []*schedapi.CandidateDisk, backupCandidateDisks []*schedapi.CandidateDisk, autoAttach bool, ) error { for idx := 0; idx < len(disks); idx += 1 { if len(disks[idx].DiskId) > 0 && len(disks[idx].Storage) > 0 { continue } diskConfig, err := parseDiskInfo(ctx, userCred, disks[idx]) if err != nil { return errors.Wrap(err, "parseDiskInfo") } var candidateDisk *schedapi.CandidateDisk var backupCandidateDisk *schedapi.CandidateDisk if len(candidateDisks) > idx { candidateDisk = candidateDisks[idx] } if len(backupCandidateDisks) != 0 && len(backupCandidateDisks) > idx { backupCandidateDisk = backupCandidateDisks[idx] } disk, err := self.createDiskOnHost(ctx, userCred, host, diskConfig, pendingUsage, inheritBilling, isWithServerCreate, candidateDisk, backupCandidateDisk, autoAttach) if err != nil { return err } diskConfig.DiskId = disk.Id disks[idx] = diskConfig if diskConfig.NVMEDevice != nil { err = self.attachNVMEDevice(ctx, userCred, host, pendingUsage, disk, diskConfig) if err != nil { return err } } } return nil } func (self *SGuest) attachNVMEDevice( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, pendingUsage quotas.IQuota, disk *SDisk, diskConfig *api.DiskConfig, ) error { gd := self.GetGuestDisk(disk.Id) diskConfig.NVMEDevice.DiskIndex = &gd.Index err := self.createIsolatedDeviceOnHost(ctx, userCred, host, diskConfig.NVMEDevice, pendingUsage) if err != nil { return errors.Wrap(err, "self.createIsolatedDeviceOnHost") } dev, err := self.GetIsolatedDeviceByDiskIndex(gd.Index) if err != nil { return errors.Wrap(err, "self.GetIsolatedDeviceByDiskIndex") } diskConfig.SizeMb = dev.NvmeSizeMB _, err = db.Update(disk, func() error { disk.DiskSize = dev.NvmeSizeMB return nil }) if err != nil { return errors.Wrap(err, "update nvme disk size") } return nil } func (self *SGuest) CreateDiskOnStorage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, diskConfig *api.DiskConfig, pendingUsage quotas.IQuota, inheritBilling bool, isWithServerCreate bool) (*SDisk, error) { lockman.LockObject(ctx, storage) defer lockman.ReleaseObject(ctx, storage) lockman.LockClass(ctx, QuotaManager, self.ProjectId) defer lockman.ReleaseClass(ctx, QuotaManager, self.ProjectId) diskName := fmt.Sprintf("vdisk-%s-%d", pinyinutils.Text2Pinyin(self.Name), time.Now().UnixNano()) billingType := billing_api.BILLING_TYPE_POSTPAID billingCycle := "" if inheritBilling { billingType = self.BillingType billingCycle = self.BillingCycle } autoDelete := false if storage.IsLocal() || billingType == billing_api.BILLING_TYPE_PREPAID || isWithServerCreate { autoDelete = true } disk, err := storage.createDisk(ctx, diskName, diskConfig, userCred, self.GetOwnerId(), autoDelete, self.IsSystem, billingType, billingCycle, self.EncryptKeyId) if err != nil { return nil, err } if pendingUsage != nil { cancelUsage := SQuota{} cancelUsage.Storage = disk.DiskSize keys, err := self.GetQuotaKeys() if err != nil { return nil, err } cancelUsage.SetKeys(keys) err = quotas.CancelPendingUsage(ctx, userCred, pendingUsage, &cancelUsage, true) if err != nil { return nil, err } } return disk, nil } func (self *SGuest) ChooseHostStorage(host *SHost, diskConfig *api.DiskConfig, candidate *schedapi.CandidateDisk) (*SStorage, error) { if candidate == nil || len(candidate.StorageIds) == 0 { return self.GetDriver().ChooseHostStorage(host, self, diskConfig, nil) } return self.GetDriver().ChooseHostStorage(host, self, diskConfig, candidate.StorageIds) } func (self *SGuest) createDiskOnHost( ctx context.Context, userCred mcclient.TokenCredential, host *SHost, diskConfig *api.DiskConfig, pendingUsage quotas.IQuota, inheritBilling bool, isWithServerCreate bool, candidate *schedapi.CandidateDisk, backupCandidate *schedapi.CandidateDisk, autoAttach bool, ) (*SDisk, error) { var ( storage *SStorage err error ) if len(diskConfig.Storage) > 0 { _storage, err := StorageManager.FetchByIdOrName(ctx, userCred, diskConfig.Storage) if err != nil { if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError2("storage", diskConfig.Storage) } return nil, fmt.Errorf("get storage(%s) error: %v", diskConfig.Storage, err) } storage = _storage.(*SStorage) } else { storage, err = self.ChooseHostStorage(host, diskConfig, candidate) if err != nil { return nil, errors.Wrap(err, "ChooseHostStorage") } } if storage == nil { return nil, fmt.Errorf("No storage on %s to create disk for %s", host.GetName(), diskConfig.Backend) } log.Debugf("Choose storage %s:%s for disk %#v", storage.Name, storage.Id, diskConfig) disk, err := self.CreateDiskOnStorage(ctx, userCred, storage, diskConfig, pendingUsage, inheritBilling, isWithServerCreate) if err != nil { return nil, err } if diskConfig.ExistingPath != "" { disk.SetMetadata(ctx, api.DISK_META_EXISTING_PATH, diskConfig.ExistingPath, userCred) } if len(self.BackupHostId) > 0 { backupHost := HostManager.FetchHostById(self.BackupHostId) backupStorage, err := self.ChooseHostStorage(backupHost, diskConfig, backupCandidate) if err != nil { return nil, errors.Wrap(err, "ChooseHostStorage") } diff, err := db.Update(disk, func() error { disk.BackupStorageId = backupStorage.Id return nil }) if err != nil { log.Errorf("Disk save backup storage error") return disk, err } db.OpsLog.LogEvent(disk, db.ACT_UPDATE, diff, userCred) } if autoAttach { err = self.attach2Disk(ctx, disk, userCred, diskConfig.Driver, diskConfig.Cache, diskConfig.Mountpoint, diskConfig.BootIndex) } err = self.InheritTo(ctx, userCred, disk) if err != nil { return nil, errors.Wrapf(err, "unable to inherit from guest %s to disk %s", self.GetId(), disk.GetId()) } return disk, err } func (self *SGuest) CreateIsolatedDeviceOnHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, devs []*api.IsolatedDeviceConfig, pendingUsage quotas.IQuota) error { for _, devConfig := range devs { if devConfig.DevType == api.NIC_TYPE || devConfig.DevType == api.NVME_PT_TYPE { continue } err := self.createIsolatedDeviceOnHost(ctx, userCred, host, devConfig, pendingUsage) if err != nil { return err } } return nil } func (self *SGuest) createIsolatedDeviceOnHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, devConfig *api.IsolatedDeviceConfig, pendingUsage quotas.IQuota) error { lockman.LockClass(ctx, QuotaManager, self.ProjectId) defer lockman.ReleaseClass(ctx, QuotaManager, self.ProjectId) err := IsolatedDeviceManager.attachHostDeviceToGuestByDesc(ctx, self, host, devConfig, userCred) if err != nil { return err } cancelUsage := SQuota{IsolatedDevice: 1} keys, err := self.GetQuotaKeys() if err != nil { return err } cancelUsage.SetKeys(keys) err = quotas.CancelPendingUsage(ctx, userCred, pendingUsage, &cancelUsage, true) // success return err } func (self *SGuest) JoinGroups(ctx context.Context, userCred mcclient.TokenCredential, groupIds []string) error { for _, id := range groupIds { _, err := GroupguestManager.Attach(ctx, id, self.Id) if err != nil { return err } } return nil } type SGuestDiskCategory struct { Root *SDisk Swap []*SDisk Data []*SDisk } func (self *SGuest) CategorizeDisks() SGuestDiskCategory { diskCat := SGuestDiskCategory{} guestdisks, err := self.GetGuestDisks() if err != nil { log.Errorf("no disk for this server!!!") return diskCat } for _, gd := range guestdisks { if diskCat.Root == nil { diskCat.Root = gd.GetDisk() } else { disk := gd.GetDisk() if disk.FsFormat == "swap" { diskCat.Swap = append(diskCat.Swap, disk) } else { diskCat.Data = append(diskCat.Data, disk) } } } return diskCat } type SGuestNicCategory struct { InternalNics []SGuestnetwork ExternalNics []SGuestnetwork } func (self *SGuest) CategorizeNics() SGuestNicCategory { netCat := SGuestNicCategory{} guestnics, err := self.GetNetworks("") if err != nil { log.Errorf("no nics for this server!!! %s", err) return netCat } for _, gn := range guestnics { if gn.IsExit() { netCat.ExternalNics = append(netCat.ExternalNics, gn) } else { netCat.InternalNics = append(netCat.InternalNics, gn) } } return netCat } func (self *SGuest) LeaveAllGroups(ctx context.Context, userCred mcclient.TokenCredential) { groupGuests := make([]SGroupguest, 0) q := GroupguestManager.Query() err := q.Filter(sqlchemy.Equals(q.Field("guest_id"), self.Id)).All(&groupGuests) if err != nil { log.Errorf("query by guest_id %s: %v", self.Id, err) return } for _, gg := range groupGuests { gg.SetModelManager(GroupguestManager, &gg) gg.Delete(context.Background(), userCred) var group SGroup gq := GroupManager.Query() err := gq.Filter(sqlchemy.Equals(gq.Field("id"), gg.GroupId)).First(&group) if err != nil { log.Errorf("get by group id %s: %v", gg.GroupId, err) return } group.SetModelManager(GroupManager, &group) db.OpsLog.LogDetachEvent(ctx, self, &group, userCred, nil) } } func (self *SGuest) DetachAllNetworks(ctx context.Context, userCred mcclient.TokenCredential) error { // from clouds.models.portmaps import Portmaps // Portmaps.delete_guest_network_portmaps(self, user_cred) gns, err := self.GetNetworks("") if err != nil { return err } return GuestnetworkManager.DeleteGuestNics(ctx, userCred, gns, false) } func (self *SGuest) EjectIso(cdromOrdinal int64, userCred mcclient.TokenCredential) bool { cdrom := self.getCdrom(false, cdromOrdinal) if cdrom != nil && len(cdrom.ImageId) > 0 { imageId := cdrom.ImageId if cdrom.ejectIso() { db.OpsLog.LogEvent(self, db.ACT_ISO_DETACH, imageId, userCred) return true } } return false } func (self *SGuest) EjectAllIso(userCred mcclient.TokenCredential) bool { cdroms, _ := self.getCdroms() for _, cdrom := range cdroms { if len(cdrom.ImageId) > 0 { imageId := cdrom.ImageId if cdrom.ejectIso() { db.OpsLog.LogEvent(self, db.ACT_ISO_DETACH, imageId, userCred) } else { return false } } } return true } func (self *SGuest) EjectVfd(floppyOrdinal int64, userCred mcclient.TokenCredential) bool { floppy := self.getFloppy(false, floppyOrdinal) if floppy != nil && len(floppy.ImageId) > 0 { imageId := floppy.ImageId if floppy.ejectVfd() { db.OpsLog.LogEvent(self, db.ACT_VFD_DETACH, imageId, userCred) return true } } return false } func (self *SGuest) EjectAllVfd(userCred mcclient.TokenCredential) bool { floppys, _ := self.getFloppys() for _, floppy := range floppys { if len(floppy.ImageId) > 0 { imageId := floppy.ImageId if floppy.ejectVfd() { db.OpsLog.LogEvent(self, db.ACT_ISO_DETACH, imageId, userCred) } else { return false } } } return true } func (self *SGuest) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { // self.SVirtualResourceBase.Delete(ctx, userCred) // override log.Infof("guest delete do nothing") return nil } func (self *SGuest) CleanTapRecords(ctx context.Context, userCred mcclient.TokenCredential) error { // delete tap devices if err := NetTapServiceManager.removeTapServicesByGuestId(ctx, userCred, self.Id); err != nil { return errors.Wrap(err, "NetTapServiceManager.getTapServicesByGuestId") } if err := NetTapFlowManager.removeTapFlowsByGuestId(ctx, userCred, self.Id); err != nil { return errors.Wrap(err, "NetTapFlowManager.getTapServicesByGuestId") } return nil } func (self *SGuest) GetLoadbalancerBackends() ([]SLoadbalancerBackend, error) { q := LoadbalancerBackendManager.Query().Equals("backend_id", self.Id) ret := []SLoadbalancerBackend{} return ret, db.FetchModelObjects(LoadbalancerBackendManager, q, &ret) } func (self *SGuest) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error { return self.purge(ctx, userCred) } func (self *SGuest) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { overridePendingDelete := false purge := false if query != nil { overridePendingDelete = jsonutils.QueryBoolean(query, "override_pending_delete", false) purge = jsonutils.QueryBoolean(query, "purge", false) } if (overridePendingDelete || purge) && !db.IsAdminAllowDelete(ctx, userCred, self) { return false } return self.IsOwner(userCred) || db.IsAdminAllowDelete(ctx, userCred, self) } // 删除虚拟机 func (self *SGuest) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query api.ServerDeleteInput, data jsonutils.JSONObject) error { return self.StartDeleteGuestTask(ctx, userCred, "", query) } func (self *SGuest) DeleteAllDisksInDB(ctx context.Context, userCred mcclient.TokenCredential) error { guestDisks, err := self.GetGuestDisks() if err != nil { return errors.Wrapf(err, "GetGuestDisks") } for _, guestdisk := range guestDisks { disk := guestdisk.GetDisk() err := guestdisk.Detach(ctx, userCred) if err != nil { return errors.Wrapf(err, "guestdisk.Detach guest_id: %s disk_id: %s", guestdisk.GuestId, guestdisk.DiskId) } if disk != nil { cnt, err := disk.GetGuestDiskCount() if err != nil { return errors.Wrap(err, "disk.GetGuestDiskCount") } if cnt == 0 { db.OpsLog.LogEvent(disk, db.ACT_DELETE, nil, userCred) db.OpsLog.LogEvent(disk, db.ACT_DELOCATE, nil, userCred) err = disk.RealDelete(ctx, userCred) if err != nil { return errors.Wrap(err, "disk.RealDelete") } } } } return nil } func (self *SGuest) DeleteAllInstanceSnapshotInDB(ctx context.Context, userCred mcclient.TokenCredential) error { isps, err := self.GetInstanceSnapshots() if err != nil { return errors.Wrap(err, "unable to GetInstanceSnapshots") } for i := range isps { err = isps[i].RealDelete(ctx, userCred) return errors.Wrapf(err, "unable to real delete instance snapshot %q for guest %q", isps[i].GetName(), self.GetId()) } return nil } func (self *SGuest) isNeedDoResetPasswd() bool { guestdisks, _ := self.GetGuestDisks() disk := guestdisks[0].GetDisk() if len(disk.SnapshotId) > 0 { return false } return true } func (self *SGuest) GetDeployConfigOnHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, params *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { config := jsonutils.NewDict() desc, err := self.GetDriver().GetJsonDescAtHost(ctx, userCred, self, host, params) if err != nil { return nil, errors.Wrapf(err, "GetJsonDescAtHost") } config.Add(desc, "desc") deploys, err := cmdline.FetchDeployConfigsByJSON(params) if err != nil { return nil, err } if len(deploys) > 0 { config.Add(jsonutils.Marshal(deploys), "deploys") } deployAction, _ := params.GetString("deploy_action") if len(deployAction) == 0 { deployAction = "deploy" } config.Add(jsonutils.NewBool(jsonutils.QueryBoolean(params, "enable_cloud_init", false)), "enable_cloud_init") if account, _ := params.GetString("login_account"); len(account) > 0 { config.Set("login_account", jsonutils.NewString(account)) } resetPasswd := jsonutils.QueryBoolean(params, "reset_password", true) if deployAction == "create" && resetPasswd { resetPasswd = self.isNeedDoResetPasswd() } if resetPasswd { config.Add(jsonutils.JSONTrue, "reset_password") passwd, _ := params.GetString("password") if len(passwd) > 0 { config.Add(jsonutils.NewString(passwd), "password") } keypair := self.getKeypair() if keypair != nil { config.Add(jsonutils.NewString(keypair.PublicKey), "public_key") } deletePubKey, _ := params.GetString("delete_public_key") if len(deletePubKey) > 0 { config.Add(jsonutils.NewString(deletePubKey), "delete_public_key") } } else { config.Add(jsonutils.JSONFalse, "reset_password") } // add default public keys _, adminPubKey, err := sshkeys.GetSshAdminKeypair(ctx) if err != nil { log.Errorf("fail to get ssh admin public key %s", err) } _, projPubKey, err := sshkeys.GetSshProjectKeypair(ctx, self.ProjectId) if err != nil { log.Errorf("fail to get ssh project public key %s", err) } config.Add(jsonutils.NewString(adminPubKey), "admin_public_key") config.Add(jsonutils.NewString(projPubKey), "project_public_key") config.Add(jsonutils.NewString(deployAction), "action") onFinish := "shutdown" if jsonutils.QueryBoolean(params, "auto_start", false) || jsonutils.QueryBoolean(params, "restart", false) { onFinish = "none" } else if utils.IsInStringArray(self.Status, []string{api.VM_ADMIN}) { onFinish = "none" } config.Add(jsonutils.NewString(onFinish), "on_finish") if jsonutils.QueryBoolean(params, "deploy_telegraf", false) { influxdbUrl := self.GetDriver().FetchMonitorUrl(ctx, self) config.Add(jsonutils.JSONTrue, "deploy_telegraf") serverDetails, err := self.getDetails(ctx, userCred) if err != nil { return nil, errors.Wrap(err, "get details") } telegrafConf, err := devtool_utils.GenerateTelegrafConf( serverDetails, influxdbUrl, self.OsType, self.Hypervisor) if err != nil { return nil, errors.Wrap(err, "get telegraf conf") } config.Add(jsonutils.NewString(telegrafConf), "telegraf_conf") } return config, nil } func (self *SGuest) getDetails(ctx context.Context, userCred mcclient.TokenCredential) (*api.ServerDetails, error) { res := GuestManager.FetchCustomizeColumns(ctx, userCred, jsonutils.NewDict(), []interface{}{self}, nil, false) jsonDict := jsonutils.Marshal(res[0]).(*jsonutils.JSONDict) jsonDict.Update(jsonutils.Marshal(self).(*jsonutils.JSONDict)) serverDetails := new(api.ServerDetails) err := jsonDict.Unmarshal(serverDetails) if err != nil { return nil, err } return serverDetails, nil } func (self *SGuest) isBootIndexDuplicated(bootIndex int8) (bool, error) { if bootIndex < 0 { return false, nil } cdroms, err := self.getCdroms() if err != nil { return true, err } for i := 0; i < len(cdroms); i++ { if cdroms[i].BootIndex == bootIndex { return true, nil } } gd, err := self.GetGuestDisks() if err != nil { return true, err } for i := 0; i < len(gd); i++ { if gd[i].BootIndex == bootIndex { return true, nil } } return false, nil } func (self *SGuest) getVga() string { if utils.IsInStringArray(self.Vga, []string{"cirrus", "vmware", "qxl", "virtio", "std"}) { return self.Vga } return "std" } func (self *SGuest) GetVdi() string { if utils.IsInStringArray(self.Vdi, []string{"vnc", "spice"}) { return self.Vdi } return "vnc" } func (self *SGuest) getMachine() string { if utils.IsInStringArray(self.Machine, []string{"pc", "q35"}) { return self.Machine } return "pc" } func (self *SGuest) getBios() string { if utils.IsInStringArray(self.Bios, []string{"BIOS", "UEFI"}) { return self.Bios } return "BIOS" } func (self *SGuest) getKvmOptions() string { return self.GetMetadata(context.Background(), "kvm", nil) } func (self *SGuest) getExtraOptions(ctx context.Context) jsonutils.JSONObject { return self.GetMetadataJson(ctx, "extra_options", nil) } func (self *SGuest) GetIsolatedDevices() ([]SIsolatedDevice, error) { q := IsolatedDeviceManager.Query().Equals("guest_id", self.Id) devs := []SIsolatedDevice{} err := db.FetchModelObjects(IsolatedDeviceManager, q, &devs) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return devs, nil } func (self *SGuest) GetIsolatedDeviceByNetworkIndex(index int) (*SIsolatedDevice, error) { dev := SIsolatedDevice{} q := IsolatedDeviceManager.Query().Equals("guest_id", self.Id).Equals("network_index", index) if cnt, err := q.CountWithError(); err != nil { return nil, err } else if cnt == 0 { return nil, nil } err := q.First(&dev) if err != nil { return nil, err } dev.SetModelManager(IsolatedDeviceManager, &dev) return &dev, nil } func (self *SGuest) GetIsolatedDeviceByDiskIndex(index int8) (*SIsolatedDevice, error) { dev := SIsolatedDevice{} q := IsolatedDeviceManager.Query().Equals("guest_id", self.Id).Equals("disk_index", index) if cnt, err := q.CountWithError(); err != nil { return nil, err } else if cnt == 0 { return nil, nil } err := q.First(&dev) if err != nil { return nil, err } dev.SetModelManager(IsolatedDeviceManager, &dev) return &dev, nil } func (self *SGuest) GetJsonDescAtHypervisor(ctx context.Context, host *SHost) *api.GuestJsonDesc { desc := &api.GuestJsonDesc{ Name: self.Name, Hostname: self.Hostname, Description: self.Description, UUID: self.Id, Mem: self.VmemSize, Cpu: self.VcpuCount, CpuSockets: self.CpuSockets, Vga: self.getVga(), Vdi: self.GetVdi(), Machine: self.getMachine(), Bios: self.getBios(), BootOrder: self.BootOrder, SrcIpCheck: self.SrcIpCheck.Bool(), SrcMacCheck: self.SrcMacCheck.Bool(), HostId: host.Id, EncryptKeyId: self.EncryptKeyId, IsDaemon: self.IsDaemon.Bool(), LightMode: self.RescueMode, } if len(self.BackupHostId) > 0 { if self.HostId == host.Id { isMaster := true desc.IsMaster = &isMaster } else if self.BackupHostId == host.Id { isSlave := true desc.IsSlave = &isSlave } } if self.HostId != host.Id { desc.IsVolatileHost = true } // isolated devices isolatedDevs, _ := self.GetIsolatedDevices() for _, dev := range isolatedDevs { desc.IsolatedDevices = append(desc.IsolatedDevices, dev.getDesc()) } // nics, domain desc.Domain = options.Options.DNSDomain nics, _ := self.GetNetworks("") for _, nic := range nics { nicDesc := nic.getJsonDescAtHost(ctx, host) desc.Nics = append(desc.Nics, nicDesc) if len(nicDesc.Domain) > 0 { desc.Domain = nicDesc.Domain } } { var prevNicDesc *api.GuestnetworkJsonDesc if len(desc.Nics) > 0 { prevNicDesc = desc.Nics[len(desc.Nics)-1] } // append tap nic tapNicDesc := self.getTapNicJsonDesc(ctx, prevNicDesc) if tapNicDesc != nil { desc.Nics = append(desc.Nics, tapNicDesc) } } // disks disks, _ := self.GetGuestDisks() for _, disk := range disks { diskDesc := disk.GetJsonDescAtHost(ctx, host) desc.Disks = append(desc.Disks, diskDesc) } cdroms, _ := self.getCdroms() for _, cdrom := range cdroms { cdromDesc := cdrom.getJsonDesc() desc.Cdroms = append(desc.Cdroms, cdromDesc) } if len(desc.Cdroms) > 0 { desc.Cdrom = desc.Cdroms[0] } //floppy floppys, _ := self.getFloppys() for _, floppy := range floppys { floppyDesc := floppy.getJsonDesc() desc.Floppys = append(desc.Floppys, floppyDesc) } // tenant tc, _ := self.GetTenantCache(ctx) if tc != nil { desc.Tenant = tc.GetName() desc.DomainId = tc.DomainId desc.ProjectDomain = tc.Domain } desc.TenantId = self.ProjectId keypair := self.getKeypair() if keypair != nil { desc.Keypair = keypair.Name desc.Pubkey = keypair.PublicKey } desc.NetworkRoles = self.getNetworkRoles() desc.Secgroups, _ = self.getSecgroupJson() desc.SecurityRules = self.getSecurityGroupsRules() desc.AdminSecurityRules = self.getAdminSecurityRules() desc.ExtraOptions = self.getExtraOptions(ctx) desc.Kvm = self.getKvmOptions() zone, _ := self.getZone() if zone != nil { desc.ZoneId = zone.Id desc.Zone = zone.Name } desc.OsName = self.GetOS() desc.Metadata, _ = self.GetAllMetadata(ctx, nil) userData, _ := desc.Metadata["user_data"] if len(userData) > 0 { decodeData, _ := userdata.Decode(userData) if len(decodeData) > 0 { userData = decodeData } desc.UserData = userData } desc.PendingDeleted = self.PendingDeleted // add scaling group sggs, err := ScalingGroupGuestManager.Fetch("", self.Id) if err == nil && len(sggs) > 0 { desc.ScalingGroupId = sggs[0].ScalingGroupId } return desc } func (self *SGuest) GetJsonDescAtBaremetal(ctx context.Context, host *SHost) *api.GuestJsonDesc { desc := &api.GuestJsonDesc{ Name: self.Name, Description: self.Description, UUID: self.Id, Mem: self.VmemSize, Cpu: self.VcpuCount, } desc.DiskConfig = host.getDiskConfig() netifs := host.GetAllNetInterfaces() desc.Domain = options.Options.DNSDomain for _, nic := range netifs { nicDesc := nic.getServerJsonDesc() if len(nicDesc.Ip) > 0 { desc.Nics = append(desc.Nics, nicDesc) if len(nicDesc.Domain) > 0 { desc.Domain = nicDesc.Domain } } else { desc.NicsStandby = append(desc.NicsStandby, nicDesc) } } disks, _ := self.GetGuestDisks() for _, disk := range disks { diskDesc := disk.GetJsonDescAtHost(ctx, host) desc.Disks = append(desc.Disks, diskDesc) } tc, _ := self.GetTenantCache(ctx) if tc != nil { desc.Tenant = tc.GetName() desc.DomainId = tc.DomainId desc.ProjectDomain = tc.Domain } desc.TenantId = self.ProjectId keypair := self.getKeypair() if keypair != nil { desc.Keypair = keypair.Name desc.Pubkey = keypair.PublicKey } desc.NetworkRoles = self.getNetworkRoles() desc.SecurityRules = self.getSecurityGroupsRules() desc.AdminSecurityRules = self.getAdminSecurityRules() zone, _ := self.getZone() if zone != nil { desc.ZoneId = zone.Id desc.Zone = zone.Name } desc.OsName = self.GetOS() desc.Metadata, _ = self.GetAllMetadata(ctx, nil) desc.UserData, _ = desc.Metadata["user_data"] desc.PendingDeleted = self.PendingDeleted return desc } func (self *SGuest) getNetworkRoles() []string { key := db.Metadata.GetSysadminKey("network_role") roleStr := self.GetMetadata(context.Background(), key, auth.AdminCredential()) if len(roleStr) > 0 { return strings.Split(roleStr, ",") } return nil } func (manager *SGuestManager) FetchGuestById(guestId string) *SGuest { guest, err := manager.FetchById(guestId) if err != nil { log.Errorf("FetchById fail %s", err) return nil } return guest.(*SGuest) } func (manager *SGuestManager) GetSpecShouldCheckStatus(query *jsonutils.JSONDict) (bool, error) { return true, nil } func (self *SGuest) GetSpec(checkStatus bool) *jsonutils.JSONDict { if checkStatus { if utils.IsInStringArray(self.Status, []string{api.VM_SCHEDULE_FAILED}) { return nil } } spec := jsonutils.NewDict() spec.Set("cpu", jsonutils.NewInt(int64(self.VcpuCount))) spec.Set("mem", jsonutils.NewInt(int64(self.VmemSize))) // get disk spec guestdisks, _ := self.GetGuestDisks() diskSpecs := jsonutils.NewArray() for _, guestdisk := range guestdisks { info := guestdisk.ToDiskInfo() diskSpec := jsonutils.NewDict() diskSpec.Set("size", jsonutils.NewInt(info.Size)) diskSpec.Set("backend", jsonutils.NewString(info.Backend)) diskSpec.Set("medium_type", jsonutils.NewString(info.MediumType)) diskSpec.Set("disk_type", jsonutils.NewString(info.DiskType)) diskSpecs.Add(diskSpec) } spec.Set("disk", diskSpecs) // get nic spec guestnics, _ := self.GetNetworks("") nicSpecs := jsonutils.NewArray() for _, guestnic := range guestnics { nicSpec := jsonutils.NewDict() nicSpec.Set("bandwidth", jsonutils.NewInt(int64(guestnic.getBandwidth()))) t := "int" if guestnic.IsExit() { t = "ext" } nicSpec.Set("type", jsonutils.NewString(t)) nicSpecs.Add(nicSpec) } spec.Set("nic", nicSpecs) // get isolate device spec guestgpus, _ := self.GetIsolatedDevices() gpuSpecs := []GpuSpec{} for _, guestgpu := range guestgpus { if strings.HasPrefix(guestgpu.DevType, "GPU") { gs := guestgpu.GetGpuSpec() gpuSpecs = append(gpuSpecs, *gs) } } spec.Set("gpu", jsonutils.Marshal(gpuSpecs)) return spec } func (manager *SGuestManager) GetSpecIdent(spec *jsonutils.JSONDict) []string { cpuCount, _ := spec.Int("cpu") memSize, _ := spec.Int("mem") memSizeMB, _ := utils.GetSizeMB(fmt.Sprintf("%d", memSize), "M") specKeys := []string{ fmt.Sprintf("cpu:%d", cpuCount), fmt.Sprintf("mem:%dM", memSizeMB), } countKey := func(kf func(*jsonutils.JSONDict) string, dataArray jsonutils.JSONObject) map[string]int64 { countMap := make(map[string]int64) datas, _ := dataArray.GetArray() for _, data := range datas { key := kf(data.(*jsonutils.JSONDict)) if count, ok := countMap[key]; !ok { countMap[key] = 1 } else { count++ countMap[key] = count } } return countMap } kfuncs := map[string]func(*jsonutils.JSONDict) string{ "disk": func(data *jsonutils.JSONDict) string { backend, _ := data.GetString("backend") mediumType, _ := data.GetString("medium_type") size, _ := data.Int("size") sizeGB, _ := utils.GetSizeGB(fmt.Sprintf("%d", size), "M") return fmt.Sprintf("disk:%s_%s_%dG", backend, mediumType, sizeGB) }, "nic": func(data *jsonutils.JSONDict) string { typ, _ := data.GetString("type") bw, _ := data.Int("bandwidth") return fmt.Sprintf("nic:%s_%dM", typ, bw) }, "gpu": func(data *jsonutils.JSONDict) string { vendor, _ := data.GetString("vendor") model, _ := data.GetString("model") return fmt.Sprintf("gpu:%s_%s", vendor, model) }, } for sKey, kf := range kfuncs { sArrary, err := spec.Get(sKey) if err != nil { log.Errorf("Get key %s array error: %v", sKey, err) continue } for key, count := range countKey(kf, sArrary) { specKeys = append(specKeys, fmt.Sprintf("%sx%d", key, count)) } } return specKeys } func (self *SGuest) GetGpuSpec() *GpuSpec { if len(self.InstanceType) == 0 { return nil } host, err := self.GetHost() if err != nil { return nil } zone, err := host.GetZone() if err != nil { return nil } q := ServerSkuManager.Query().Equals("name", self.InstanceType).Equals("cloudregion_id", zone.CloudregionId).IsNotEmpty("gpu_spec") sku := &SServerSku{} err = q.First(sku) if err != nil { return nil } return &GpuSpec{ Model: sku.GpuSpec, Amount: sku.GpuCount, } } func (self *SGuest) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { desc := self.SVirtualResourceBase.GetShortDesc(ctx) desc.Set("mem", jsonutils.NewInt(int64(self.VmemSize))) desc.Set("cpu", jsonutils.NewInt(int64(self.VcpuCount))) desc.Set("status", jsonutils.NewString(self.Status)) desc.Set("shutdown_mode", jsonutils.NewString(self.ShutdownMode)) if len(self.InstanceType) > 0 { desc.Set("instance_type", jsonutils.NewString(self.InstanceType)) } if gp := self.GetGpuSpec(); gp != nil { desc.Set("gpu_model", jsonutils.NewString(gp.Model)) desc.Set("gpu_count", jsonutils.NewString(gp.Amount)) } address := jsonutils.NewString(strings.Join(self.GetRealIPs(), ",")) desc.Set("ip_addr", address) if len(self.OsType) > 0 { desc.Add(jsonutils.NewString(self.OsType), "os_type") } if osDist := self.GetMetadata(ctx, "os_distribution", nil); len(osDist) > 0 { desc.Add(jsonutils.NewString(osDist), "os_distribution") } if osVer := self.GetMetadata(ctx, "os_version", nil); len(osVer) > 0 { desc.Add(jsonutils.NewString(osVer), "os_version") } templateId := self.GetTemplateId() if len(templateId) > 0 { desc.Set("template_id", jsonutils.NewString(templateId)) } extBw := self.getBandwidth(true) intBw := self.getBandwidth(false) if extBw > 0 { desc.Set("ext_bandwidth", jsonutils.NewInt(int64(extBw))) } if intBw > 0 { desc.Set("int_bandwidth", jsonutils.NewInt(int64(intBw))) } if len(self.OsType) > 0 { desc.Add(jsonutils.NewString(self.OsType), "os_type") } if len(self.ExternalId) > 0 { desc.Add(jsonutils.NewString(self.ExternalId), "externalId") } desc.Set("hypervisor", jsonutils.NewString(self.GetHypervisor())) host, _ := self.GetHost() spec := self.GetSpec(false) if self.GetHypervisor() == api.HYPERVISOR_BAREMETAL { if host != nil { hostSpec := host.GetSpec(false) hostSpecIdent := HostManager.GetSpecIdent(hostSpec) spec.Set("host_spec", jsonutils.NewString(strings.Join(hostSpecIdent, "/"))) } } if spec != nil { desc.Update(spec) } var billingInfo SCloudBillingInfo if host != nil { desc.Set("host", jsonutils.NewString(host.Name)) desc.Set("host_id", jsonutils.NewString(host.Id)) billingInfo.SCloudProviderInfo = host.getCloudProviderInfo() } if len(self.BackupHostId) > 0 { backupHost := HostManager.FetchHostById(self.BackupHostId) if backupHost != nil { desc.Set("backup_host", jsonutils.NewString(backupHost.Name)) desc.Set("backup_host_id", jsonutils.NewString(backupHost.Id)) } } if priceKey := self.GetMetadata(ctx, "ext:price_key", nil); len(priceKey) > 0 { billingInfo.PriceKey = priceKey } billingInfo.SBillingBaseInfo = self.getBillingBaseInfo() desc.Update(jsonutils.Marshal(billingInfo)) return desc } func (self *SGuest) saveOsType(userCred mcclient.TokenCredential, osType string) error { diff, err := db.Update(self, func() error { self.OsType = osType return nil }) if err != nil { return err } db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred) return err } type sDeployInfo struct { Os string Account string Key string Distro string Version string Arch string Language string TelegrafDeployed bool } func (self *SGuest) SaveDeployInfo(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) { deployInfo := sDeployInfo{} data.Unmarshal(&deployInfo) info := make(map[string]interface{}) if len(deployInfo.Os) > 0 { self.saveOsType(userCred, deployInfo.Os) info["os_name"] = deployInfo.Os } driver := self.GetDriver() if len(deployInfo.Account) > 0 { info["login_account"] = deployInfo.Account if len(deployInfo.Key) > 0 { info["login_key"] = deployInfo.Key if len(self.KeypairId) > 0 && !driver.IsSupportdDcryptPasswordFromSecretKey() { // Tencent Cloud does not support simultaneous setting of secret keys and passwords info["login_key"], _ = seclib2.EncryptBase64(self.GetKeypairPublicKey(), "") } info["login_key_timestamp"] = timeutils.UtcNow() } else { info["login_key"] = "none" info["login_key_timestamp"] = "none" } } if len(deployInfo.Distro) > 0 { info["os_distribution"] = deployInfo.Distro } if len(deployInfo.Version) > 0 { info["os_version"] = deployInfo.Version } if len(deployInfo.Arch) > 0 { info["os_arch"] = deployInfo.Arch } if len(deployInfo.Language) > 0 { info["os_language"] = deployInfo.Language } if deployInfo.TelegrafDeployed { info["telegraf_deployed"] = true } self.SetAllMetadata(ctx, info, userCred) self.saveOldPassword(ctx, userCred) } func (self *SGuest) isAllDisksReady() bool { ready := true disks, _ := self.GetGuestDisks() if disks == nil || len(disks) == 0 { return true } for i := 0; i < len(disks); i += 1 { disk := disks[i].GetDisk() if !(disk.isReady() || disk.Status == api.DISK_START_MIGRATE) { ready = false break } } return ready } func (self *SGuest) GetKeypairPublicKey() string { keypair := self.getKeypair() if keypair != nil { return keypair.PublicKey } return "" } func (manager *SGuestManager) GetIpsInProjectWithName(projectId, name string, isExitOnly bool, addrType api.TAddressType) []string { name = strings.TrimSuffix(name, ".") ipField := "ip_addr" gwField := "guest_gateway" if addrType == api.AddressTypeIPv6 { ipField = "ip6_addr" gwField = "guest_gateway6" } guestnics := GuestnetworkManager.Query().IsNotEmpty(ipField).SubQuery() guestsQ := manager.Query().IsFalse("pending_deleted").Equals("hostname", name) if len(projectId) > 0 { guestsQ = guestsQ.Equals("tenant_id", projectId) } guests := guestsQ.SubQuery() networks := NetworkManager.Query().IsNotNull(gwField).SubQuery() q := guestnics.Query(guestnics.Field(ipField)) q = q.Join(guests, sqlchemy.Equals(guests.Field("id"), guestnics.Field("guest_id"))) q = q.Join(networks, sqlchemy.Equals(networks.Field("id"), guestnics.Field("network_id"))) ips := make([]string, 0) rows, err := q.Rows() if err != nil { log.Errorf("Get guest ip with name query err: %v", err) return ips } defer rows.Close() for rows.Next() { var ip string err = rows.Scan(&ip) if err != nil { log.Errorf("Get guest ip with name scan err: %v", err) return ips } ips = append(ips, ip) } if addrType == api.AddressTypeIPv6 { return ips } return manager.getIpsByExit(ips, isExitOnly) } func (manager *SGuestManager) getIpsByExit(ips []string, isExitOnly bool) []string { intRet := make([]string, 0) extRet := make([]string, 0) for _, ip := range ips { addr, _ := netutils.NewIPV4Addr(ip) if netutils.IsExitAddress(addr) { extRet = append(extRet, ip) continue } intRet = append(intRet, ip) } if isExitOnly { return extRet } else if len(intRet) > 0 { return intRet } return extRet } func (manager *SGuestManager) getExpiredPendingDeleteGuests() []SGuest { deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds*-1) * time.Second) q := manager.Query() q = q.IsTrue("pending_deleted").LT("pending_deleted_at", deadline).Limit(options.Options.PendingDeleteMaxCleanBatchSize) guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { log.Errorf("fetch guests error %s", err) return nil } return guests } func (manager *SGuestManager) CleanPendingDeleteServers(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) { guests := manager.getExpiredPendingDeleteGuests() if guests == nil { return } for i := 0; i < len(guests); i += 1 { opts := api.ServerDeleteInput{ OverridePendingDelete: true, DeleteSnapshots: options.Options.DeleteSnapshotExpiredRelease, DeleteEip: options.Options.DeleteEipExpiredRelease, DeleteDisks: options.Options.DeleteDisksExpiredRelease, } // 跳过单独在云上开机过的虚拟机,避免误清理 if len(guests[i].GetExternalId()) > 0 { iVm, err := guests[i].GetIVM(ctx) if err == nil && iVm.GetStatus() == api.VM_RUNNING { if guests[i].Status != api.VM_DELETE_FAIL { guests[i].SetStatus(ctx, userCred, api.VM_DELETE_FAIL, "vm status is running") } continue } } guests[i].StartDeleteGuestTask(ctx, userCred, "", opts) } } func (manager *SGuestManager) getExpiredPrepaidGuests() []SGuest { deadline := time.Now().Add(time.Duration(options.Options.PrepaidExpireCheckSeconds*-1) * time.Second) q := manager.Query() q = q.Equals("billing_type", billing_api.BILLING_TYPE_PREPAID).LT("expired_at", deadline). IsFalse("pending_deleted").Limit(options.Options.ExpiredPrepaidMaxCleanBatchSize) guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { log.Errorf("fetch guests error %s", err) return nil } return guests } func (manager *SGuestManager) getNeedRenewPrepaidGuests() ([]SGuest, error) { deadline := time.Now().Add(time.Duration(options.Options.PrepaidAutoRenewHours)*time.Hour + 20*time.Minute) q := manager.Query() q = q.Equals("billing_type", billing_api.BILLING_TYPE_PREPAID).LT("expired_at", deadline). IsFalse("pending_deleted").In("hypervisor", GetNotSupportAutoRenewHypervisors()).IsTrue("auto_renew") guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { return nil, errors.Wrap(err, "db.FetchModelObjects") } return guests, nil } func (manager *SGuestManager) getExpiredPostpaidGuests() []SGuest { q := ListExpiredPostpaidResources(manager.Query(), options.Options.ExpiredPrepaidMaxCleanBatchSize) q = q.IsFalse("pending_deleted") guests := make([]SGuest, 0) err := db.FetchModelObjects(GuestManager, q, &guests) if err != nil { log.Errorf("fetch guests error %s", err) return nil } return guests } func (self *SGuest) doExternalSync(ctx context.Context, userCred mcclient.TokenCredential) error { host, _ := self.GetHost() if host == nil { return fmt.Errorf("no host???") } ihost, iprovider, err := host.GetIHostAndProvider(ctx) if err != nil { return err } iVM, err := ihost.GetIVMById(self.ExternalId) if err != nil { return err } return self.syncWithCloudVM(ctx, userCred, iprovider, host, iVM, nil, true) } func (manager *SGuestManager) DeleteExpiredPrepaidServers(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) { guests := manager.getExpiredPrepaidGuests() if guests == nil { return } for i := 0; i < len(guests); i += 1 { // fake delete expired prepaid servers if len(guests[i].ExternalId) > 0 { err := guests[i].doExternalSync(ctx, userCred) if err == nil && guests[i].IsValidPrePaid() { continue } } guests[i].SetDisableDelete(userCred, false) opts := api.ServerDeleteInput{ DeleteSnapshots: options.Options.DeleteSnapshotExpiredRelease, DeleteEip: options.Options.DeleteEipExpiredRelease, DeleteDisks: options.Options.DeleteDisksExpiredRelease, } guests[i].StartDeleteGuestTask(ctx, userCred, "", opts) } } func (manager *SGuestManager) AutoRenewPrepaidServer(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) { guests, err := manager.getNeedRenewPrepaidGuests() if err != nil { log.Errorf("failed to get need renew prepaid guests error: %v", err) return } for i := 0; i < len(guests); i += 1 { if len(guests[i].ExternalId) > 0 && !guests[i].GetDriver().IsSupportSetAutoRenew() { err := guests[i].doExternalSync(ctx, userCred) if err == nil && guests[i].IsValidPrePaid() { continue } } guests[i].startGuestRenewTask(ctx, userCred, guests[i].BillingCycle, "") } } func (manager *SGuestManager) DeleteExpiredPostpaidServers(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) { guests := manager.getExpiredPostpaidGuests() if len(guests) == 0 { log.Infof("No expired postpaid guest") return } for i := 0; i < len(guests); i++ { if len(guests[i].ExternalId) > 0 { err := guests[i].doExternalSync(ctx, userCred) if err == nil && guests[i].IsValidPostPaid() { continue } } guests[i].SetDisableDelete(userCred, false) opts := api.ServerDeleteInput{ DeleteSnapshots: options.Options.DeleteSnapshotExpiredRelease, DeleteEip: options.Options.DeleteEipExpiredRelease, DeleteDisks: options.Options.DeleteDisksExpiredRelease, } guests[i].StartDeleteGuestTask(ctx, userCred, "", opts) } } func (self *SGuest) IsEipAssociable() error { if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) { return errors.Wrapf(httperrors.ErrInvalidStatus, "cannot associate eip in status %s", self.Status) } err := ValidateAssociateEip(self) if err != nil { return errors.Wrap(err, "ValidateAssociateEip") } var eip *SElasticip switch self.Hypervisor { case api.HYPERVISOR_AWS: eip, err = self.GetElasticIp() default: eip, err = self.GetEipOrPublicIp() } if err != nil { log.Errorf("Fail to get Eip %s", err) return errors.Wrap(err, "IsEipAssociable") } if eip != nil { return httperrors.NewInvalidStatusError("already associate with eip") } return nil } func (self *SGuest) GetEipOrPublicIp() (*SElasticip, error) { return ElasticipManager.getEip(api.EIP_ASSOCIATE_TYPE_SERVER, self.Id, "") } func (self *SGuest) GetElasticIp() (*SElasticip, error) { return ElasticipManager.getEip(api.EIP_ASSOCIATE_TYPE_SERVER, self.Id, api.EIP_MODE_STANDALONE_EIP) } func (self *SGuest) GetPublicIp() (*SElasticip, error) { return ElasticipManager.getEip(api.EIP_ASSOCIATE_TYPE_SERVER, self.Id, api.EIP_MODE_INSTANCE_PUBLICIP) } func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, extEip cloudprovider.ICloudEIP, syncOwnerId mcclient.IIdentityProvider) compare.SyncResult { result := compare.SyncResult{} eip, err := self.GetEipOrPublicIp() if err != nil { result.Error(fmt.Errorf("GetEipOrPublicIp error %s", err)) return result } region, err := self.getRegion() if err != nil { result.Error(fmt.Errorf("getRegion error %s", err)) return result } if eip == nil && extEip == nil { // do nothing return result } if eip == nil && extEip != nil { // add neip, err := ElasticipManager.getEipByExtEip(ctx, userCred, extEip, provider, region, syncOwnerId) if err != nil { result.AddError(errors.Wrapf(err, "getEipByExtEip")) return result } err = neip.AssociateInstance(ctx, userCred, api.EIP_ASSOCIATE_TYPE_SERVER, self) if err != nil { result.AddError(errors.Wrapf(err, "neip.AssociateInstance")) return result } result.Add() return result } if eip != nil && extEip == nil { // remove err = eip.Dissociate(ctx, userCred) if err != nil { result.DeleteError(err) return result } result.Delete() return result } // sync if eip.IpAddr != extEip.GetIpAddr() { // remove then add err = eip.Dissociate(ctx, userCred) if err != nil { // fail to remove result.DeleteError(err) return result } result.Delete() neip, err := ElasticipManager.getEipByExtEip(ctx, userCred, extEip, provider, region, syncOwnerId) if err != nil { result.AddError(err) return result } err = neip.AssociateInstance(ctx, userCred, api.EIP_ASSOCIATE_TYPE_SERVER, self) if err != nil { result.AddError(err) } else { result.Add() } return result } // do nothing err = eip.SyncWithCloudEip(ctx, userCred, provider, extEip, syncOwnerId) if err != nil { result.UpdateError(err) } else { result.Update() } return result } func (self *SGuest) getSecgroupsBySecgroupExternalIds(externalIds []string) ([]SSecurityGroup, error) { vpc, err := self.GetVpc() if err != nil { return nil, errors.Wrapf(err, "GetVpc") } region, err := vpc.GetRegion() if err != nil { return nil, errors.Wrapf(err, "GetRegion") } filter, err := region.GetDriver().GetSecurityGroupFilter(vpc) if err != nil { return nil, errors.Wrapf(err, "GetSecurityGroupFilter") } q := SecurityGroupManager.Query().In("external_id", externalIds) q = filter(q) secgroups := []SSecurityGroup{} err = db.FetchModelObjects(SecurityGroupManager, q, &secgroups) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return secgroups, nil } func (self *SGuest) SyncVMSecgroups(ctx context.Context, userCred mcclient.TokenCredential, externalIds []string) error { // clear secgroup if vm not support security group if self.GetDriver().GetMaxSecurityGroupCount() == 0 || len(externalIds) == 0 { _, err := db.Update(self, func() error { self.SecgrpId = "" self.AdminSecgrpId = "" return nil }) return err } secgroups, err := self.getSecgroupsBySecgroupExternalIds(externalIds) if err != nil { return errors.Wrap(err, "getSecgroupsBySecgroupExternalIds") } secgroupIds := []string{} for _, secgroup := range secgroups { secgroupIds = append(secgroupIds, secgroup.Id) } return self.SaveSecgroups(ctx, userCred, secgroupIds) } func (self *SGuest) GetIVM(ctx context.Context) (cloudprovider.ICloudVM, error) { if len(self.ExternalId) == 0 { return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty externalId") } host, err := self.GetHost() if err != nil { return nil, errors.Wrapf(err, "GetHost") } iregion, err := host.GetIRegion(ctx) if err != nil { return nil, errors.Wrapf(err, "GetIRegion") } ihost, err := iregion.GetIHostById(host.ExternalId) if err != nil { return nil, errors.Wrapf(err, "GetIHost") } ivm, err := ihost.GetIVMById(self.ExternalId) if err != nil { if errors.Cause(err) != cloudprovider.ErrNotFound { return nil, errors.Wrapf(err, "GetIVMById(%s)", self.ExternalId) } return iregion.GetIVMById(self.ExternalId) } return ivm, nil } func (self *SGuest) PendingDetachScalingGroup() error { sggs, err := ScalingGroupGuestManager.Fetch("", self.GetId()) if err != nil { return err } for i := range sggs { sggs[i].SetGuestStatus(api.SG_GUEST_STATUS_PENDING_REMOVE) } return nil } func (self *SGuest) DeleteEip(ctx context.Context, userCred mcclient.TokenCredential) error { eip, err := self.GetEipOrPublicIp() if err != nil { log.Errorf("Delete eip fail for get Eip %s", err) return err } if eip == nil { return nil } if eip.Mode == api.EIP_MODE_INSTANCE_PUBLICIP { err = eip.RealDelete(ctx, userCred) if err != nil { log.Errorf("Delete eip on delete server fail %s", err) return err } } else { err = eip.Dissociate(ctx, userCred) if err != nil { log.Errorf("Dissociate eip on delete server fail %s", err) return err } } return nil } func (self *SGuest) SetDisableDelete(userCred mcclient.TokenCredential, val bool) error { diff, err := db.Update(self, func() error { if val { self.DisableDelete = tristate.True } else { self.DisableDelete = tristate.False } return nil }) if err != nil { return err } db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred) logclient.AddSimpleActionLog(self, logclient.ACT_UPDATE, diff, userCred, true) return err } func (self *SGuest) getDefaultStorageType() string { diskCat := self.CategorizeDisks() if diskCat.Root != nil { rootStorage, _ := diskCat.Root.GetStorage() if rootStorage != nil { return rootStorage.StorageType } } return api.STORAGE_LOCAL } func (self *SGuest) GetApptags() []string { tagsStr := self.GetMetadata(context.Background(), api.VM_METADATA_APP_TAGS, nil) if len(tagsStr) > 0 { return strings.Split(tagsStr, ",") } return nil } func (self *SGuest) ToSchedDesc() *schedapi.ScheduleInput { desc := new(schedapi.ScheduleInput) config := &schedapi.ServerConfig{ Name: self.Name, Memory: self.VmemSize, Ncpu: int(self.VcpuCount), ServerConfigs: new(api.ServerConfigs), } desc.Id = self.Id self.FillGroupSchedDesc(config.ServerConfigs) self.FillDiskSchedDesc(config.ServerConfigs) self.FillNetSchedDesc(config.ServerConfigs) if len(self.HostId) > 0 && regutils.MatchUUID(self.HostId) { desc.HostId = self.HostId } config.Project = self.ProjectId config.Domain = self.DomainId /*tags := self.GetApptags() for i := 0; i < len(tags); i++ { desc.Set(tags[i], jsonutils.JSONTrue) }*/ config.Hypervisor = self.GetHypervisor() desc.ServerConfig = *config desc.OsArch = self.OsArch return desc } func (self *SGuest) FillGroupSchedDesc(desc *api.ServerConfigs) { groups := make([]SGroupguest, 0) err := GroupguestManager.Query().Equals("guest_id", self.Id).All(&groups) if err != nil { log.Errorln(err) return } groupids := make([]string, len(groups)) for i := range groups { groupids[i] = groups[i].GroupId } desc.InstanceGroupIds = groupids } func (self *SGuest) FillDiskSchedDesc(desc *api.ServerConfigs) { guestDisks := make([]SGuestdisk, 0) err := GuestdiskManager.Query().Equals("guest_id", self.Id).All(&guestDisks) if err != nil { log.Errorf("FillDiskSchedDesc: %v", err) return } for i := 0; i < len(guestDisks); i++ { diskConf := guestDisks[i].ToDiskConfig() // HACK: storage used by self, so earse it if !utils.IsInStringArray(diskConf.Backend, api.SHARED_STORAGE) { diskConf.Storage = "" } desc.Disks = append(desc.Disks, diskConf) } } func (self *SGuest) FillNetSchedDesc(desc *api.ServerConfigs) { guestNetworks := make([]SGuestnetwork, 0) err := GuestnetworkManager.Query().Equals("guest_id", self.Id).All(&guestNetworks) if err != nil { log.Errorf("FillNetSchedDesc: %v", err) return } if desc.Networks == nil { desc.Networks = make([]*api.NetworkConfig, 0) } for i := 0; i < len(guestNetworks); i++ { desc.Networks = append(desc.Networks, guestNetworks[i].ToNetworkConfig()) } } func (self *SGuest) GuestDisksHasSnapshot() (bool, error) { guestDisks, err := self.GetGuestDisks() if err != nil { return false, errors.Wrapf(err, "GetGuestDisks") } for i := 0; i < len(guestDisks); i++ { cnt, err := SnapshotManager.GetDiskSnapshotCount(guestDisks[i].DiskId) if err != nil { return false, err } if cnt > 0 { return true, nil } } return false, nil } func (self *SGuest) OnScheduleToHost(ctx context.Context, userCred mcclient.TokenCredential, hostId string) error { err := self.SetHostId(userCred, hostId) if err != nil { return err } notes := jsonutils.NewDict() notes.Add(jsonutils.NewString(hostId), "host_id") db.OpsLog.LogEvent(self, db.ACT_SCHEDULE, notes, userCred) host, _ := self.GetHost() return host.ClearSchedDescCache() } func (guest *SGuest) GetDetailsTasks(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { since := time.Time{} if query.Contains("since") { since, _ = query.GetTime("since") } var isOpen *bool = nil if query.Contains("is_open") { isOpenVal, _ := query.Bool("is_open") isOpen = &isOpenVal } q := taskman.TaskManager.QueryTasksOfObject(guest, since, isOpen) objs, err := db.Query2List(taskman.TaskManager, ctx, userCred, q, query, false) if err != nil { return nil, err } ret := jsonutils.NewDict() ret.Add(jsonutils.NewArray(objs...), "tasks") return ret, nil } func (guest *SGuest) GetDynamicConditionInput() *jsonutils.JSONDict { return guest.ToSchedDesc().ToConditionInput() } func (self *SGuest) ToCreateInput(ctx context.Context, userCred mcclient.TokenCredential) *api.ServerCreateInput { genInput := self.toCreateInput() userInput, err := self.GetCreateParams(ctx, userCred) if err != nil { return genInput } if self.GetHypervisor() != api.HYPERVISOR_BAREMETAL { // fill missing create params like schedtags disks := []*api.DiskConfig{} for idx, disk := range genInput.Disks { tmpD := disk if idx < len(userInput.Disks) { inputDisk := userInput.Disks[idx] tmpD.Schedtags = inputDisk.Schedtags tmpD.Storage = inputDisk.Storage } disks = append(disks, tmpD) } userInput.Disks = disks } nets := []*api.NetworkConfig{} for idx, net := range genInput.Networks { tmpN := net if idx < len(userInput.Networks) { inputNet := userInput.Networks[idx] tmpN.Schedtags = inputNet.Schedtags tmpN.Network = inputNet.Network } nets = append(nets, tmpN) } userInput.Networks = nets userInput.IsolatedDevices = genInput.IsolatedDevices userInput.Count = 1 // override some old userInput properties via genInput because of change config behavior userInput.VmemSize = genInput.VmemSize userInput.VcpuCount = genInput.VcpuCount userInput.Vga = genInput.Vga userInput.Vdi = genInput.Vdi userInput.Bios = genInput.Bios userInput.Cdrom = genInput.Cdrom userInput.Description = genInput.Description userInput.BootOrder = genInput.BootOrder userInput.DisableDelete = genInput.DisableDelete userInput.ShutdownBehavior = genInput.ShutdownBehavior userInput.IsSystem = genInput.IsSystem userInput.SecgroupId = genInput.SecgroupId userInput.KeypairId = genInput.KeypairId userInput.EipBw = genInput.EipBw userInput.EipChargeType = genInput.EipChargeType provider := self.GetDriver() if provider.IsSupportPublicIp() { userInput.PublicIpBw = genInput.PublicIpBw userInput.PublicIpChargeType = genInput.PublicIpChargeType } userInput.AutoRenew = genInput.AutoRenew // cloned server should belongs to the project creating it userInput.ProjectId = userCred.GetProjectId() userInput.ProjectDomainId = userCred.GetProjectDomainId() userInput.Secgroups = []string{} secgroups, _ := self.GetSecgroups() for _, secgroup := range secgroups { userInput.Secgroups = append(userInput.Secgroups, secgroup.Id) } if genInput.ResourceType != "" { userInput.ResourceType = genInput.ResourceType } if genInput.InstanceType != "" { userInput.InstanceType = genInput.InstanceType } if genInput.PreferRegion != "" { userInput.PreferRegion = genInput.PreferRegion } if genInput.PreferZone != "" { userInput.PreferZone = genInput.PreferZone } // clean some of user input userInput.GenerateName = "" userInput.Description = "" userInput.Hostname = "" return userInput } func (self *SGuest) toCreateInput() *api.ServerCreateInput { r := new(api.ServerCreateInput) r.VmemSize = self.VmemSize r.VcpuCount = int(self.VcpuCount) if guestCdrom := self.getCdrom(false, 0); guestCdrom != nil { r.Cdrom = guestCdrom.ImageId } r.Vga = self.Vga r.Vdi = self.Vdi r.Bios = self.Bios r.Description = self.Description r.BootOrder = self.BootOrder r.DisableDelete = new(bool) *r.DisableDelete = self.DisableDelete.Bool() r.ShutdownBehavior = self.ShutdownBehavior // ignore r.DeployConfigs r.IsSystem = &self.IsSystem r.SecgroupId = self.SecgrpId r.ServerConfigs = new(api.ServerConfigs) r.Hypervisor = self.Hypervisor r.InstanceType = self.InstanceType r.ProjectId = self.ProjectId r.ProjectDomainId = self.DomainId r.Count = 1 r.Disks = self.ToDisksConfig() r.Networks = self.ToNetworksConfig() r.IsolatedDevices = self.ToIsolatedDevicesConfig() r.AutoRenew = self.AutoRenew if keypair := self.getKeypair(); keypair != nil { r.KeypairId = keypair.Id } if host, _ := self.GetHost(); host != nil { r.ResourceType = host.ResourceType } if eip, _ := self.GetEipOrPublicIp(); eip != nil { switch eip.Mode { case api.EIP_MODE_STANDALONE_EIP: r.EipBw = eip.Bandwidth r.EipChargeType = eip.ChargeType case api.EIP_MODE_INSTANCE_PUBLICIP: if driver := self.GetDriver(); driver.IsSupportPublicIp() { r.PublicIpBw = eip.Bandwidth r.PublicIpChargeType = eip.ChargeType } } } if zone, _ := self.getZone(); zone != nil { region, _ := zone.GetRegion() r.PreferRegion = region.GetId() r.PreferZone = zone.GetId() } return r } func (self *SGuest) ToDisksConfig() []*api.DiskConfig { guestDisks, err := self.GetGuestDisks() if err != nil { return nil } ret := make([]*api.DiskConfig, len(guestDisks)) for idx, guestDisk := range guestDisks { diskConf := new(api.DiskConfig) disk := guestDisk.GetDisk() diskConf.Index = int(guestDisk.Index) diskConf.ImageId = disk.GetTemplateId() diskConf.SnapshotId = disk.SnapshotId diskConf.DiskType = disk.DiskType diskConf.SizeMb = disk.DiskSize diskConf.Fs = disk.FsFormat diskConf.Format = disk.DiskFormat diskConf.Driver = guestDisk.Driver diskConf.Cache = guestDisk.CacheMode diskConf.Mountpoint = guestDisk.Mountpoint storage, _ := disk.GetStorage() diskConf.Backend = storage.StorageType diskConf.Medium = storage.MediumType ret[idx] = diskConf } return ret } func (self *SGuest) ToNetworksConfig() []*api.NetworkConfig { guestNetworks, _ := self.GetNetworks("") if len(guestNetworks) == 0 { return nil } ret := make([]*api.NetworkConfig, 0) teamMacs := []string{} for _, gn := range guestNetworks { if tg, _ := gn.GetTeamGuestnetwork(); tg != nil { teamMacs = append(teamMacs, gn.TeamWith) } } for _, guestNetwork := range guestNetworks { netConf := new(api.NetworkConfig) network, err := guestNetwork.GetNetwork() if err != nil { continue } requireTeaming := false if tg, _ := guestNetwork.GetTeamGuestnetwork(); tg != nil { requireTeaming = true } if utils.IsInStringArray(guestNetwork.MacAddr, teamMacs) { continue } // XXX: same wire netConf.Wire = network.WireId netConf.Network = network.Id netConf.Exit = guestNetwork.IsExit() if len(guestNetwork.Ip6Addr) > 0 { netConf.RequireIPv6 = true } // netConf.Private // netConf.Reserved netConf.Driver = guestNetwork.Driver netConf.BwLimit = guestNetwork.BwLimit netConf.RequireTeaming = requireTeaming // netConf.NetType ret = append(ret, netConf) } return ret } func (self *SGuest) ToIsolatedDevicesConfig() []*api.IsolatedDeviceConfig { guestIsolatedDevices, _ := self.GetIsolatedDevices() if len(guestIsolatedDevices) == 0 { return nil } ret := make([]*api.IsolatedDeviceConfig, len(guestIsolatedDevices)) for idx, guestIsolatedDevice := range guestIsolatedDevices { devConf := new(api.IsolatedDeviceConfig) devConf.Model = guestIsolatedDevice.Model devConf.Vendor = guestIsolatedDevice.getVendor() devConf.DevType = guestIsolatedDevice.DevType ret[idx] = devConf } return ret } func (self *SGuest) IsImport(ctx context.Context, userCred mcclient.TokenCredential) bool { return self.GetMetadata(ctx, "__is_import", userCred) == "true" } func (guest *SGuest) GetDetailsRemoteNics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { iVM, err := guest.GetIVM(ctx) if err != nil { return nil, httperrors.NewGeneralError(err) } iNics, err := iVM.GetINics() if err != nil { return nil, httperrors.NewGeneralError(err) } type SVNic struct { Index int Ip string Mac string Driver string } nics := make([]SVNic, len(iNics)) for i := range iNics { nics[i] = SVNic{ Index: i, Ip: iNics[i].GetIP(), Mac: iNics[i].GetMAC(), Driver: iNics[i].GetDriver(), } } // ret := jsonutils.NewDict() // ret.Set("vnics", jsonutils.Marshal(nics)) return jsonutils.Marshal(nics), nil } func (self *SGuest) GetInstanceSnapshots() ([]SInstanceSnapshot, error) { instanceSnapshots := make([]SInstanceSnapshot, 0) q := InstanceSnapshotManager.Query().Equals("guest_id", self.Id) err := db.FetchModelObjects(InstanceSnapshotManager, q, &instanceSnapshots) if err != nil { return nil, err } return instanceSnapshots, nil } func (self *SGuest) GetInstanceSnapshotCount() (int, error) { q := InstanceSnapshotManager.Query().Equals("guest_id", self.Id) return q.CountWithError() } func (self *SGuest) GetDiskSnapshotsNotInInstanceSnapshots() ([]SSnapshot, error) { guestDisks, err := self.GetGuestDisks() if err != nil { return nil, errors.Wrapf(err, "GetGuestDisks") } diskIds := make([]string, len(guestDisks)) for i := 0; i < len(guestDisks); i++ { diskIds[i] = guestDisks[i].DiskId } snapshots := make([]SSnapshot, 0) q := SnapshotManager.Query().IsFalse("fake_deleted").In("disk_id", diskIds) sq := InstanceSnapshotJointManager.Query("snapshot_id").SubQuery() q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("id"), sq.Field("snapshot_id"))). Filter(sqlchemy.IsNull(sq.Field("snapshot_id"))) err = db.FetchModelObjects(SnapshotManager, q, &snapshots) if err != nil { return nil, errors.Wrapf(err, "db.FetchModelObjects") } return snapshots, nil } func (self *SGuest) getGuestUsage(guestCount int) (SQuota, SRegionQuota, error) { usage := SQuota{} regionUsage := SRegionQuota{} usage.Count = guestCount usage.Cpu = int(self.VcpuCount) * guestCount usage.Memory = int(self.VmemSize * guestCount) diskSize := self.getDiskSize() if diskSize < 0 { return usage, regionUsage, httperrors.NewInternalServerError("fetch disk size failed") } usage.Storage = self.getDiskSize() * guestCount netCount, err := self.NetworkCount() if err != nil && errors.Cause(err) != sql.ErrNoRows { return usage, regionUsage, err } regionUsage.Port = netCount // regionUsage.Bw = self.getBandwidth(false) eip, err := self.GetEipOrPublicIp() if err != nil && errors.Cause(err) != sql.ErrNoRows { return usage, regionUsage, err } if eip != nil { regionUsage.Eip = 1 } return usage, regionUsage, nil } func (self *SGuestManager) checkGuestImage(ctx context.Context, input *api.ServerCreateInput) error { if len(input.GuestImageID) == 0 { return nil } guestImageId := input.GuestImageID params := jsonutils.NewDict() params.Add(jsonutils.JSONTrue, "details") s := auth.GetAdminSession(ctx, options.Options.Region) ret, err := image.GuestImages.Get(s, guestImageId, params) if err != nil { return errors.Wrap(err, "get guest image from glance error") } images := &api.SImagesInGuest{} err = ret.Unmarshal(images) if err != nil { return errors.Wrap(err, "unmarshal guest image") } input.GuestImageID = images.Id log.Infof("usage guest image %s(%s)", images.Name, images.Id) if len(input.Disks) > 0 { input.Disks[0].ImageId = images.RootImage.Id } else { input.Disks = append(input.Disks, &api.DiskConfig{ ImageId: images.RootImage.Id, }, ) } for i := range images.DataImages { if len(input.Disks) > i+1 { input.Disks[i+1].ImageId = images.DataImages[i].Id } else { input.Disks = append(input.Disks, &api.DiskConfig{ ImageId: images.DataImages[i].Id, }, ) } } return nil } func (self *SGuest) GetDiskIndex(diskId string) int8 { guestDisks, _ := self.GetGuestDisks() for _, gd := range guestDisks { if gd.DiskId == diskId { return gd.Index } } return -1 } func (guest *SGuest) GetRegionalQuotaKeys() (quotas.IQuotaKeys, error) { host, _ := guest.GetHost() if host == nil { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid host") } provider := host.GetCloudprovider() if provider == nil && len(host.ManagerId) > 0 { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid manager") } region, _ := host.GetRegion() if region == nil { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid region") } return fetchRegionalQuotaKeys(rbacscope.ScopeProject, guest.GetOwnerId(), region, provider), nil } func (guest *SGuest) GetQuotaKeys() (quotas.IQuotaKeys, error) { host, _ := guest.GetHost() if host == nil { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid host") } provider := host.GetCloudprovider() if provider == nil && len(host.ManagerId) > 0 { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid manager") } zone, _ := host.GetZone() if zone == nil { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid zone") } hypervisor := guest.Hypervisor if !utils.IsInStringArray(hypervisor, api.ONECLOUD_HYPERVISORS) { hypervisor = "" } return fetchComputeQuotaKeys( rbacscope.ScopeProject, guest.GetOwnerId(), zone, provider, hypervisor, ), nil } func (guest *SGuest) GetUsages() []db.IUsage { if guest.PendingDeleted || guest.Deleted { return nil } usage, regionUsage, err := guest.getGuestUsage(1) if err != nil { log.Errorf("guest.getGuestUsage fail %s", err) return nil } keys, err := guest.GetQuotaKeys() if err != nil { log.Errorf("guest.GetQuotaKeys fail %s", err) return nil } usage.SetKeys(keys) regionUsage.SetKeys(keys.(SComputeResourceKeys).SRegionalCloudResourceKeys) return []db.IUsage{ &usage, ®ionUsage, } } var ( // `^[a-zA-Z][a-zA-Z0-9._@-]*$`) serverNameREG = regexp.MustCompile(`^[a-zA-Z$][a-zA-Z0-9-${}.]*$`) hostnameREG = regexp.MustCompile(`^[a-z$][a-z0-9-${}.]*$`) ) func (manager *SGuestManager) ValidateNameLoginAccount(name string) error { if hostnameREG.MatchString(name) { return nil } return httperrors.NewInputParameterError("name starts with letter, and contains letter, number and - only") } func (guest *SGuest) StartRemoteUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, replaceTags bool, parentTaskId string) error { data := jsonutils.NewDict() if replaceTags { data.Add(jsonutils.JSONTrue, "replace_tags") } if task, err := taskman.TaskManager.NewTask(ctx, "GuestRemoteUpdateTask", guest, userCred, data, parentTaskId, "", nil); err != nil { log.Errorln(err) return errors.Wrap(err, "Start GuestRemoteUpdateTask") } else { guest.SetStatus(ctx, userCred, api.VM_UPDATE_TAGS, "StartRemoteUpdateTask") task.ScheduleRun(nil) } return nil } func (guest *SGuest) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) { if len(guest.ExternalId) == 0 || options.Options.KeepTagLocalization { return } host, err := guest.GetHost() if err != nil { return } if account := host.GetCloudaccount(); account != nil && account.ReadOnly { return } err = guest.StartRemoteUpdateTask(ctx, userCred, true, "") if err != nil { log.Errorf("StartRemoteUpdateTask fail: %s", err) } } func (self *SGuest) GetAddress() (string, error) { gns, err := self.GetNetworks("") if err != nil { return "", errors.Wrapf(err, "GetNetworks") } for _, gn := range gns { if !gn.IsExit() { return gn.IpAddr, nil } } return "", errors.Wrapf(cloudprovider.ErrNotFound, "guest %s address", self.Name) } func (guest *SGuest) InferPowerStates() { if len(guest.PowerStates) == 0 { switch guest.Status { case api.VM_READY: guest.PowerStates = api.VM_POWER_STATES_OFF case api.VM_UNKNOWN: guest.PowerStates = api.VM_POWER_STATES_UNKNOWN case api.VM_INIT: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_SCHEDULE: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_SCHEDULE_FAILED: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_CREATE_NETWORK: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_NETWORK_FAILED: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_DEVICE_FAILED: guest.PowerStates = api.VM_POWER_STATES_UNKNOWN case api.VM_CREATE_FAILED: guest.PowerStates = api.VM_POWER_STATES_OFF case api.VM_CREATE_DISK: guest.PowerStates = api.VM_POWER_STATES_OFF case api.VM_DISK_FAILED: guest.PowerStates = api.VM_POWER_STATES_OFF case api.VM_IMAGE_CACHING: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_START_DEPLOY: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_DEPLOYING: guest.PowerStates = api.VM_POWER_STATES_OFF case api.VM_START_START: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_STARTING: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_START_FAILED: guest.PowerStates = api.VM_POWER_STATES_OFF case api.VM_RUNNING: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_START_STOP: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_STOPPING: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_STOP_FAILED: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_RENEWING: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_RENEW_FAILED: guest.PowerStates = api.VM_POWER_STATES_ON case api.VM_ATTACH_DISK: guest.PowerStates = api.VM_POWER_STATES_UNKNOWN case api.VM_DETACH_DISK: guest.PowerStates = api.VM_POWER_STATES_UNKNOWN default: guest.PowerStates = api.VM_POWER_STATES_UNKNOWN } } } func (guest *SGuest) HasBackupGuest() bool { return guest.BackupHostId != "" } func (guest *SGuest) SetGuestBackupMirrorJobInProgress(ctx context.Context, userCred mcclient.TokenCredential) error { return guest.SetMetadata(ctx, api.MIRROR_JOB, api.MIRROR_JOB_INPROGRESS, userCred) } func (guest *SGuest) SetGuestBackupMirrorJobNotReady(ctx context.Context, userCred mcclient.TokenCredential) error { return guest.SetMetadata(ctx, api.MIRROR_JOB, "", userCred) } func (guest *SGuest) TrySetGuestBackupMirrorJobReady(ctx context.Context, userCred mcclient.TokenCredential) error { if guest.IsGuestBackupMirrorJobFailed(ctx, userCred) { // can't update guest backup mirror job status from failed to ready return nil } return guest.SetMetadata(ctx, api.MIRROR_JOB, api.MIRROR_JOB_READY, userCred) } func (guest *SGuest) SetGuestBackupMirrorJobFailed(ctx context.Context, userCred mcclient.TokenCredential) error { return guest.SetMetadata(ctx, api.MIRROR_JOB, api.MIRROR_JOB_FAILED, userCred) } func (guest *SGuest) IsGuestBackupMirrorJobFailed(ctx context.Context, userCred mcclient.TokenCredential) bool { return guest.GetMetadata(ctx, api.MIRROR_JOB, userCred) == api.MIRROR_JOB_FAILED } func (guest *SGuest) IsGuestBackupMirrorJobReady(ctx context.Context, userCred mcclient.TokenCredential) bool { return guest.GetMetadata(ctx, api.MIRROR_JOB, userCred) == api.MIRROR_JOB_READY } func (guest *SGuest) GetGuestBackupMirrorJobStatus(ctx context.Context, userCred mcclient.TokenCredential) string { return guest.GetMetadata(ctx, api.MIRROR_JOB, userCred) } func (guest *SGuest) ResetGuestQuorumChildIndex(ctx context.Context, userCred mcclient.TokenCredential) error { return guest.SetMetadata(ctx, api.QUORUM_CHILD_INDEX, "", userCred) } type SGuestTotalCount struct { apis.TotalCountBase CpuCount int MemMb int DiskMb int64 DiskCount int } func (manager *SGuestManager) CustomizedTotalCount(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, totalQ *sqlchemy.SQuery) (int, jsonutils.JSONObject, error) { results := SGuestTotalCount{} totalQ = totalQ.AppendField(sqlchemy.SUM("cpu_count", totalQ.Field("vcpu_count"))) totalQ = totalQ.AppendField(sqlchemy.SUM("mem_mb", totalQ.Field("vmem_size"))) err := totalQ.First(&results) if err != nil { return -1, nil, errors.Wrap(err, "SGuestManager query total") } log.Debugf("CustomizedTotalCount %s", jsonutils.Marshal(results)) diskQ := DiskManager.Query() diskGuestQ := GuestdiskManager.Query().SubQuery() diskQ = diskQ.Join(diskGuestQ, sqlchemy.Equals(diskQ.Field("id"), diskGuestQ.Field("disk_id"))) totalSQ := totalQ.ResetFields().SubQuery() diskQ = diskQ.Join(totalSQ, sqlchemy.Equals(diskGuestQ.Field("guest_id"), totalSQ.Field("id"))) diskQ = diskQ.AppendField(sqlchemy.COUNT("disk_count")) diskQ = diskQ.AppendField(sqlchemy.SUM("disk_mb", diskQ.Field("disk_size"))) err = diskQ.First(&results) if err != nil { return -1, nil, errors.Wrap(err, "SGuestManager query total_disk") } // log.Debugf("CustomizedTotalCount %s", jsonutils.Marshal(results)) return results.Count, jsonutils.Marshal(results), nil } func (guest *SGuest) IsSriov() bool { nics, err := guest.GetNetworks("") if err != nil { log.Errorf("guest.GetNetworks fail %s", err) return false } for i := range nics { if nics[i].Driver == api.NETWORK_DRIVER_VFIO { return true } } return false } func (guest *SGuest) getDisksCandidateHostIds() ([]string, error) { disks, err := guest.GetDisks() if err != nil { return nil, errors.Wrap(err, "guest.GetDisks") } ret := stringutils2.NewSortedStrings(nil) for i := range disks { candidates, err := disks[i].getCandidateHostIds() if err != nil { return nil, errors.Wrap(err, "getCandidateHostIds") } sorted := stringutils2.NewSortedStrings(candidates) if i > 0 { ret = stringutils2.Intersect(ret, sorted) } else { ret = sorted } } return ret, nil }