| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package models
- import (
- "context"
- "database/sql"
- "fmt"
- "math"
- "sort"
- "strconv"
- "strings"
- "sync"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/tristate"
- "yunion.io/x/pkg/util/compare"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/apis"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/cloudcommon/validators"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules/scheduler"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- "yunion.io/x/onecloud/pkg/util/yunionmeta"
- )
- // +onecloud:swagger-gen-model-singular=serversku
- // +onecloud:swagger-gen-model-plural=serverskus
- type SServerSkuManager struct {
- db.SEnabledStatusStandaloneResourceBaseManager
- SCloudregionResourceBaseManager
- SZoneResourceBaseManager
- }
- var ServerSkuManager *SServerSkuManager
- func init() {
- ServerSkuManager = &SServerSkuManager{
- SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
- SServerSku{},
- "serverskus_tbl",
- "serversku",
- "serverskus",
- ),
- }
- ServerSkuManager.NameRequireAscii = false
- ServerSkuManager.SetVirtualObject(ServerSkuManager)
- // CREATE INDEX sku_index ON serverskus_tbl (`deleted`, `is_emulated`, `provider`, `cloudregion_id`, `postpaid_status`, `prepaid_status`)
- ServerSkuManager.TableSpec().AddIndex(false, "deleted", "is_emulated", "provider", "cloudregion_id", "postpaid_status", "prepaid_status")
- }
- // SServerSku 实际对应的是instance type清单. 这里的Sku实际指的是instance type。
- type SServerSku struct {
- db.SEnabledStatusStandaloneResourceBase
- db.SExternalizedResourceBase
- SCloudregionResourceBase
- SZoneResourceBase
- InstanceTypeFamily string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"` // x2
- InstanceTypeCategory string `width:"32" charset:"utf8" nullable:"true" list:"user" create:"admin_optional" update:"admin"` // 通用型
- LocalCategory string `width:"32" charset:"utf8" nullable:"true" list:"user" create:"admin_optional" update:"admin" default:""` // 记录本地分类
- PrepaidStatus string `width:"32" charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_optional" default:"available"` // 预付费资源状态 available|soldout
- PostpaidStatus string `width:"32" charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_optional" default:"available"` // 按需付费资源状态 available|soldout
- CpuArch string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"` // CPU 架构 x86|xarm
- CpuCoreCount int `nullable:"false" list:"user" create:"admin_required"`
- MemorySizeMB int `nullable:"false" list:"user" create:"admin_required"`
- OsName string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin" default:"Any"` // Windows|Linux|Any
- SysDiskResizable tristate.TriState `default:"true" list:"user" create:"admin_optional" update:"admin"`
- SysDiskType string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- SysDiskMinSizeGB int `nullable:"true" list:"user" create:"admin_optional" update:"admin"` // not required。 windows比较新的版本都是50G左右。
- SysDiskMaxSizeGB int `nullable:"true" list:"user" create:"admin_optional" update:"admin"` // not required
- AttachedDiskType string `width:"32" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- AttachedDiskSizeGB int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- AttachedDiskCount int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- DataDiskTypes string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- DataDiskMaxCount int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- NicType string `width:"32" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- NicMaxCount int `default:"1" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- GpuAttachable tristate.TriState `default:"true" list:"user" create:"admin_optional" update:"admin"`
- GpuSpec string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- GpuCount string `width:"16" nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- GpuMaxCount int `nullable:"true" list:"user" create:"admin_optional" update:"admin"`
- Provider string `width:"64" charset:"ascii" nullable:"true" list:"user" default:"OneCloud" create:"admin_optional"`
- Md5 string `width:"32" charset:"utf8" nullable:"true"`
- }
- func (manager *SServerSkuManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
- regionId, _ := data.GetString("cloudregion_id")
- zoneId, _ := data.GetString("zone_id")
- return jsonutils.Marshal(map[string]string{"cloudregion_id": regionId, "zone_id": zoneId})
- }
- func (manager *SServerSkuManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
- regionId, _ := values.GetString("cloudregion_id")
- if len(regionId) > 0 {
- q = q.Equals("cloudregion_id", regionId)
- }
- zoneId, _ := values.GetString("zone_id")
- if len(zoneId) > 0 {
- q = q.Equals("zone_id", zoneId)
- }
- return q
- }
- func sliceToJsonObject(items []int) jsonutils.JSONObject {
- sort.Slice(items, func(i, j int) bool {
- if items[i] < items[j] {
- return true
- }
- return false
- })
- ret := jsonutils.NewArray()
- for _, item := range items {
- ret.Add(jsonutils.NewInt(int64(item)))
- }
- return ret
- }
- func genInstanceType(family string, cpu, memMb int64) (string, error) {
- if cpu <= 0 {
- return "", fmt.Errorf("cpu_core_count should great than zero")
- }
- if memMb <= 0 {
- return "", fmt.Errorf("memory_size_mb should great than zero")
- }
- if memMb%1024 != 0 && memMb != 512 {
- return "", fmt.Errorf("memory_size_mb should be 512 or integral multiple of 1024")
- }
- switch memMb {
- case 512:
- return fmt.Sprintf("ecs.%s.c%dm1.nano", family, cpu), nil
- default:
- return fmt.Sprintf("ecs.%s.c%dm%d", family, cpu, memMb/1024), nil
- }
- }
- func (self SServerSku) GetGlobalId() string {
- return self.ExternalId
- }
- func (manager *SServerSkuManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.ServerSkuDetails {
- rows := make([]api.ServerSkuDetails, len(objs))
- stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- regRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- instanceTypes := []string{}
- for i := range rows {
- rows[i] = api.ServerSkuDetails{
- EnabledStatusStandaloneResourceDetails: stdRows[i],
- ZoneResourceInfoBase: zoneRows[i].ZoneResourceInfoBase,
- CloudregionResourceInfo: regRows[i],
- }
- sku := objs[i].(*SServerSku)
- if !utils.IsInStringArray(sku.Name, instanceTypes) {
- instanceTypes = append(instanceTypes, sku.Name)
- }
- rows[i].CloudEnv = strings.Split(zoneRows[i].RegionExternalId, "/")[0]
- }
- ret := []struct {
- InstanceType string
- CloudregionId string
- ZoneId string
- }{}
- guestsQ := GuestManager.Query().SubQuery()
- hostsQ := HostManager.Query().SubQuery()
- zonesQ := ZoneManager.Query().SubQuery()
- q := guestsQ.Query(
- guestsQ.Field("instance_type"),
- hostsQ.Field("zone_id"),
- zonesQ.Field("cloudregion_id"),
- ).
- Join(hostsQ, sqlchemy.Equals(guestsQ.Field("host_id"), hostsQ.Field("id"))).
- Join(zonesQ, sqlchemy.Equals(hostsQ.Field("zone_id"), zonesQ.Field("id"))).
- Filter(sqlchemy.In(guestsQ.Field("instance_type"), instanceTypes))
- err := q.All(&ret)
- if err != nil {
- log.Errorf("query instance cnt error: %v", err)
- return rows
- }
- skuMap := map[string]map[string]int{}
- for _, sku := range ret {
- _, ok := skuMap[sku.InstanceType]
- if !ok {
- skuMap[sku.InstanceType] = map[string]int{}
- }
- _, ok = skuMap[sku.InstanceType][sku.ZoneId]
- if !ok {
- skuMap[sku.InstanceType][sku.ZoneId] = 0
- }
- skuMap[sku.InstanceType][sku.ZoneId] += 1
- _, ok = skuMap[sku.InstanceType][sku.CloudregionId]
- if !ok {
- skuMap[sku.InstanceType][sku.CloudregionId] = 0
- }
- skuMap[sku.InstanceType][sku.CloudregionId] += 1
- }
- for i := range rows {
- sku := objs[i].(*SServerSku)
- if len(sku.ZoneId) > 0 {
- rows[i].TotalGuestCount = skuMap[sku.Name][sku.ZoneId]
- } else {
- rows[i].TotalGuestCount = skuMap[sku.Name][sku.CloudregionId]
- }
- }
- return rows
- }
- func (self *SServerSkuManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ServerSkuCreateInput) (api.ServerSkuCreateInput, error) {
- var region *SCloudregion
- if len(input.CloudregionId) > 0 {
- _region, err := validators.ValidateModel(ctx, userCred, CloudregionManager, &input.CloudregionId)
- if err != nil {
- return input, err
- }
- region = _region.(*SCloudregion)
- }
- if len(input.ZoneId) > 0 {
- _zone, err := validators.ValidateModel(ctx, userCred, ZoneManager, &input.ZoneId)
- if err != nil {
- return input, err
- }
- zone := _zone.(*SZone)
- if len(input.CloudregionId) == 0 {
- input.CloudregionId = zone.CloudregionId
- }
- if input.CloudregionId != zone.CloudregionId {
- return input, httperrors.NewConflictError("zone %s not in cloudregion %s", zone.Name, input.CloudregionId)
- }
- region, _ = zone.GetRegion()
- }
- if input.Enabled == nil {
- enabled := true
- input.Enabled = &enabled
- }
- input.Provider = api.CLOUD_PROVIDER_ONECLOUD
- input.Status = api.SkuStatusReady
- if region != nil {
- input.Provider = region.Provider
- }
- if input.Provider == api.CLOUD_PROVIDER_ONECLOUD {
- input.CloudregionId = api.DEFAULT_REGION_ID
- } else if utils.IsInStringArray(input.Provider, api.PRIVATE_CLOUD_PROVIDERS) {
- input.Status = api.SkuStatusCreating
- }
- if !utils.IsInStringArray(input.Provider, api.PUBLIC_CLOUD_PROVIDERS) {
- if input.CpuCoreCount < 1 || input.CpuCoreCount > options.Options.SkuMaxCpuCount {
- return input, httperrors.NewOutOfRangeError("cpu_core_count should be range of 1~%d", options.Options.SkuMaxCpuCount)
- }
- if input.MemorySizeMB < 512 || input.MemorySizeMB > 1024*options.Options.SkuMaxMemSize {
- return input, httperrors.NewOutOfRangeError("memory_size_mb, shoud be range of 512~%d", 1024*options.Options.SkuMaxMemSize)
- }
- if len(input.InstanceTypeCategory) == 0 {
- input.InstanceTypeCategory = api.SkuCategoryGeneralPurpose
- }
- if !utils.IsInStringArray(input.InstanceTypeCategory, api.SKU_FAMILIES) {
- return input, httperrors.NewInputParameterError("instance_type_category shoud be one of %s", api.SKU_FAMILIES)
- }
- input.LocalCategory = input.InstanceTypeCategory
- input.InstanceTypeFamily = api.InstanceFamilies[input.InstanceTypeCategory]
- }
- if len(input.LocalCategory) == 0 {
- input.LocalCategory = input.InstanceTypeCategory
- }
- if len(input.InstanceTypeFamily) == 0 {
- input.InstanceTypeFamily = api.InstanceFamilies[input.InstanceTypeCategory]
- }
- var err error
- if len(input.Name) == 0 {
- // 格式 ecs.g1.c1m1
- input.Name, err = genInstanceType(input.InstanceTypeFamily, input.CpuCoreCount, input.MemorySizeMB)
- if err != nil {
- return input, httperrors.NewInputParameterError("%v", err)
- }
- q := self.Query().Equals("name", input.Name)
- if len(input.CloudregionId) > 0 {
- q = q.Equals("cloudregion_id", input.CloudregionId)
- }
- count, err := q.CountWithError()
- if err != nil {
- return input, httperrors.NewInternalServerError("checkout server sku name duplicate error: %v", err)
- }
- if count > 0 {
- return input, httperrors.NewDuplicateResourceError("Duplicate sku %s", input.Name)
- }
- }
- input.EnabledStatusStandaloneResourceCreateInput, err = self.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput)
- if err != nil {
- return input, err
- }
- return input, nil
- }
- func (self *SServerSku) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
- if utils.IsInStringArray(self.Provider, api.PRIVATE_CLOUD_PROVIDERS) {
- self.StartSkuCreateTask(ctx, userCred)
- }
- }
- func (self *SServerSku) StartSkuCreateTask(ctx context.Context, userCred mcclient.TokenCredential) error {
- task, err := taskman.TaskManager.NewTask(ctx, "ServerSkuCreateTask", self, userCred, nil, "", "", nil)
- if err != nil {
- return errors.Wrapf(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (self *SServerSku) GetPrivateCloudproviders() ([]SCloudprovider, error) {
- providers := []SCloudprovider{}
- q := CloudproviderManager.Query().In("provider", CloudproviderManager.GetPrivateProviderProvidersQuery())
- err := db.FetchModelObjects(CloudproviderManager, q, &providers)
- if err != nil {
- return nil, err
- }
- return providers, nil
- }
- func (self *SServerSkuManager) ClearSchedDescCache(wait bool) error {
- s := auth.GetAdminSession(context.Background(), options.Options.Region)
- _, err := scheduler.SchedManager.SyncSku(s, true)
- if err != nil {
- return errors.Wrapf(err, "chedManager.SyncSku")
- }
- return nil
- }
- func (self *SServerSkuManager) FetchByZoneExtId(zoneExtId string, name string) (db.IModel, error) {
- zoneObj, err := db.FetchByExternalId(ZoneManager, zoneExtId)
- if err != nil {
- return nil, err
- }
- return self.FetchByZoneId(zoneObj.GetId(), name)
- }
- func (self *SServerSkuManager) FetchByZoneId(zoneId string, name string) (db.IModel, error) {
- q := self.Query().Equals("zone_id", zoneId).Equals("name", name)
- count, err := q.CountWithError()
- if err != nil {
- return nil, err
- }
- if count == 1 {
- obj, err := db.NewModelObject(self)
- if err != nil {
- return nil, err
- }
- err = q.First(obj)
- if err != nil {
- return nil, err
- } else {
- return obj.(db.IStandaloneModel), nil
- }
- } else if count > 1 {
- return nil, sqlchemy.ErrDuplicateEntry
- } else {
- return nil, sql.ErrNoRows
- }
- }
- // 四舍五入
- func round(n int, step int) int {
- q := float64(n) / float64(step)
- return int(math.Floor(q+0.5)) * step
- }
- // 内存按GB取整
- func roundMem(n int) int {
- if n <= 512 {
- return 512
- }
- return round(n, 1024)
- }
- // step必须是偶数
- func interval(n int, step int) (int, int) {
- r := round(n, step)
- start := r - step/2
- end := r + step/2
- return start, end
- }
- // 计算内存所在区间范围
- func intervalMem(n int) (int, int) {
- if n <= 512 {
- return 0, 512
- }
- return interval(n, 1024)
- }
- func networkUsableRegionQueries(f sqlchemy.IQueryField) []sqlchemy.ICondition {
- providers := usableCloudProviders()
- networks := NetworkManager.Query("wire_id").Equals("status", api.NETWORK_STATUS_AVAILABLE)
- wires := WireManager.Query("vpc_id").In("id", networks)
- _vpcs := VpcManager.Query("cloudregion_id").
- Equals("status", api.VPC_STATUS_AVAILABLE).
- In("id", wires)
- filters := sqlchemy.OR(sqlchemy.In(_vpcs.Field("manager_id"), providers), sqlchemy.IsNullOrEmpty(_vpcs.Field("manager_id")))
- vpcs := _vpcs.Filter(filters).SubQuery()
- sq := vpcs.Query(sqlchemy.DISTINCT("cloudregion_id", vpcs.Field("cloudregion_id")))
- return []sqlchemy.ICondition{sqlchemy.In(f, sq.SubQuery())}
- }
- func usableFilter(q *sqlchemy.SQuery, public_cloud bool) (*sqlchemy.SQuery, error) {
- // 过滤出公有云provider状态健康的sku
- if public_cloud {
- providerTable := usableCloudProviders().SubQuery()
- providerRegionTable := CloudproviderRegionManager.Query().SubQuery()
- _subq := providerRegionTable.Query(sqlchemy.DISTINCT("cloudregion_id", providerRegionTable.Field("cloudregion_id")))
- subq := _subq.Join(providerTable, sqlchemy.Equals(providerRegionTable.Field("cloudprovider_id"), providerTable.Field("id"))).SubQuery()
- q.Join(subq, sqlchemy.Equals(q.Field("cloudregion_id"), subq.Field("cloudregion_id")))
- }
- // 过滤出network usable的sku
- if public_cloud {
- zoneIds, err := NetworkUsableZoneIds(true, true, nil)
- if err != nil {
- return nil, errors.Wrap(err, "NetworkUsableZoneIds")
- }
- q = q.Filter(sqlchemy.OR(sqlchemy.In(q.Field("zone_id"), zoneIds), sqlchemy.IsNullOrEmpty(q.Field("zone_id")))) //Azure的zone_id可能为空
- } else {
- // 本地IDC sku 只定义到region层级, zone id 为空.因此只能按region查询
- iconditions := networkUsableRegionQueries(q.Field("cloudregion_id"))
- // 私有云 sku region及zone为空
- iconditions = append(iconditions, sqlchemy.IsNullOrEmpty(q.Field("cloudregion_id")))
- q = q.Filter(sqlchemy.OR(iconditions...))
- }
- return q, nil
- }
- func (manager *SServerSkuManager) GetPropertyInstanceSpecs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- listQuery := api.ServerSkuListInput{}
- err := query.Unmarshal(&listQuery)
- if err != nil {
- return nil, errors.Wrap(err, "query.Unmarshal")
- }
- q, err := manager.ListItemFilter(ctx, manager.Query(), userCred, listQuery)
- if err != nil {
- return nil, errors.Wrap(err, "manager.ListItemFilter")
- }
- skus := make([]SServerSku, 0)
- q = q.GroupBy(q.Field("cpu_core_count"), q.Field("memory_size_mb"))
- q = q.Asc(q.Field("cpu_core_count"), q.Field("memory_size_mb"))
- err = db.FetchModelObjects(manager, q, &skus)
- if err != nil {
- log.Errorf("FetchModelObjects %s: %s", q.DebugString(), err)
- return nil, httperrors.NewBadRequestError("instance specs list query error")
- }
- cpus := jsonutils.NewArray()
- mems_mb := []int{}
- cpu_mems_mb := map[string][]int{}
- mems := map[int]bool{}
- oc := 0
- for i := range skus {
- nc := skus[i].CpuCoreCount
- nm := roundMem(skus[i].MemorySizeMB) // 内存按GB取整
- if nc > oc {
- cpus.Add(jsonutils.NewInt(int64(nc)))
- oc = nc
- }
- if _, exists := mems[nm]; !exists {
- mems_mb = append(mems_mb, nm)
- mems[nm] = true
- }
- k := strconv.Itoa(nc)
- if _, exists := cpu_mems_mb[k]; !exists {
- cpu_mems_mb[k] = []int{nm}
- } else {
- idx := len(cpu_mems_mb[k]) - 1
- if cpu_mems_mb[k][idx] != nm {
- cpu_mems_mb[k] = append(cpu_mems_mb[k], nm)
- }
- }
- }
- ret := jsonutils.NewDict()
- ret.Add(cpus, "cpus")
- ret.Add(sliceToJsonObject(mems_mb), "mems_mb")
- r_obj := jsonutils.Marshal(&cpu_mems_mb)
- ret.Add(r_obj, "cpu_mems_mb")
- return ret, nil
- }
- func (self *SServerSku) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSkuUpdateInput) (api.ServerSkuUpdateInput, error) {
- if len(input.Name) > 0 {
- return input, httperrors.NewUnsupportOperationError("Cannot change server sku name")
- }
- var err error
- input.EnabledStatusStandaloneResourceBaseUpdateInput, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusStandaloneResourceBaseUpdateInput)
- if err != nil {
- return input, errors.Wrap(err, "SEnabledStatusStandaloneResourceBase.ValidateUpdateData")
- }
- return input, nil
- }
- func (self *SServerSku) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- ServerSkuManager.ClearSchedDescCache(true)
- if utils.IsInStringArray(self.Provider, api.PUBLIC_CLOUD_PROVIDERS) && (data.Contains("prepaid_status") || data.Contains("postpaid_status")) {
- self.SetMetadata(ctx, api.SERVER_SKU_PROJECT_SRC_KEY, api.SERVER_SKU_PROJECT_SRC_VALUE_LOCAL, userCred)
- }
- self.SEnabledStatusStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
- }
- func (self *SServerSku) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- log.Infof("SServerSku delete do nothing")
- return nil
- }
- func (self *SServerSku) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- ServerSkuManager.ClearSchedDescCache(true)
- return db.RealDeleteModel(ctx, userCred, self)
- }
- func (self *SServerSku) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- return self.StartServerSkuDeleteTask(ctx, userCred, jsonutils.QueryBoolean(data, "purge", false), "")
- }
- func (self *SServerSku) StartServerSkuDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, purge bool, parentTaskId string) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewBool(purge), "purge")
- task, err := taskman.TaskManager.NewTask(ctx, "ServerSkuDeleteTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- log.Errorf("newTask ServerSkuDeleteTask fail %s", err)
- return err
- }
- self.SetStatus(ctx, userCred, api.SkuStatusDeleting, "start to delete")
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SServerSku) GetGuestCount() (int, error) {
- guestsQ := GuestManager.Query().SubQuery()
- hostsQ := HostManager.Query().SubQuery()
- zonesQ := ZoneManager.Query().SubQuery()
- q := guestsQ.Query().
- Join(hostsQ, sqlchemy.Equals(guestsQ.Field("host_id"), hostsQ.Field("id"))).
- Join(zonesQ, sqlchemy.Equals(hostsQ.Field("zone_id"), zonesQ.Field("id"))).
- Filter(sqlchemy.Equals(guestsQ.Field("instance_type"), self.Name))
- if len(self.ZoneId) > 0 {
- q = q.Filter(sqlchemy.Equals(hostsQ.Field("zone_id"), self.ZoneId))
- } else {
- q = q.Filter(sqlchemy.Equals(zonesQ.Field("cloudregion_id"), self.CloudregionId))
- }
- return q.CountWithError()
- }
- func (self *SServerSku) ValidateDeleteCondition(ctx context.Context, info *api.ServerSkuDetails) error {
- totalGuestCnt := 0
- if info != nil {
- totalGuestCnt = info.TotalGuestCount
- } else {
- totalGuestCnt, _ = self.GetGuestCount()
- }
- if totalGuestCnt > 0 {
- return httperrors.NewNotEmptyError("now allow to delete inuse instance_type.please remove related servers first: %s", self.Name)
- }
- if !options.Options.EnableDeletePublicCloudSku && utils.IsInStringArray(self.Provider, api.PUBLIC_CLOUD_PROVIDERS) {
- return httperrors.NewForbiddenError("not allow to delete public cloud instance_type: %s", self.Name)
- }
- return nil
- }
- func (self *SServerSku) GetZoneExternalId() (string, error) {
- zoneObj, err := ZoneManager.FetchById(self.ZoneId)
- if err != nil {
- return "", err
- }
- zone := zoneObj.(*SZone)
- return zone.GetExternalId(), nil
- }
- func listItemDomainFilter(q *sqlchemy.SQuery, providers []string, domainId string) *sqlchemy.SQuery {
- // CLOUD_PROVIDER_ONECLOUD 没有对应的cloudaccount
- if len(domainId) > 0 {
- if len(providers) >= 1 && !utils.IsInStringArray(api.CLOUD_PROVIDER_ONECLOUD, providers) {
- // 明确指定只查询公有云provider的情况,只查询公有云skus
- q = q.In("provider", getDomainManagerProviderSubq(domainId))
- } else if len(providers) == 1 && utils.IsInStringArray(api.CLOUD_PROVIDER_ONECLOUD, providers) {
- // 明确指定只查询私有云provider的情况
- } else {
- // 公有云skus & 私有云skus 混合查询
- publicSkusQ := sqlchemy.In(q.Field("provider"), getDomainManagerProviderSubq(domainId))
- privateSkusQ := sqlchemy.Equals(q.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD)
- q = q.Filter(sqlchemy.OR(publicSkusQ, privateSkusQ))
- }
- }
- return q
- }
- // 主机套餐规格列表
- func (manager *SServerSkuManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.ServerSkuListInput,
- ) (*sqlchemy.SQuery, error) {
- publicCloud := false
- cloudEnvStr := query.CloudEnv
- if cloudEnvStr == api.CLOUD_ENV_PUBLIC_CLOUD {
- publicCloud = true
- pq := CloudproviderManager.GetPublicProviderProvidersQuery()
- q = q.Join(pq, sqlchemy.Equals(q.Field("provider"), pq.Field("provider")))
- }
- if cloudEnvStr == api.CLOUD_ENV_PRIVATE_CLOUD {
- pq := CloudproviderManager.GetPrivateProviderProvidersQuery()
- q = q.Join(pq, sqlchemy.Equals(q.Field("provider"), pq.Field("provider")))
- }
- if cloudEnvStr == api.CLOUD_ENV_ON_PREMISE {
- q = q.Filter(
- sqlchemy.OR(
- sqlchemy.Equals(q.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
- sqlchemy.In(q.Field("provider"), CloudproviderManager.GetOnPremiseProviderProvidersQuery()),
- ),
- )
- }
- if cloudEnvStr == api.CLOUD_ENV_PRIVATE_ON_PREMISE {
- q = q.Filter(sqlchemy.OR(
- sqlchemy.In(q.Field("provider"), CloudproviderManager.GetPrivateProviderProvidersQuery()), //私有云
- sqlchemy.Equals(q.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD), //本地IDC
- ),
- )
- }
- if domainStr := query.ProjectDomainId; len(domainStr) > 0 {
- domain, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, domainStr)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2("domains", domainStr)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- query.ProjectDomainId = domain.GetId()
- }
- q = listItemDomainFilter(q, query.Providers, query.ProjectDomainId)
- providers := query.Providers
- if len(providers) > 0 {
- q = q.Filter(sqlchemy.In(q.Field("provider"), providers))
- if len(providers) == 1 && utils.IsInStringArray(providers[0], cloudprovider.GetPublicProviders()) {
- publicCloud = true
- }
- }
- conditions := []sqlchemy.ICondition{}
- for _, arch := range query.CpuArch {
- if len(arch) == 0 {
- continue
- }
- if arch == apis.OS_ARCH_X86 {
- conditions = append(conditions, sqlchemy.OR(
- sqlchemy.Startswith(q.Field("cpu_arch"), arch),
- sqlchemy.Equals(q.Field("cpu_arch"), apis.OS_ARCH_I386),
- sqlchemy.IsNullOrEmpty(q.Field("cpu_arch")),
- ))
- } else if arch == apis.OS_ARCH_ARM {
- conditions = append(conditions, sqlchemy.OR(
- sqlchemy.Startswith(q.Field("cpu_arch"), arch),
- sqlchemy.Equals(q.Field("cpu_arch"), apis.OS_ARCH_AARCH32),
- sqlchemy.Equals(q.Field("cpu_arch"), apis.OS_ARCH_AARCH64),
- sqlchemy.IsNullOrEmpty(q.Field("cpu_arch")),
- ))
- } else if arch == apis.OS_ARCH_RISCV {
- conditions = append(conditions, sqlchemy.OR(
- sqlchemy.Startswith(q.Field("cpu_arch"), arch),
- sqlchemy.Equals(q.Field("cpu_arch"), apis.OS_ARCH_RISCV32),
- sqlchemy.Equals(q.Field("cpu_arch"), apis.OS_ARCH_RISCV64),
- sqlchemy.IsNullOrEmpty(q.Field("cpu_arch")),
- ))
- } else {
- conditions = append(conditions, sqlchemy.Startswith(q.Field("cpu_arch"), arch))
- }
- }
- if len(conditions) > 0 {
- q = q.Filter(sqlchemy.OR(conditions...))
- }
- if query.Distinct {
- q = q.GroupBy(q.Field("name"))
- }
- brands := query.Brands
- if len(brands) > 0 {
- q = q.Filter(sqlchemy.In(q.Field("brand"), brands))
- }
- q, err := manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter")
- }
- if query.Usable != nil && *query.Usable {
- q, err := usableFilter(q, publicCloud)
- if err != nil {
- return nil, err
- }
- q = q.IsTrue("enabled")
- }
- zoneStr := query.ZoneId
- if len(zoneStr) > 0 {
- zoneObj, err := validators.ValidateModel(ctx, userCred, ZoneManager, &zoneStr)
- if err != nil {
- return nil, err
- }
- zone := zoneObj.(*SZone)
- region, err := zone.GetRegion()
- if err != nil {
- return nil, errors.Wrapf(err, "GetRegion %s", zone.Name)
- }
- q = q.Filter(
- sqlchemy.OR(
- sqlchemy.AND(
- sqlchemy.Equals(q.Field("cloudregion_id"), region.Id),
- sqlchemy.IsNullOrEmpty(q.Field("zone_id")),
- ),
- sqlchemy.Equals(q.Field("zone_id"), zone.Id),
- ),
- )
- }
- q, err = managedResourceFilterByRegion(ctx, q, query.RegionalFilterListInput, "", nil)
- if err != nil {
- return nil, errors.Wrap(err, "managedResourceFilterByRegion")
- }
- if len(query.PostpaidStatus) > 0 {
- q = q.Equals("postpaid_status", query.PostpaidStatus)
- }
- if len(query.PrepaidStatus) > 0 {
- q = q.Equals("prepaid_status", query.PrepaidStatus)
- }
- conditions = []sqlchemy.ICondition{}
- for _, sizeMb := range query.MemorySizeMb {
- // 按区间查询内存, 避免0.75G这样的套餐不好过滤
- if sizeMb > 0 {
- s, e := intervalMem(sizeMb)
- conditions = append(
- conditions,
- sqlchemy.AND(
- sqlchemy.GT(q.Field("memory_size_mb"), s),
- sqlchemy.LE(q.Field("memory_size_mb"), e),
- ),
- )
- }
- }
- if len(conditions) > 0 {
- q = q.Filter(sqlchemy.OR(conditions...))
- }
- if len(query.CpuCoreCount) > 0 {
- q = q.In("cpu_core_count", query.CpuCoreCount)
- }
- return q, err
- }
- func (manager *SServerSkuManager) OrderByExtraFields(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.ServerSkuListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
- }
- if db.NeedOrderQuery([]string{query.OrderByTotalGuestCount}) {
- guestQ := GuestManager.Query()
- guestQ = guestQ.AppendField(guestQ.Field("instance_type"), sqlchemy.COUNT("total_guest_count"))
- guestQ = guestQ.GroupBy(guestQ.Field("instance_type"))
- guestSQ := guestQ.SubQuery()
- q = q.Join(guestSQ, sqlchemy.Equals(guestSQ.Field("instance_type"), q.Field("name")))
- q = q.AppendField(q.QueryFields()...)
- q = q.AppendField(guestSQ.Field("total_guest_count"))
- q = db.OrderByFields(q, []string{query.OrderByTotalGuestCount}, []sqlchemy.IQueryField{q.Field("total_guest_count")})
- }
- return q, nil
- }
- func (manager *SServerSkuManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SServerSkuManager) GetMatchedSku(regionId string, cpu int64, memMB int64) (*SServerSku, error) {
- ret := &SServerSku{}
- _region, err := CloudregionManager.FetchById(regionId)
- if err != nil {
- return nil, errors.Wrapf(err, "CloudregionManager.FetchById(%s)", regionId)
- }
- region := _region.(*SCloudregion)
- if utils.IsInStringArray(region.Provider, api.PRIVATE_CLOUD_PROVIDERS) {
- regionId = api.DEFAULT_REGION_ID
- }
- q := manager.Query()
- q = q.Equals("cpu_core_count", cpu).Equals("memory_size_mb", memMB).Equals("cloudregion_id", regionId).Equals("postpaid_status", api.SkuStatusAvailable)
- err = q.First(ret)
- if err != nil {
- return nil, errors.Wrap(err, "ServerSkuManager.GetMatchedSku")
- }
- return ret, nil
- }
- func (manager *SServerSkuManager) FetchSkuByNameAndProvider(name string, provider string, checkConsistency bool) (*SServerSku, error) {
- q := manager.Query().IsTrue("enabled")
- q = q.Equals("name", name)
- if utils.IsInStringArray(provider, []string{api.CLOUD_PROVIDER_ONECLOUD, api.CLOUD_PROVIDER_VMWARE, api.CLOUD_PROVIDER_NUTANIX}) {
- q = q.Filter(
- sqlchemy.Equals(q.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
- )
- } else if utils.IsInStringArray(provider, api.PRIVATE_CLOUD_PROVIDERS) {
- q = q.Filter(sqlchemy.OR(
- sqlchemy.Equals(q.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
- sqlchemy.Equals(q.Field("provider"), provider),
- ))
- } else {
- q = q.Equals("provider", provider)
- }
- skus := make([]SServerSku, 0)
- err := db.FetchModelObjects(manager, q, &skus)
- if err != nil {
- log.Errorf("fetch sku fail %s", err)
- return nil, err
- }
- if len(skus) == 0 {
- log.Errorf("no sku found for %s %s", name, provider)
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), name)
- }
- if len(skus) == 1 {
- return &skus[0], nil
- }
- if checkConsistency {
- for i := 1; i < len(skus); i += 1 {
- if skus[i].CpuCoreCount != skus[0].CpuCoreCount || skus[i].MemorySizeMB != skus[0].MemorySizeMB {
- log.Errorf("inconsistent sku %s %s", jsonutils.Marshal(&skus[0]), jsonutils.Marshal(&skus[i]))
- return nil, httperrors.NewDuplicateResourceError("duplicate instanceType %s", name)
- }
- }
- }
- return &skus[0], nil
- }
- func (manager *SServerSkuManager) GetPublicCloudSkuCount() (int, error) {
- q := manager.Query()
- q = q.Filter(sqlchemy.In(q.Field("provider"), cloudprovider.GetPublicProviders()))
- return q.CountWithError()
- }
- func (manager *SServerSkuManager) GetSkuCountByRegion(regionId string) (int, error) {
- q := manager.Query()
- if len(regionId) == 0 {
- q = q.IsNotEmpty("cloudregion_id")
- } else {
- q = q.Equals("cloudregion_id", regionId)
- }
- return q.CountWithError()
- }
- func (manager *SServerSkuManager) GetSkuCountByZone(zoneId string) []SServerSku {
- skus := []SServerSku{}
- q := manager.Query().Equals("zone_id", zoneId)
- if err := db.FetchModelObjects(manager, q, &skus); err != nil {
- log.Errorf("failed to get skus by zoneId %s error: %v", zoneId, err)
- }
- return skus
- }
- func (manager *SServerSkuManager) GetOneCloudSkus() ([]string, error) {
- skus := []SServerSku{}
- q := manager.Query().Equals("provider", api.CLOUD_PROVIDER_ONECLOUD)
- err := db.FetchModelObjects(manager, q, &skus)
- if err != nil {
- return nil, err
- }
- result := []string{}
- for _, sku := range skus {
- result = append(result, fmt.Sprintf("%d/%d", sku.CpuCoreCount, sku.MemorySizeMB))
- }
- return result, nil
- }
- func (manager *SServerSkuManager) GetSkus(provider string, cpu, memMB int) ([]SServerSku, error) {
- skus := []SServerSku{}
- q := manager.Query()
- if provider == api.CLOUD_PROVIDER_ONECLOUD {
- providerFilter := sqlchemy.OR(sqlchemy.Equals(q.Field("provider"), provider), sqlchemy.IsNullOrEmpty(q.Field("provider")))
- q = q.Equals("cpu_core_count", cpu).Equals("memory_size_mb", memMB).Filter(providerFilter)
- } else {
- q = q.Equals("cpu_core_count", cpu).Equals("memory_size_mb", memMB).Equals("provider", provider)
- }
- if err := db.FetchModelObjects(manager, q, &skus); err != nil {
- log.Errorf("failed to get skus with provider %s cpu %d mem %d error: %v", provider, cpu, memMB, err)
- return nil, err
- }
- return skus, nil
- }
- // 删除表中zone not found的记录
- func (manager *SServerSkuManager) DeleteInvalidSkus() error {
- _, err := sqlchemy.GetDB().Exec(
- fmt.Sprintf(
- "delete from %s where length(zone_id) > 0 and zone_id not in (select id from zones_tbl where deleted=0)",
- manager.TableSpec().Name(),
- ),
- )
- return err
- }
- func (manager *SServerSkuManager) SyncPrivateCloudSkus(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- region *SCloudregion,
- skus []cloudprovider.ICloudSku,
- xor bool,
- ) compare.SyncResult {
- lockman.LockRawObject(ctx, manager.Keyword(), region.Id)
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), region.Id)
- result := compare.SyncResult{}
- dbSkus, err := region.GetServerSkus()
- if err != nil {
- result.Error(errors.Wrapf(err, "GetServerSkus"))
- return result
- }
- removed := make([]SServerSku, 0)
- commondb := make([]SServerSku, 0)
- commonext := make([]cloudprovider.ICloudSku, 0)
- added := make([]cloudprovider.ICloudSku, 0)
- err = compare.CompareSets(dbSkus, skus, &removed, &commondb, &commonext, &added)
- if err != nil {
- result.Error(errors.Wrapf(err, "CompareSets"))
- return result
- }
- for i := 0; i < len(removed); i += 1 {
- err = removed[i].RealDelete(ctx, userCred)
- if err != nil {
- result.DeleteError(err)
- continue
- }
- result.Delete()
- }
- if !xor {
- for i := 0; i < len(commondb); i += 1 {
- err = commondb[i].SyncWithPrivateCloudSku(ctx, userCred, commonext[i])
- if err != nil {
- result.UpdateError(err)
- }
- result.Update()
- }
- }
- for i := 0; i < len(added); i += 1 {
- err := manager.newPrivateCloudSku(ctx, userCred, region, added[i])
- if err != nil {
- result.AddError(err)
- continue
- }
- result.Add()
- }
- return result
- }
- func (self *SServerSku) SyncWithPrivateCloudSku(ctx context.Context, userCred mcclient.TokenCredential, sku cloudprovider.ICloudSku) error {
- _, err := db.Update(self, func() error {
- self.Status = api.SkuStatusAvailable
- self.constructSku(sku)
- return nil
- })
- return err
- }
- func (self *SServerSku) constructSku(extSku cloudprovider.ICloudSku) {
- self.ExternalId = extSku.GetGlobalId()
- self.InstanceTypeFamily = extSku.GetInstanceTypeFamily()
- self.InstanceTypeCategory = extSku.GetInstanceTypeCategory()
- self.PrepaidStatus = extSku.GetPrepaidStatus()
- self.PostpaidStatus = extSku.GetPostpaidStatus()
- self.CpuArch = extSku.GetCpuArch()
- self.CpuCoreCount = extSku.GetCpuCoreCount()
- self.MemorySizeMB = extSku.GetMemorySizeMB()
- self.OsName = extSku.GetOsName()
- self.SysDiskResizable = tristate.NewFromBool(extSku.GetSysDiskResizable())
- self.SysDiskType = extSku.GetSysDiskType()
- self.SysDiskMinSizeGB = extSku.GetSysDiskMinSizeGB()
- self.SysDiskMaxSizeGB = extSku.GetSysDiskMaxSizeGB()
- self.AttachedDiskType = extSku.GetAttachedDiskType()
- self.AttachedDiskSizeGB = extSku.GetAttachedDiskSizeGB()
- self.AttachedDiskCount = extSku.GetAttachedDiskCount()
- self.DataDiskTypes = extSku.GetDataDiskTypes()
- self.DataDiskMaxCount = extSku.GetDataDiskMaxCount()
- self.NicType = extSku.GetNicType()
- self.NicMaxCount = extSku.GetNicMaxCount()
- self.GpuAttachable = tristate.NewFromBool(extSku.GetGpuAttachable())
- self.GpuSpec = extSku.GetGpuSpec()
- self.GpuCount = extSku.GetGpuCount()
- self.GpuMaxCount = extSku.GetGpuMaxCount()
- self.Name = extSku.GetName()
- }
- func (region *SCloudregion) getMetaUrl(base string, externalId string) string {
- if region.Provider == api.CLOUD_PROVIDER_HUAWEI && strings.Contains(region.ExternalId, "_") {
- idx := strings.Index(region.ExternalId, "_")
- return fmt.Sprintf("%s/%s/%s.json", base, region.ExternalId[:idx], externalId)
- }
- return fmt.Sprintf("%s/%s/%s.json", base, region.ExternalId, externalId)
- }
- func (region *SCloudregion) newPublicCloudSku(ctx context.Context, userCred mcclient.TokenCredential, extSku SServerSku) error {
- meta, err := yunionmeta.FetchYunionmeta(ctx)
- if err != nil {
- return err
- }
- zones, err := region.GetZones()
- if err != nil {
- return errors.Wrap(err, "GetZones")
- }
- zoneMaps := map[string]string{}
- for _, zone := range zones {
- zoneMaps[zone.ExternalId] = zone.Id
- }
- sku := &SServerSku{}
- sku.SetModelManager(ServerSkuManager, sku)
- skuUrl := region.getMetaUrl(meta.ServerBase, extSku.ExternalId)
- err = meta.Get(skuUrl, sku)
- if err != nil {
- return errors.Wrapf(err, "Get")
- }
- if len(sku.ZoneId) > 0 {
- zoneId := sku.ZoneId
- sku.ZoneId = yunionmeta.GetZoneIdBySuffix(zoneMaps, zoneId)
- if len(sku.ZoneId) == 0 {
- return errors.Wrapf(cloudprovider.ErrNotFound, "%v", zoneId)
- }
- }
- // 第一次同步新建的套餐是启用状态
- sku.Enabled = tristate.True
- sku.Status = api.SkuStatusReady
- sku.CloudregionId = region.Id
- sku.Provider = region.Provider
- return ServerSkuManager.TableSpec().Insert(ctx, sku)
- }
- func (manager *SServerSkuManager) newPrivateCloudSku(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, extSku cloudprovider.ICloudSku) error {
- sku := &SServerSku{Provider: region.Provider}
- sku.SetModelManager(manager, sku)
- // 第一次同步新建的套餐是启用状态
- sku.Enabled = tristate.True
- sku.Status = api.SkuStatusReady
- sku.constructSku(extSku)
- sku.CloudregionId = region.Id
- sku.Name = extSku.GetName()
- return manager.TableSpec().Insert(ctx, sku)
- }
- func (self *SServerSku) syncWithCloudSku(ctx context.Context, region *SCloudregion, isLocalChangedStatus bool, extSku SServerSku) error {
- if self.Md5 == extSku.Md5 {
- return nil
- }
- meta, err := yunionmeta.FetchYunionmeta(ctx)
- if err != nil {
- return err
- }
- sku := &SServerSku{}
- skuUrl := region.getMetaUrl(meta.ServerBase, extSku.ExternalId)
- err = meta.Get(skuUrl, sku)
- if err != nil {
- return errors.Wrapf(err, "Get")
- }
- _, err = db.Update(self, func() error {
- if !isLocalChangedStatus {
- self.PrepaidStatus = sku.PrepaidStatus
- self.PostpaidStatus = sku.PostpaidStatus
- }
- self.SysDiskType = sku.SysDiskType
- self.DataDiskTypes = sku.DataDiskTypes
- self.CpuArch = sku.CpuArch
- self.InstanceTypeFamily = sku.InstanceTypeFamily
- self.InstanceTypeCategory = sku.InstanceTypeCategory
- self.LocalCategory = sku.LocalCategory
- self.NicType = sku.NicType
- self.GpuAttachable = sku.GpuAttachable
- self.GpuSpec = sku.GpuSpec
- self.GpuCount = sku.GpuCount
- self.CpuCoreCount = sku.CpuCoreCount
- self.MemorySizeMB = sku.MemorySizeMB
- self.Md5 = sku.Md5
- return nil
- })
- return err
- }
- func (self *SServerSku) MarkAsSoldout(ctx context.Context) error {
- _, err := db.UpdateWithLock(ctx, self, func() error {
- self.PrepaidStatus = api.SkuStatusSoldout
- self.PostpaidStatus = api.SkuStatusSoldout
- return nil
- })
- return errors.Wrap(err, "SServerSku.MarkAsSoldout")
- }
- func (region *SCloudregion) FetchSkusByRegion() ([]SServerSku, error) {
- q := ServerSkuManager.Query().Equals("cloudregion_id", region.Id)
- skus := make([]SServerSku, 0)
- err := db.FetchModelObjects(ServerSkuManager, q, &skus)
- if err != nil {
- return nil, errors.Wrapf(err, "FetchSkusByRegion %s", region.ExternalId)
- }
- return skus, nil
- }
- func (region *SCloudregion) GetUsedSkus() (map[string]bool, error) {
- hosts := HostManager.Query().SubQuery()
- zones := ZoneManager.Query().Equals("cloudregion_id", region.Id).SubQuery()
- q := GuestManager.Query("instance_type").Distinct()
- q = q.Join(hosts, sqlchemy.Equals(q.Field("host_id"), hosts.Field("id")))
- q = q.Join(zones, sqlchemy.Equals(hosts.Field("zone_id"), zones.Field("id")))
- ret := []struct {
- InstanceType string `json:"instance_type"`
- }{}
- err := q.All(&ret)
- if err != nil {
- return nil, errors.Wrapf(err, "GetUsedSkus %s", region.ExternalId)
- }
- usedSkus := make(map[string]bool, 0)
- for _, item := range ret {
- usedSkus[item.InstanceType] = true
- }
- return usedSkus, nil
- }
- // 获取本地已变更过套餐状态的公有云套餐
- func (manager *SServerSkuManager) GetLocalSkus() (map[string]bool, error) {
- q := db.Metadata.Query("obj_id").Equals("obj_type", manager.Keyword()).Equals("key", api.SERVER_SKU_PROJECT_SRC_KEY).Equals("value", api.SERVER_SKU_PROJECT_SRC_VALUE_LOCAL)
- ret := []struct {
- ObjId string `json:"obj_id"`
- }{}
- err := q.All(&ret)
- if err != nil {
- return nil, errors.Wrapf(err, "GetLocalSkus")
- }
- localSkus := make(map[string]bool, 0)
- for _, item := range ret {
- localSkus[item.ObjId] = true
- }
- return localSkus, nil
- }
- func (region *SCloudregion) SyncServerSkus(ctx context.Context, userCred mcclient.TokenCredential, xor bool) compare.SyncResult {
- lockman.LockRawObject(ctx, ServerSkuManager.Keyword(), region.Id)
- defer lockman.ReleaseRawObject(ctx, ServerSkuManager.Keyword(), region.Id)
- result := compare.SyncResult{}
- meta, err := yunionmeta.FetchYunionmeta(ctx)
- if err != nil {
- result.Error(errors.Wrapf(err, "FetchYunionmeta"))
- return result
- }
- extSkus := []SServerSku{}
- err = meta.List(ServerSkuManager.Keyword(), region.ExternalId, &extSkus)
- if err != nil {
- result.Error(errors.Wrapf(err, "List"))
- return result
- }
- dbSkus, err := region.FetchSkusByRegion()
- if err != nil {
- result.Error(errors.Wrapf(err, "FetchSkusByRegion %s", region.ExternalId))
- return result
- }
- removed := make([]SServerSku, 0)
- commondb := make([]SServerSku, 0)
- commonext := make([]SServerSku, 0)
- added := make([]SServerSku, 0)
- err = compare.CompareSets(dbSkus, extSkus, &removed, &commondb, &commonext, &added)
- if err != nil {
- result.Error(errors.Wrapf(err, "CompareSets %s", region.ExternalId))
- return result
- }
- usedSkus, err := region.GetUsedSkus()
- if err != nil {
- result.Error(errors.Wrapf(err, "GetUsedSkus %s", region.ExternalId))
- return result
- }
- localSkus, err := ServerSkuManager.GetLocalSkus()
- if err != nil {
- result.Error(errors.Wrapf(err, "GetLocalSkus"))
- return result
- }
- purgeIds := []string{}
- for i := 0; i < len(removed); i += 1 {
- if usedSkus[removed[i].Name] {
- continue
- }
- purgeIds = append(purgeIds, removed[i].Id)
- }
- if len(purgeIds) > 0 {
- err = db.Purge(ServerSkuManager, "id", purgeIds, true)
- if err != nil {
- result.Error(errors.Wrapf(err, "Purge %s", region.ExternalId))
- } else {
- result.DelCnt += len(purgeIds)
- }
- }
- if !xor {
- for i := 0; i < len(commondb); i += 1 {
- _, isLocalChangedStatus := localSkus[commondb[i].Id]
- err = commondb[i].syncWithCloudSku(ctx, region, isLocalChangedStatus, commonext[i])
- if err != nil {
- result.UpdateError(err)
- } else {
- result.Update()
- }
- }
- }
- ch := make(chan struct{}, options.Options.SkuBatchSync)
- defer close(ch)
- var wg sync.WaitGroup
- for i := 0; i < len(added); i += 1 {
- ch <- struct{}{}
- wg.Add(1)
- go func(sku SServerSku) {
- defer func() {
- wg.Done()
- <-ch
- }()
- err = region.newPublicCloudSku(ctx, userCred, sku)
- if err != nil {
- result.AddError(err)
- } else {
- result.Add()
- }
- }(added[i])
- }
- wg.Wait()
- // notfiy sched manager
- _, err = scheduler.SchedManager.SyncSku(auth.GetAdminSession(ctx, options.Options.Region), false)
- if err != nil {
- log.Errorf("SchedManager SyncSku %s", err)
- }
- return result
- }
- func (manager *SServerSkuManager) InitializeData() error {
- count, err := manager.Query().Equals("cloudregion_id", api.DEFAULT_REGION_ID).IsNullOrEmpty("zone_id").CountWithError()
- if err != nil {
- return errors.Wrapf(err, "fetch default region skus")
- }
- if count == 0 {
- skus := []struct {
- cpu int
- memGb int
- }{
- {1, 1}, {1, 2}, {1, 4}, {1, 8},
- {2, 2}, {2, 4}, {2, 8}, {2, 12}, {2, 16},
- {4, 4}, {4, 12}, {4, 16}, {4, 24}, {4, 32},
- {8, 8}, {8, 16}, {8, 24}, {8, 32}, {8, 64},
- {12, 12}, {12, 16}, {12, 24}, {12, 32}, {12, 64},
- {16, 16}, {16, 24}, {16, 32}, {16, 48}, {16, 64},
- {24, 24}, {24, 32}, {24, 48}, {24, 64}, {24, 128},
- {32, 32}, {32, 48}, {32, 64}, {32, 128},
- }
- for _, item := range skus {
- sku := &SServerSku{}
- sku.CloudregionId = api.DEFAULT_REGION_ID
- sku.CpuCoreCount = item.cpu
- sku.MemorySizeMB = item.memGb * 1024
- sku.IsEmulated = false
- sku.Enabled = tristate.True
- sku.InstanceTypeCategory = api.SkuCategoryGeneralPurpose
- sku.LocalCategory = api.SkuCategoryGeneralPurpose
- sku.InstanceTypeFamily = api.InstanceFamilies[api.SkuCategoryGeneralPurpose]
- sku.Name, _ = genInstanceType(sku.InstanceTypeFamily, int64(item.cpu), int64(item.memGb*1024))
- sku.PrepaidStatus = api.SkuStatusAvailable
- sku.PostpaidStatus = api.SkuStatusAvailable
- sku.SetModelManager(manager, sku)
- err := manager.TableSpec().Insert(context.TODO(), sku)
- if err != nil {
- log.Errorf("ServerSkuManager Initialize local sku %s", err)
- }
- }
- }
- return nil
- }
- func (manager *SServerSkuManager) ListItemExportKeys(ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- keys stringutils2.SSortedStrings,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys")
- }
- if keys.ContainsAny(manager.SCloudregionResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SCloudregionResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemExportKeys")
- }
- }
- if keys.Contains("zone") {
- q, err = manager.SZoneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, stringutils2.NewSortedStrings([]string{"zone"}))
- if err != nil {
- return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemExportKeys")
- }
- }
- return q, nil
- }
- func (manager *SServerSkuManager) PerformSyncSkus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SkuSyncInput) (jsonutils.JSONObject, error) {
- return PerformActionSyncSkus(ctx, userCred, manager.Keyword(), input)
- }
- func (manager *SServerSkuManager) GetPropertySyncTasks(ctx context.Context, userCred mcclient.TokenCredential, query api.SkuTaskQueryInput) (jsonutils.JSONObject, error) {
- return GetPropertySkusSyncTasks(ctx, userCred, query)
- }
- func (self *SServerSku) GetICloudSku(ctx context.Context) (cloudprovider.ICloudSku, error) {
- region, err := self.GetRegion()
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetRegion")
- }
- return nil, errors.Wrapf(err, "GetRegion")
- }
- providers, err := region.GetCloudproviders()
- if err != nil {
- return nil, errors.Wrapf(err, "GetCloudprovider")
- }
- for i := range providers {
- provider := providers[i]
- driver, err := provider.GetProvider(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "GetDriver()")
- }
- iRegion, err := driver.GetIRegionById(region.ExternalId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetIRegionById(%s)", region.ExternalId)
- }
- skus, err := iRegion.GetISkus()
- if err != nil {
- return nil, errors.Wrapf(err, "GetICloudSku")
- }
- for i := range skus {
- if skus[i].GetGlobalId() == self.ExternalId {
- return skus[i], nil
- }
- }
- }
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%v", self.ExternalId)
- }
- func fetchSkuSyncCloudregions() []SCloudregion {
- cloudregions := []SCloudregion{}
- q := CloudregionManager.Query()
- q = q.In("provider", CloudproviderManager.GetPublicProviderProvidersQuery())
- err := db.FetchModelObjects(CloudregionManager, q, &cloudregions)
- if err != nil {
- log.Errorf("fetchSkuSyncCloudregions.FetchCloudregions failed: %v", err)
- return nil
- }
- return cloudregions
- }
- // 全量同步sku列表.
- func SyncServerSkus(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- // 清理无效的sku
- log.Debugf("DeleteInvalidSkus in processing...")
- ServerSkuManager.DeleteInvalidSkus()
- cloudregions := fetchSkuSyncCloudregions()
- if len(cloudregions) == 0 {
- return
- }
- meta, err := yunionmeta.FetchYunionmeta(ctx)
- if err != nil {
- log.Errorf("FetchYunionmeta %v", err)
- return
- }
- index, err := meta.Index(ServerSkuManager.Keyword())
- if err != nil {
- log.Errorf("getServerSkuIndex error: %v", err)
- return
- }
- for i := range cloudregions {
- region := &cloudregions[i]
- skuMeta := &SServerSku{}
- skuMeta.SetModelManager(ServerSkuManager, skuMeta)
- skuMeta.Id = region.ExternalId
- oldMd5 := db.Metadata.GetStringValue(ctx, skuMeta, db.SKU_METADAT_KEY, userCred)
- newMd5, ok := index[region.ExternalId]
- if !ok || newMd5 == yunionmeta.EMPTY_MD5 || len(oldMd5) > 0 && newMd5 == oldMd5 {
- continue
- }
- db.Metadata.SetValue(ctx, skuMeta, db.SKU_METADAT_KEY, newMd5, userCred)
- result := region.SyncServerSkus(ctx, userCred, false)
- notes := fmt.Sprintf("SyncServerSkusByRegion %s result: %v", region.Name, result.Result())
- log.Debugf("%s", notes)
- }
- }
- // 同步指定region sku列表
- func SyncServerSkusByRegion(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, xor bool) compare.SyncResult {
- result := compare.SyncResult{}
- result = region.SyncServerSkus(ctx, userCred, xor)
- notes := fmt.Sprintf("SyncServerSkusByRegion %s result: %v", region.Name, result.Result())
- log.Infof("%s", notes)
- return result
- }
|