mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-28 03:23:25 +08:00
769 lines
23 KiB
Go
769 lines
23 KiB
Go
package models
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/tristate"
|
|
"yunion.io/x/pkg/util/compare"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
)
|
|
|
|
const (
|
|
ZONE_INIT = "init"
|
|
ZONE_ENABLE = "enable"
|
|
ZONE_DISABLE = "disable"
|
|
ZONE_SOLDOUT = "soldout"
|
|
// ZONE_LACK = "lack"
|
|
)
|
|
|
|
type SZoneManager struct {
|
|
db.SStatusStandaloneResourceBaseManager
|
|
}
|
|
|
|
var ZoneManager *SZoneManager
|
|
|
|
func init() {
|
|
ZoneManager = &SZoneManager{
|
|
SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
|
|
SZone{},
|
|
"zones_tbl",
|
|
"zone",
|
|
"zones",
|
|
),
|
|
}
|
|
ZoneManager.NameRequireAscii = false
|
|
}
|
|
|
|
type SZone struct {
|
|
db.SStatusStandaloneResourceBase
|
|
|
|
Location string `width:"256" charset:"utf8" get:"user" list:"user" update:"admin"` // = Column(VARCHAR(256, charset='utf8'))
|
|
Contacts string `width:"256" charset:"utf8" get:"user" update:"admin"` // = Column(VARCHAR(256, charset='utf8'))
|
|
NameCn string `width:"256" charset:"utf8"` // = Column(VARCHAR(256, charset='utf8'))
|
|
// status = Column(VARCHAR(36, charset='ascii'), nullable=False, default=ZONE_DISABLE)
|
|
ManagerUri string `width:"256" charset:"ascii" list:"admin" update:"admin"` // = Column(VARCHAR(256, charset='ascii'), nullable=True)
|
|
// admin_id = Column(VARCHAR(36, charset='ascii'), nullable=False)
|
|
CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"admin_required"`
|
|
}
|
|
|
|
func (manager *SZoneManager) GetContextManager() []db.IModelManager {
|
|
return []db.IModelManager{CloudregionManager}
|
|
}
|
|
|
|
func (self *SZoneManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
|
return db.IsAdminAllowCreate(userCred, self)
|
|
}
|
|
|
|
func (self *SZone) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return db.IsAdminAllowGet(userCred, self)
|
|
}
|
|
|
|
func (self *SZone) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
|
|
return db.IsAdminAllowUpdate(userCred, self)
|
|
}
|
|
|
|
func (self *SZone) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
|
return db.IsAdminAllowDelete(userCred, self)
|
|
}
|
|
|
|
func (manager *SZoneManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return true
|
|
}
|
|
|
|
func (zone *SZone) ValidateDeleteCondition(ctx context.Context) error {
|
|
usage := zone.GeneralUsage()
|
|
if !usage.isEmpty() {
|
|
return httperrors.NewNotEmptyError("not empty zone")
|
|
}
|
|
return zone.SStandaloneResourceBase.ValidateDeleteCondition(ctx)
|
|
}
|
|
|
|
/*
|
|
@classmethod
|
|
def tenant_id_hash(cls, tenant_id, mod):
|
|
intval = 0
|
|
for i in range(len(tenant_id)):
|
|
intval += ord(tenant_id[i])
|
|
return intval % mod
|
|
|
|
@classmethod
|
|
def get_hashed_zone_id(cls, tenant_id, excludes=None):
|
|
from clouds.models.hosts import Hosts
|
|
q = Hosts.query(Hosts.zone_id, func.count('*')) \
|
|
.filter(Hosts.enabled==True) \
|
|
.filter(Hosts.host_status==Hosts.HOST_ONLINE)
|
|
if excludes is not None and len(excludes) > 0:
|
|
q = q.filter(not_(Hosts.zone_id.in_(excludes)))
|
|
q = q.group_by(Hosts.zone_id).all()
|
|
zones = []
|
|
weights = {}
|
|
for (zone_id, weight) in q:
|
|
zones.append(zone_id)
|
|
weights[zone_id] = weight
|
|
ring = HashRing(zones, weights)
|
|
return ring.get_node(tenant_id)
|
|
|
|
|
|
def is_zone_manageable(self):
|
|
if self.manager_uri is not None and len(self.manager_uri) > 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def request(self, url, on_succ, on_fail, user_cred, **kwargs):
|
|
headers = {}
|
|
headers['X-Auth-Token'] = user_cred.token
|
|
zoneclient.get_client().request(self, 'GET', url, headers, \
|
|
on_succ, on_fail, **kwargs)
|
|
|
|
*/
|
|
|
|
func (manager *SZoneManager) Count() int {
|
|
return manager.Query().Count()
|
|
}
|
|
|
|
type ZoneGeneralUsage struct {
|
|
Hosts int
|
|
HostsEnabled int
|
|
Baremetals int
|
|
BaremetalsEnabled int
|
|
Wires int
|
|
Networks int
|
|
Storages int
|
|
}
|
|
|
|
func (usage *ZoneGeneralUsage) isEmpty() bool {
|
|
if usage.Hosts > 0 {
|
|
return false
|
|
}
|
|
if usage.Wires > 0 {
|
|
return false
|
|
}
|
|
if usage.Networks > 0 {
|
|
return false
|
|
}
|
|
if usage.Storages > 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (zone *SZone) GeneralUsage() ZoneGeneralUsage {
|
|
usage := ZoneGeneralUsage{}
|
|
usage.Hosts = zone.HostCount("", "", tristate.None, "", tristate.None)
|
|
usage.HostsEnabled = zone.HostCount("", "", tristate.True, "", tristate.None)
|
|
usage.Baremetals = zone.HostCount("", "", tristate.None, "", tristate.True)
|
|
usage.BaremetalsEnabled = zone.HostCount("", "", tristate.True, "", tristate.True)
|
|
usage.Wires = zone.getWireCount()
|
|
usage.Networks = zone.getNetworkCount()
|
|
usage.Storages = zone.getStorageCount()
|
|
return usage
|
|
}
|
|
|
|
func (zone *SZone) HostCount(status string, hostStatus string, enabled tristate.TriState, hostType string, isBaremetal tristate.TriState) int {
|
|
q := HostManager.Query().Equals("zone_id", zone.Id)
|
|
if len(status) > 0 {
|
|
q = q.Equals("status", status)
|
|
}
|
|
if len(hostStatus) > 0 {
|
|
q = q.Equals("host_status", hostStatus)
|
|
}
|
|
if enabled.IsTrue() {
|
|
q = q.IsTrue("enabled")
|
|
} else if enabled.IsFalse() {
|
|
q = q.IsFalse("enabled")
|
|
}
|
|
if len(hostType) > 0 {
|
|
q = q.Equals("host_type", hostType)
|
|
}
|
|
if isBaremetal.IsTrue() {
|
|
q = q.IsTrue("is_baremetal")
|
|
} else if isBaremetal.IsFalse() {
|
|
q = q.IsFalse("is_baremetal")
|
|
}
|
|
return q.Count()
|
|
}
|
|
|
|
func (zone *SZone) getWireCount() int {
|
|
q := WireManager.Query().Equals("zone_id", zone.Id)
|
|
return q.Count()
|
|
}
|
|
|
|
func (zone *SZone) getStorageCount() int {
|
|
q := StorageManager.Query().Equals("zone_id", zone.Id)
|
|
return q.Count()
|
|
}
|
|
|
|
func (zone *SZone) getNetworkCount() int {
|
|
return getNetworkCount(zone)
|
|
}
|
|
|
|
/*def host_count(self, status=None, host_status=None, enabled=None, host_type=None, is_baremetal=None):
|
|
from hosts import Hosts
|
|
q = Hosts.query().filter(Hosts.zone_id==self.id)
|
|
if status is not None:
|
|
q = q.filter(Hosts.status==status)
|
|
if host_status is not None:
|
|
q = q.filter(Hosts.host_status==host_status)
|
|
if enabled is not None:
|
|
q = q.filter(Hosts.enabled==enabled)
|
|
if host_type is not None:
|
|
q = q.filter(Hosts.host_type==host_type)
|
|
if is_baremetal is not None:
|
|
q = q.filter(Hosts.is_baremetal==is_baremetal)
|
|
return q.count()
|
|
|
|
def wire_count(self):
|
|
from wires import Wires
|
|
q = Wires.query().filter(Wires.zone_id==self.id)
|
|
return q.count()
|
|
*/
|
|
|
|
/*
|
|
|
|
@classmethod
|
|
def get_resident_zones_of_tenant(cls, tenant_id):
|
|
from guests import Guests
|
|
from hosts import Hosts
|
|
q = Zones.query(Zones.id, func.count('*')) \
|
|
.join(Hosts, and_(Hosts.zone_id==Zones.id,
|
|
Hosts.deleted==False,
|
|
Hosts.enabled==True,
|
|
Hosts.host_status==Hosts.HOST_ONLINE)) \
|
|
.join(Guests, and_(Guests.host_id==Hosts.id,
|
|
Guests.deleted==False)) \
|
|
.filter(Guests.tenant_id==tenant_id) \
|
|
.group_by(Zones.id)
|
|
ret = []
|
|
for (zone_id, guest_cnt) in q.all():
|
|
ret.append((Zones.fetch(zone_id), guest_cnt))
|
|
return ret
|
|
|
|
@classmethod
|
|
def get_default_schedule_zone_of_tenant(cls, user_cred, tenant_id, params,
|
|
count=1):
|
|
from guests import Guests
|
|
zones = cls.get_resident_zones_of_tenant(tenant_id)
|
|
if len(zones) == 0:
|
|
if options.default_zone is not None:
|
|
zone = Zones.fetch(options.default_zone)
|
|
ret = Guests.check_resource_limit(user_cred, params, count,
|
|
range_obj=zone)
|
|
if len(ret) > 0:
|
|
logging.error('Default zone out of resources')
|
|
return None
|
|
else:
|
|
return zone
|
|
else:
|
|
sel_z = None
|
|
max_cnt = 0
|
|
for (z, gcnt) in zones:
|
|
ret = Guests.check_resource_limit(user_cred, params, count,
|
|
range_obj=z)
|
|
if len(ret) == 0:
|
|
if max_cnt < gcnt:
|
|
sel_z = z
|
|
max_cnt = gcnt
|
|
return sel_z
|
|
|
|
def get_wires(self):
|
|
from wires import Wires
|
|
q = Wires.query().filter(Wires.zone_id==self.id)
|
|
return q.all()
|
|
|
|
def get_name_cn(self):
|
|
return self.name_cn or self.name
|
|
|
|
|
|
def get_more_details(self):
|
|
ret = {}
|
|
ret.update(self.general_usage(None))
|
|
return ret
|
|
|
|
def get_customize_columns(self, user_cred, kwargs):
|
|
ret = super(Zones, self).get_customize_columns(user_cred, kwargs)
|
|
ret.update(self.get_more_details())
|
|
return ret
|
|
|
|
def get_details(self, user_cred, kwargs):
|
|
ret = super(Zones, self).get_details(user_cred, kwargs)
|
|
ret.update(self.get_more_details())
|
|
return ret
|
|
|
|
*/
|
|
|
|
func zoneExtra(zone *SZone, extra *jsonutils.JSONDict) *jsonutils.JSONDict {
|
|
usage := zone.GeneralUsage()
|
|
extra.Update(jsonutils.Marshal(usage))
|
|
region := zone.GetRegion()
|
|
if region != nil {
|
|
extra.Add(jsonutils.NewString(region.Name), "cloudregion")
|
|
}
|
|
return extra
|
|
}
|
|
|
|
func (zone *SZone) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
|
|
extra := zone.SStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query)
|
|
return zoneExtra(zone, extra)
|
|
}
|
|
|
|
func (zone *SZone) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
|
|
extra, err := zone.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return zoneExtra(zone, extra), nil
|
|
}
|
|
|
|
func (zone *SZone) GetCloudRegionId() string {
|
|
if len(zone.CloudregionId) == 0 {
|
|
return "default"
|
|
} else {
|
|
return zone.CloudregionId
|
|
}
|
|
}
|
|
|
|
func (manager *SZoneManager) GetZonesByRegion(region *SCloudregion) ([]SZone, error) {
|
|
zones := make([]SZone, 0)
|
|
q := manager.Query().Equals("cloudregion_id", region.Id)
|
|
err := db.FetchModelObjects(manager, q, &zones)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return zones, nil
|
|
}
|
|
|
|
func (manager *SZoneManager) SyncZones(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, zones []cloudprovider.ICloudZone) ([]SZone, []cloudprovider.ICloudZone, compare.SyncResult) {
|
|
localZones := make([]SZone, 0)
|
|
remoteZones := make([]cloudprovider.ICloudZone, 0)
|
|
syncResult := compare.SyncResult{}
|
|
|
|
dbZones, err := manager.GetZonesByRegion(region)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return nil, nil, syncResult
|
|
}
|
|
|
|
removed := make([]SZone, 0)
|
|
commondb := make([]SZone, 0)
|
|
commonext := make([]cloudprovider.ICloudZone, 0)
|
|
added := make([]cloudprovider.ICloudZone, 0)
|
|
|
|
err = compare.CompareSets(dbZones, zones, &removed, &commondb, &commonext, &added)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return nil, nil, syncResult
|
|
}
|
|
|
|
for i := 0; i < len(removed); i += 1 {
|
|
err = removed[i].ValidateDeleteCondition(ctx)
|
|
if err != nil { // cannot delete
|
|
err = removed[i].SetStatus(userCred, ZONE_DISABLE, "sync to delete")
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
} else {
|
|
err = removed[i].Delete(ctx, userCred)
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}
|
|
}
|
|
for i := 0; i < len(commondb); i += 1 {
|
|
err = commondb[i].syncWithCloudZone(commonext[i], region)
|
|
if err != nil {
|
|
syncResult.UpdateError(err)
|
|
} else {
|
|
localZones = append(localZones, commondb[i])
|
|
remoteZones = append(remoteZones, commonext[i])
|
|
syncResult.Update()
|
|
}
|
|
}
|
|
for i := 0; i < len(added); i += 1 {
|
|
new, err := manager.newFromCloudZone(added[i], region)
|
|
if err != nil {
|
|
syncResult.AddError(err)
|
|
} else {
|
|
localZones = append(localZones, *new)
|
|
remoteZones = append(remoteZones, added[i])
|
|
syncResult.Add()
|
|
}
|
|
}
|
|
|
|
return localZones, remoteZones, syncResult
|
|
}
|
|
|
|
func (self *SZone) syncWithCloudZone(extZone cloudprovider.ICloudZone, region *SCloudregion) error {
|
|
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
|
|
self.Name = extZone.GetName()
|
|
self.Status = extZone.GetStatus()
|
|
|
|
self.IsEmulated = extZone.IsEmulated()
|
|
|
|
self.CloudregionId = region.Id
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Errorf("syncWithCloudZone error %s", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (manager *SZoneManager) newFromCloudZone(extZone cloudprovider.ICloudZone, region *SCloudregion) (*SZone, error) {
|
|
zone := SZone{}
|
|
zone.SetModelManager(manager)
|
|
|
|
zone.Name = extZone.GetName()
|
|
zone.Status = extZone.GetStatus()
|
|
zone.ExternalId = extZone.GetGlobalId()
|
|
|
|
zone.IsEmulated = extZone.IsEmulated()
|
|
|
|
zone.CloudregionId = region.Id
|
|
|
|
err := manager.TableSpec().Insert(&zone)
|
|
if err != nil {
|
|
log.Errorf("newFromCloudZone fail %s", err)
|
|
return nil, err
|
|
}
|
|
return &zone, nil
|
|
}
|
|
|
|
func (manager *SZoneManager) FetchZoneById(zoneId string) *SZone {
|
|
zoneObj, err := manager.FetchById(zoneId)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return nil
|
|
}
|
|
return zoneObj.(*SZone)
|
|
}
|
|
|
|
func (zone *SZone) GetRegion() *SCloudregion {
|
|
return CloudregionManager.FetchRegionById(zone.GetCloudRegionId())
|
|
}
|
|
|
|
func (manager *SZoneManager) InitializeData() error {
|
|
// set cloudregion ID
|
|
zones := make([]SZone, 0)
|
|
q := manager.Query()
|
|
err := db.FetchModelObjects(manager, q, &zones)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, z := range zones {
|
|
if len(z.CloudregionId) == 0 {
|
|
manager.TableSpec().Update(&z, func() error {
|
|
z.CloudregionId = "default"
|
|
return nil
|
|
})
|
|
}
|
|
if z.Status == ZONE_INIT || z.Status == ZONE_DISABLE {
|
|
manager.TableSpec().Update(&z, func() error {
|
|
z.Status = ZONE_ENABLE
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
Query 1:
|
|
vpc.manager_id is not empty && wire.zone_id is not empty
|
|
*/
|
|
func (manager *SZoneManager) usableZoneQ1(providers, vpcs, wires, networks *sqlchemy.SSubQuery, usableNet, usableVpc bool) *sqlchemy.SSubQuery {
|
|
// join tables
|
|
sq := wires.Query(sqlchemy.DISTINCT("zone_id", wires.Field("zone_id")))
|
|
if usableNet {
|
|
sq = sq.Join(networks, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id")))
|
|
}
|
|
sq = sq.Join(vpcs, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id")))
|
|
sq = sq.Join(providers, sqlchemy.Equals(vpcs.Field("manager_id"), providers.Field("id")))
|
|
|
|
// add filters
|
|
if usableNet {
|
|
sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), NETWORK_STATUS_AVAILABLE))
|
|
}
|
|
sq = sq.Filter(sqlchemy.IsNotEmpty(wires.Field("zone_id")))
|
|
sq = sq.Filter(sqlchemy.IsTrue(providers.Field("enabled")))
|
|
sq = sq.Filter(sqlchemy.In(providers.Field("status"), CLOUD_PROVIDER_VALID_STATUS))
|
|
if usableVpc {
|
|
sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), VPC_STATUS_AVAILABLE))
|
|
}
|
|
|
|
return sq.SubQuery()
|
|
}
|
|
|
|
/*
|
|
Query 2:
|
|
vpc.manager_id is empty && wire.zone_id is not empty
|
|
*/
|
|
func (manager *SZoneManager) usableZoneQ2(vpcs, wires, networks *sqlchemy.SSubQuery, usableNet, usableVpc bool) *sqlchemy.SSubQuery {
|
|
// join tables
|
|
sq := wires.Query(sqlchemy.DISTINCT("zone_id", wires.Field("zone_id")))
|
|
if usableNet {
|
|
sq = sq.Join(networks, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id")))
|
|
}
|
|
sq = sq.Join(vpcs, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id")))
|
|
|
|
// add filters
|
|
if usableNet {
|
|
sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), NETWORK_STATUS_AVAILABLE))
|
|
}
|
|
sq = sq.Filter(sqlchemy.IsNotEmpty(wires.Field("zone_id")))
|
|
sq = sq.Filter(sqlchemy.IsNullOrEmpty(vpcs.Field("manager_id")))
|
|
if usableVpc {
|
|
sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), VPC_STATUS_AVAILABLE))
|
|
}
|
|
|
|
return sq.SubQuery()
|
|
}
|
|
|
|
/*
|
|
Query 3:
|
|
vpc.manager_id is not empty && wire.zone_id is empty
|
|
|
|
2019.01.17 目前华为云子网在整个region 可用。wire中zone_id留空。
|
|
*/
|
|
func (manager *SZoneManager) usableZoneQ3(providers, vpcs, wires, networks, zones *sqlchemy.SSubQuery, usableNet, usableVpc bool) *sqlchemy.SSubQuery {
|
|
// join tables
|
|
sq := zones.Query(sqlchemy.DISTINCT("zone_id", zones.Field("id")))
|
|
sq = sq.Join(vpcs, sqlchemy.Equals(zones.Field("cloudregion_id"), vpcs.Field("cloudregion_id")))
|
|
sq = sq.Join(wires, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id")))
|
|
if usableNet {
|
|
sq = sq.Join(networks, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id")))
|
|
}
|
|
sq = sq.Join(providers, sqlchemy.Equals(vpcs.Field("manager_id"), providers.Field("id")))
|
|
|
|
// add filters
|
|
if usableNet {
|
|
sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), NETWORK_STATUS_AVAILABLE))
|
|
}
|
|
sq = sq.Filter(sqlchemy.IsNullOrEmpty(wires.Field("zone_id")))
|
|
sq = sq.Filter(sqlchemy.IsTrue(providers.Field("enabled")))
|
|
sq = sq.Filter(sqlchemy.In(providers.Field("status"), CLOUD_PROVIDER_VALID_STATUS))
|
|
if usableVpc {
|
|
sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), VPC_STATUS_AVAILABLE))
|
|
}
|
|
|
|
return sq.SubQuery()
|
|
}
|
|
|
|
/*
|
|
Query 4:
|
|
vpc.manager_id is empty && wire.zone_id is empty
|
|
|
|
2019.01.17 目前华为云子网在整个region 可用。wire中zone_id留空。
|
|
*/
|
|
func (manager *SZoneManager) usableZoneQ4(vpcs, wires, networks, zones *sqlchemy.SSubQuery, usableNet, usableVpc bool) *sqlchemy.SSubQuery {
|
|
// join tables
|
|
sq := zones.Query(sqlchemy.DISTINCT("zone_id", zones.Field("id")))
|
|
sq = sq.Join(vpcs, sqlchemy.Equals(zones.Field("cloudregion_id"), vpcs.Field("cloudregion_id")))
|
|
sq = sq.Join(wires, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id")))
|
|
if usableNet {
|
|
sq = sq.Join(networks, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id")))
|
|
}
|
|
|
|
// add filters
|
|
if usableNet {
|
|
sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), NETWORK_STATUS_AVAILABLE))
|
|
}
|
|
sq = sq.Filter(sqlchemy.IsNullOrEmpty(wires.Field("zone_id")))
|
|
sq = sq.Filter(sqlchemy.IsNullOrEmpty(vpcs.Field("manager_id")))
|
|
if usableVpc {
|
|
sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), VPC_STATUS_AVAILABLE))
|
|
}
|
|
|
|
return sq.SubQuery()
|
|
}
|
|
|
|
func (manager *SZoneManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
|
|
q, err := manager.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if jsonutils.QueryBoolean(query, "is_private", false) || jsonutils.QueryBoolean(query, "private", false) || jsonutils.QueryBoolean(query, "private_cloud", false) {
|
|
regions := CloudregionManager.Query().SubQuery()
|
|
subq := regions.Query(regions.Field("id"))
|
|
subq = subq.Filter(sqlchemy.OR(
|
|
sqlchemy.In(regions.Field("provider"), cloudprovider.GetPrivateProviders()),
|
|
sqlchemy.IsNullOrEmpty(regions.Field("provider")),
|
|
))
|
|
q = q.In("cloudregion_id", subq.SubQuery())
|
|
}
|
|
if jsonutils.QueryBoolean(query, "is_public", false) || jsonutils.QueryBoolean(query, "public", false) || jsonutils.QueryBoolean(query, "public_cloud", false) {
|
|
regions := CloudregionManager.Query().SubQuery()
|
|
subq := regions.Query(regions.Field("id"))
|
|
subq = subq.Filter(sqlchemy.In(regions.Field("provider"), cloudprovider.GetPublicProviders()))
|
|
q = q.In("cloudregion_id", subq.SubQuery())
|
|
}
|
|
if jsonutils.QueryBoolean(query, "is_on_premise", false) {
|
|
regions := CloudregionManager.Query().SubQuery()
|
|
subq := regions.Query(regions.Field("id"))
|
|
subq = subq.Filter(sqlchemy.OR(
|
|
sqlchemy.In(regions.Field("provider"), cloudprovider.GetOnPremiseProviders()),
|
|
sqlchemy.IsNullOrEmpty(regions.Field("provider")),
|
|
))
|
|
q = q.In("cloudregion_id", subq.SubQuery())
|
|
}
|
|
if jsonutils.QueryBoolean(query, "is_managed", false) {
|
|
q = q.IsNotEmpty("external_id")
|
|
}
|
|
|
|
if jsonutils.QueryBoolean(query, "usable", false) || jsonutils.QueryBoolean(query, "usable_vpc", false) {
|
|
usableNet := jsonutils.QueryBoolean(query, "usable", false)
|
|
usableVpc := jsonutils.QueryBoolean(query, "usable_vpc", false)
|
|
|
|
sq1 := manager.usableZoneQ1(
|
|
CloudproviderManager.Query().SubQuery(),
|
|
VpcManager.Query().SubQuery(),
|
|
WireManager.Query().SubQuery(),
|
|
NetworkManager.Query().SubQuery(),
|
|
usableNet, usableVpc)
|
|
|
|
sq2 := manager.usableZoneQ2(
|
|
VpcManager.Query().SubQuery(),
|
|
WireManager.Query().SubQuery(),
|
|
NetworkManager.Query().SubQuery(),
|
|
usableNet, usableVpc)
|
|
|
|
sq3 := manager.usableZoneQ3(
|
|
CloudproviderManager.Query().SubQuery(),
|
|
VpcManager.Query().SubQuery(),
|
|
WireManager.Query().SubQuery(),
|
|
NetworkManager.Query().SubQuery(),
|
|
ZoneManager.Query().SubQuery(),
|
|
usableNet, usableVpc)
|
|
|
|
sq4 := manager.usableZoneQ4(
|
|
VpcManager.Query().SubQuery(),
|
|
WireManager.Query().SubQuery(),
|
|
NetworkManager.Query().SubQuery(),
|
|
ZoneManager.Query().SubQuery(),
|
|
usableNet, usableVpc)
|
|
|
|
q = q.Filter(sqlchemy.OR(
|
|
sqlchemy.In(q.Field("id"), sq1),
|
|
sqlchemy.In(q.Field("id"), sq2),
|
|
sqlchemy.In(q.Field("id"), sq3),
|
|
sqlchemy.In(q.Field("id"), sq4),
|
|
))
|
|
q = q.Equals("status", ZONE_ENABLE)
|
|
}
|
|
|
|
managerStr, _ := query.GetString("manager")
|
|
if len(managerStr) > 0 {
|
|
providerObj, err := CloudproviderManager.FetchByIdOrName(userCred, managerStr)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, httperrors.NewResourceNotFoundError2(CloudproviderManager.Keyword(), managerStr)
|
|
} else {
|
|
return nil, httperrors.NewGeneralError(err)
|
|
}
|
|
}
|
|
provider := providerObj.(*SCloudprovider)
|
|
subq := CloudregionManager.Query("id").Equals("provider", provider.Provider).SubQuery()
|
|
q = q.In("cloudregion_id", subq)
|
|
}
|
|
accountStr, _ := query.GetString("account")
|
|
if len(accountStr) > 0 {
|
|
accountObj, err := CloudaccountManager.FetchByIdOrName(userCred, accountStr)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, httperrors.NewResourceNotFoundError2(CloudaccountManager.Keyword(), accountStr)
|
|
} else {
|
|
return nil, httperrors.NewGeneralError(err)
|
|
}
|
|
}
|
|
account := accountObj.(*SCloudaccount)
|
|
subq := CloudregionManager.Query("id").Equals("provider", account.Provider).SubQuery()
|
|
q = q.In("cloudregion_id", subq)
|
|
}
|
|
providerStr, _ := query.GetString("provider")
|
|
if len(providerStr) > 0 {
|
|
subq := CloudregionManager.Query("id").Equals("provider", providerStr).SubQuery()
|
|
q = q.In("cloudregion_id", subq)
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
func (self *SZone) AllowGetDetailsCapability(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
|
return true
|
|
}
|
|
|
|
func (self *SZone) GetDetailsCapability(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
capa, err := GetCapabilities(ctx, userCred, query, self)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return jsonutils.Marshal(&capa), nil
|
|
}
|
|
|
|
func (self *SZone) isManaged() bool {
|
|
region := self.GetRegion()
|
|
if region != nil && len(region.ExternalId) == 0 {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (self *SZone) isSchedPolicySupported() bool {
|
|
return !self.isManaged()
|
|
}
|
|
|
|
func (self *SZone) getMinNicCount() int {
|
|
return 1
|
|
}
|
|
|
|
func (self *SZone) getMaxNicCount() int {
|
|
if self.isManaged() {
|
|
return 1
|
|
} else {
|
|
return 8
|
|
}
|
|
}
|
|
|
|
func (self *SZone) getMinDataDiskCount() int {
|
|
return 0
|
|
}
|
|
|
|
func (self *SZone) getMaxDataDiskCount() int {
|
|
return 6
|
|
}
|
|
|
|
func (manager *SZoneManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
regionStr := jsonutils.GetAnyString(data, []string{"region", "region_id", "cloudregion", "cloudregion_id"})
|
|
var regionId string
|
|
if len(regionStr) > 0 {
|
|
regionObj, err := CloudregionManager.FetchByIdOrName(nil, regionStr)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, httperrors.NewResourceNotFoundError("Region %s not found", regionStr)
|
|
} else {
|
|
return nil, httperrors.NewInternalServerError("Query region %s fail %s", regionStr, err)
|
|
}
|
|
}
|
|
regionId = regionObj.GetId()
|
|
} else {
|
|
regionId = "default"
|
|
}
|
|
data.Add(jsonutils.NewString(regionId), "cloudregion_id")
|
|
data.Set("status", jsonutils.NewString(ZONE_ENABLE))
|
|
return manager.SStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data)
|
|
}
|