diff --git a/pkg/compute/models/cachedimages.go b/pkg/compute/models/cachedimages.go index 00f06ecd7a..13ad3ea360 100644 --- a/pkg/compute/models/cachedimages.go +++ b/pkg/compute/models/cachedimages.go @@ -604,14 +604,11 @@ func (image *SCachedimage) requestRefreshExternalImage(ctx context.Context, user func (image *SCachedimage) getValidStoragecache() []SStoragecache { storagecaches := StoragecacheManager.Query().SubQuery() storagecacheimages := StoragecachedimageManager.Query().SubQuery() - providers := CloudproviderManager.Query().SubQuery() + providers := usableCloudProviders().SubQuery() q := storagecaches.Query() q = q.Join(providers, sqlchemy.Equals(providers.Field("id"), storagecaches.Field("manager_id"))) q = q.Join(storagecacheimages, sqlchemy.Equals(storagecaches.Field("id"), storagecacheimages.Field("storagecache_id"))) - q = q.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) - q = q.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - q = q.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("cachedimage_id"), image.Id)) q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE)) @@ -642,16 +639,13 @@ func (image *SCachedimage) GetUsableZoneIds() ([]string, error) { storages := StorageManager.Query().SubQuery() storagecaches := StoragecacheManager.Query().SubQuery() storagecacheimages := StoragecachedimageManager.Query().SubQuery() - providers := CloudproviderManager.Query().SubQuery() + providers := usableCloudProviders().SubQuery() q := zones.Query(zones.Field("id")) q = q.Join(storages, sqlchemy.Equals(q.Field("id"), storages.Field("zone_id"))) q = q.Join(storagecaches, sqlchemy.Equals(storages.Field("storagecache_id"), storagecaches.Field("id"))) q = q.Join(providers, sqlchemy.Equals(providers.Field("id"), storagecaches.Field("manager_id"))) q = q.Join(storagecacheimages, sqlchemy.Equals(storagecaches.Field("id"), storagecacheimages.Field("storagecache_id"))) - q = q.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) - q = q.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - q = q.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("cachedimage_id"), image.Id)) q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE)) q = q.Filter(sqlchemy.Equals(q.Field("status"), api.ZONE_ENABLE)) diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 1caac2e292..24a1d772d3 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -1177,7 +1177,7 @@ func (manager *SCloudproviderManager) ListItemFilter( } if query.Usable != nil && *query.Usable { - providers := CloudproviderManager.Query().SubQuery() + providers := usableCloudProviders().SubQuery() networks := NetworkManager.Query().SubQuery() wires := WireManager.Query().SubQuery() vpcs := VpcManager.Query().SubQuery() @@ -1188,9 +1188,6 @@ func (manager *SCloudproviderManager) ListItemFilter( sq = sq.Join(vpcs, sqlchemy.Equals(providerRegions.Field("cloudregion_id"), vpcs.Field("cloudregion_id"))) sq = sq.Join(wires, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id"))) sq = sq.Join(networks, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))) - sq = sq.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) - sq = sq.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - sq = sq.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE)) sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) sq = sq.Filter(sqlchemy.OR( diff --git a/pkg/compute/models/cloudregions.go b/pkg/compute/models/cloudregions.go index a1589373e9..a8b7cd1e55 100644 --- a/pkg/compute/models/cloudregions.go +++ b/pkg/compute/models/cloudregions.go @@ -793,7 +793,7 @@ func (manager *SCloudregionManager) ListItemFilter( usableNet := (query.Usable != nil && *query.Usable) usableVpc := (query.UsableVpc != nil && *query.UsableVpc) if usableNet || usableVpc { - providers := CloudproviderManager.Query().SubQuery() + providers := usableCloudProviders().SubQuery() networks := NetworkManager.Query().SubQuery() wires := WireManager.Query().SubQuery() vpcs := VpcManager.Query().SubQuery() @@ -807,9 +807,6 @@ func (manager *SCloudregionManager) ListItemFilter( if usableNet { sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) } - sq = sq.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) - sq = sq.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - sq = sq.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) if usableVpc { sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE)) } diff --git a/pkg/compute/models/elasticcache_skus.go b/pkg/compute/models/elasticcache_skus.go index 3c0af0e1e8..9ba46f2d41 100644 --- a/pkg/compute/models/elasticcache_skus.go +++ b/pkg/compute/models/elasticcache_skus.go @@ -276,7 +276,10 @@ func (manager *SElasticcacheSkuManager) ListItemFilter( } if query.Usable != nil && *query.Usable { - q = usableFilter(q, true) + q, err = usableFilter(q, true) + if err != nil { + return nil, err + } sq := sqlchemy.OR(sqlchemy.Equals(q.Field("prepaid_status"), api.SkuStatusAvailable), sqlchemy.Equals(q.Field("postpaid_status"), api.SkuStatusAvailable)) q = q.Filter(sq) } diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index fc23604bda..370db77171 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -317,15 +317,12 @@ func (manager *SHostManager) ListItemFilter( hosts := HostManager.Query().SubQuery() hostwires := HostwireManager.Query().SubQuery() networks := NetworkManager.Query().SubQuery() - providers := CloudproviderManager.Query().SubQuery() + providers := usableCloudProviders().SubQuery() hostQ1 := hosts.Query(hosts.Field("id")) hostQ1 = hostQ1.Join(providers, sqlchemy.Equals(hosts.Field("manager_id"), providers.Field("id"))) hostQ1 = hostQ1.Join(hostwires, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id"))) hostQ1 = hostQ1.Join(networks, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id"))) - hostQ1 = hostQ1.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) - hostQ1 = hostQ1.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - hostQ1 = hostQ1.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) hostQ1 = hostQ1.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) hostQ1 = hostQ1.Filter(sqlchemy.IsTrue(hosts.Field("enabled"))) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index fee507c8c1..dec63421cf 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -2028,33 +2028,19 @@ func (manager *SNetworkManager) ListItemFilter( } if input.Usable != nil && *input.Usable { - wires := WireManager.Query().SubQuery() - zones := ZoneManager.Query().SubQuery() - vpcs := VpcManager.Query().SubQuery() - cloudproviders := CloudproviderManager.Query().SubQuery() - providerSQ := cloudproviders.Query(cloudproviders.Field("id")).Filter( - sqlchemy.AND( - sqlchemy.IsTrue(cloudproviders.Field("enabled")), - sqlchemy.In(cloudproviders.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS), - sqlchemy.In(cloudproviders.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS), - ), - ) - regions := CloudregionManager.Query().SubQuery() + regions := CloudregionManager.Query("id").Equals("status", api.CLOUD_REGION_STATUS_INSERVER) + zones := ZoneManager.Query("id").Equals("status", api.ZONE_ENABLE).In("cloudregion_id", regions) + providerSQ := usableCloudProviders() + _vpcs := VpcManager.Query("id").Equals("status", api.VPC_STATUS_AVAILABLE) + vpcs := _vpcs.Filter(sqlchemy.OR( + sqlchemy.In(_vpcs.Field("manager_id"), providerSQ), + sqlchemy.IsNullOrEmpty(_vpcs.Field("manager_id")), + )) - sq := wires.Query(wires.Field("id")). - Join(vpcs, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id"))). - Join(zones, sqlchemy.OR(sqlchemy.Equals(wires.Field("zone_id"), zones.Field("id")), sqlchemy.IsNullOrEmpty(wires.Field("zone_id")))). - Join(regions, sqlchemy.Equals(zones.Field("cloudregion_id"), regions.Field("id"))). - Filter(sqlchemy.AND( - sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE), - sqlchemy.Equals(zones.Field("status"), api.ZONE_ENABLE), - sqlchemy.Equals(regions.Field("status"), api.CLOUD_REGION_STATUS_INSERVER), - sqlchemy.OR( - sqlchemy.In(vpcs.Field("manager_id"), providerSQ.SubQuery()), - sqlchemy.IsNullOrEmpty(vpcs.Field("manager_id")), - ), - )) - q = q.In("wire_id", sq.SubQuery()).Equals("status", api.NETWORK_STATUS_AVAILABLE) + wires := WireManager.Query("id") + wires = wires.In("vpc_id", vpcs). + Filter(sqlchemy.OR(sqlchemy.IsNullOrEmpty(wires.Field("zone_id")), sqlchemy.In(wires.Field("zone_id"), zones))) + q = q.In("wire_id", wires).Equals("status", api.NETWORK_STATUS_AVAILABLE) } hostStr := input.HostId diff --git a/pkg/compute/models/skus.go b/pkg/compute/models/skus.go index 5642af8601..e4cbd0fd76 100644 --- a/pkg/compute/models/skus.go +++ b/pkg/compute/models/skus.go @@ -494,53 +494,36 @@ func normalizeProvider(provider string) string { } func networkUsableRegionQueries(f sqlchemy.IQueryField) []sqlchemy.ICondition { - iconditions := make([]sqlchemy.ICondition, 0) - providers := CloudproviderManager.Query().SubQuery() - networks := NetworkManager.Query().SubQuery() - wires := WireManager.Query().SubQuery() - vpcs := VpcManager.Query().SubQuery() - + 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"))) - sq = sq.Join(wires, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id"))) - 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"))) - sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) - sq = sq.Filter(sqlchemy.IsTrue(providers.Field("enabled"))) - sq = sq.Filter(sqlchemy.In(providers.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - sq = sq.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) - sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE)) - - sq2 := vpcs.Query(sqlchemy.DISTINCT("cloudregion_id", vpcs.Field("cloudregion_id"))) - sq2 = sq2.Join(wires, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id"))) - sq2 = sq2.Join(networks, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))) - sq2 = sq2.Filter(sqlchemy.Equals(networks.Field("status"), api.NETWORK_STATUS_AVAILABLE)) - sq2 = sq2.Filter(sqlchemy.IsNullOrEmpty(vpcs.Field("manager_id"))) - sq2 = sq2.Filter(sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE)) - - iconditions = append(iconditions, sqlchemy.In(f, sq.SubQuery())) - iconditions = append(iconditions, sqlchemy.In(f, sq2.SubQuery())) - return iconditions + return []sqlchemy.ICondition{sqlchemy.In(f, sq.SubQuery())} } -func usableFilter(q *sqlchemy.SQuery, public_cloud bool) *sqlchemy.SQuery { +func usableFilter(q *sqlchemy.SQuery, public_cloud bool) (*sqlchemy.SQuery, error) { // 过滤出公有云provider状态健康的sku if public_cloud { - providerTable := CloudproviderManager.Query().SubQuery() + 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"))) - subq = subq.Filter(sqlchemy.IsTrue(providerTable.Field("enabled"))) - subq = subq.Filter(sqlchemy.In(providerTable.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)) - subq = subq.Filter(sqlchemy.In(providerTable.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) q = q.Filter(sqlchemy.In(q.Field("cloudregion_id"), subq.SubQuery())) } // 过滤出network usable的sku if public_cloud { - iconditions := NetworkUsableZoneQueries(q.Field("zone_id"), true, true) - iconditions = append(iconditions, sqlchemy.IsNullOrEmpty(q.Field("zone_id"))) //Azure的zone_id可能为空 - q = q.Filter(sqlchemy.OR(iconditions...)) + 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")) @@ -549,7 +532,7 @@ func usableFilter(q *sqlchemy.SQuery, public_cloud bool) *sqlchemy.SQuery { q = q.Filter(sqlchemy.OR(iconditions...)) } - return q + return q, nil } func (manager *SServerSkuManager) GetPropertyInstanceSpecs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { @@ -772,7 +755,10 @@ func (manager *SServerSkuManager) ListItemFilter( } if query.Usable != nil && *query.Usable { - q = usableFilter(q, publicCloud) + q, err := usableFilter(q, publicCloud) + if err != nil { + return nil, err + } q = q.IsTrue("enabled") } diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 16f2bfd9ba..519a4697d5 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -1447,13 +1447,10 @@ func (manager *SStorageManager) ListItemFilter( Filter(sqlchemy.IsTrue(hostTable.Field("enabled"))). Filter(sqlchemy.IsNullOrEmpty(hostTable.Field("manager_id"))) - providerTable := CloudproviderManager.Query().SubQuery() + providerTable := usableCloudProviders().SubQuery() sq2 := hostStorageTable.Query(hostStorageTable.Field("storage_id")). Join(hostTable, sqlchemy.Equals(hostTable.Field("id"), hostStorageTable.Field("host_id"))). - Join(providerTable, sqlchemy.Equals(hostTable.Field("manager_id"), providerTable.Field("id"))). - Filter(sqlchemy.IsTrue(providerTable.Field("enabled"))). - Filter(sqlchemy.In(providerTable.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS)). - Filter(sqlchemy.In(providerTable.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) + Join(providerTable, sqlchemy.Equals(hostTable.Field("manager_id"), providerTable.Field("id"))) q = q.Filter( sqlchemy.OR( diff --git a/pkg/compute/models/usable_filters.go b/pkg/compute/models/usable_filters.go new file mode 100644 index 0000000000..8876c04d01 --- /dev/null +++ b/pkg/compute/models/usable_filters.go @@ -0,0 +1,14 @@ +package models + +import ( + "yunion.io/x/sqlchemy" + + api "yunion.io/x/onecloud/pkg/apis/compute" +) + +func usableCloudProviders() *sqlchemy.SQuery { + return CloudproviderManager.Query("id"). + In("status", api.CLOUD_PROVIDER_VALID_STATUS). + In("health_status", api.CLOUD_PROVIDER_VALID_HEALTH_STATUS). + IsTrue("enabled") +} diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 2e1554d7e5..60402d0a46 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -1109,19 +1109,12 @@ func (manager *SVpcManager) ListItemFilter( vpcUsable := (query.UsableVpc != nil && *query.UsableVpc) if vpcUsable || usable { regions := CloudregionManager.Query().SubQuery() - cloudproviders := CloudproviderManager.Query().SubQuery() - providerSQ := cloudproviders.Query(cloudproviders.Field("id")).Filter( - sqlchemy.AND( - sqlchemy.IsTrue(cloudproviders.Field("enabled")), - sqlchemy.In(cloudproviders.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS), - sqlchemy.In(cloudproviders.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS), - ), - ) + providerSQ := usableCloudProviders().SubQuery() q = q.Join(regions, sqlchemy.Equals(q.Field("cloudregion_id"), regions.Field("id"))).Filter( sqlchemy.AND( sqlchemy.Equals(regions.Field("status"), api.CLOUD_REGION_STATUS_INSERVER), sqlchemy.OR( - sqlchemy.In(q.Field("manager_id"), providerSQ.SubQuery()), + sqlchemy.In(q.Field("manager_id"), providerSQ), sqlchemy.IsNullOrEmpty(q.Field("manager_id")), ), ), diff --git a/pkg/compute/models/zones.go b/pkg/compute/models/zones.go index bb48661f9b..92f92b23fc 100644 --- a/pkg/compute/models/zones.go +++ b/pkg/compute/models/zones.go @@ -367,149 +367,210 @@ func (manager *SZoneManager) InitializeData() error { /* Query 1: -vpc.manager_id is not empty && wire.zone_id is not empty +wire.zone_id is not empty */ -func 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"), api.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"), api.CLOUD_PROVIDER_VALID_STATUS)) - sq = sq.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) - if usableVpc { - sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE)) +func usableZoneQ1(vpcs map[string]map[string]string, usableNet bool) ([]string, error) { + // conditions + vpcIds := make([]string, 0) + for k, _ := range vpcs { + vpcIds = append(vpcIds, k) } - return sq.SubQuery() + q := zoneWireFilter(usableNet, false, vpcIds) + results, _, err := zoneWireFilterResult(q) + if err != nil { + return nil, errors.Wrap(err, "zoneWireFilterResult") + } + + return results, nil } /* Query 2: -vpc.manager_id is empty && wire.zone_id is not empty -*/ -func 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"), api.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"), api.VPC_STATUS_AVAILABLE)) - } - - return sq.SubQuery() -} - -/* -Query 3: -vpc.manager_id is not empty && wire.zone_id is empty +wire.zone_id is empty 2019.01.17 目前华为云子网在整个region 可用。wire中zone_id留空。 */ -func 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"))) +func usableZoneQ2(vpcs map[string]map[string]string, usableNet bool) ([]string, error) { + // conditions + vpcIds := make([]string, 0) + for k, _ := range vpcs { + vpcIds = append(vpcIds, k) } - 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"), api.NETWORK_STATUS_AVAILABLE)) + q := zoneWireFilter(usableNet, true, vpcIds) + _, vpcIds, err := zoneWireFilterResult(q) + if err != nil { + return nil, errors.Wrap(err, "zoneWireFilterResult") } - 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"), api.CLOUD_PROVIDER_VALID_STATUS)) - sq = sq.Filter(sqlchemy.In(providers.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS)) + + cloudregionIds := make([]string, 0) + for i := range vpcIds { + if vpc, ok := vpcs[vpcIds[i]]; ok { + if c, ok := vpc["cloudregion_id"]; ok && len(c) > 0 { + cloudregionIds = append(cloudregionIds, c) + } + } + } + + return zoneRegionFilter(cloudregionIds) +} + +func zoneVpcFilter(usableVpc bool, query *api.ZoneListInput) *sqlchemy.SQuery { + // conditions + q := VpcManager.Query("id", "cloudregion_id") if usableVpc { - sq = sq.Filter(sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE)) + q = q.Equals("status", api.VPC_STATUS_AVAILABLE) } - return sq.SubQuery() + if query != nil && len(query.CloudregionId) > 0 { + q.Equals("cloudregion_id", query.CloudregionId) + } + + return q } -/* -Query 4: -vpc.manager_id is empty && wire.zone_id is empty +// vpc.manager_id is empty +func usableVpc1(usableVpc bool, query *api.ZoneListInput) *sqlchemy.SQuery { + // conditions + return zoneVpcFilter(usableVpc, query).IsNullOrEmpty("manager_id") +} + +// vpc.manager_id is not empty +func usableVpc2(usableVpc bool, query *api.ZoneListInput) (*sqlchemy.SQuery, error) { + // conditions + q := zoneVpcFilter(usableVpc, query).IsNotEmpty("manager_id") + if query != nil && len(query.CloudproviderId) > 0 { + q.Equals("manager_id", query.CloudproviderId) + } else { + providerIds, err := zoneUsableProviderQuery(query) + if err != nil { + return nil, errors.Wrap(err, "zoneUsableProviderQuery") + } + + q.In("manager_id", providerIds) + } + + return q, nil +} + +func zoneUsableVpc(usableVpc bool, query *api.ZoneListInput) (map[string]map[string]string, error) { + q1 := usableVpc1(usableVpc, query) + q2, err := usableVpc2(usableVpc, query) + if err != nil { + return nil, errors.Wrap(err, "usableVpc2") + } + results := make(map[string]map[string]string, 0) + for _, q := range []*sqlchemy.SQuery{q1, q2} { + rows, err := q.Rows() + if err != nil && err != sql.ErrNoRows { + return nil, errors.Wrap(err, "zoneUsableVpc.rows") + } + + for rows.Next() { + var id string + var cloudregion_id string + err := rows.Scan(&id, &cloudregion_id) + if err != nil { + return nil, errors.Wrap(err, "rows.Scan") + } + + results[id] = map[string]string{"id": id, "cloudregion_id": cloudregion_id} + } + } + + return results, nil +} + +func zoneWireFilter(usableNet bool, zoneIdIsEmpty bool, vpcIds []string) *sqlchemy.SQuery { + wireq := WireManager.Query("vpc_id", "zone_id").In("vpc_id", vpcIds) + if zoneIdIsEmpty { + wireq.IsNullOrEmpty("zone_id") + } else { + wireq.IsNotEmpty("zone_id") + } -2019.01.17 目前华为云子网在整个region 可用。wire中zone_id留空。 -*/ -func 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"))) + netq := NetworkManager.Query().Equals("status", api.NETWORK_STATUS_AVAILABLE).SubQuery() + wireq = wireq.Join(netq, sqlchemy.Equals(netq.Field("wire_id"), wireq.Field("id"))) } - // add filters - if usableNet { - sq = sq.Filter(sqlchemy.Equals(networks.Field("status"), api.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"), api.VPC_STATUS_AVAILABLE)) - } - - return sq.SubQuery() + return wireq } -func networkUsableZoneQueries(usableNet, usableVpc bool) []*sqlchemy.SSubQuery { - queries := make([]*sqlchemy.SSubQuery, 4) - queries[0] = usableZoneQ1(CloudproviderManager.Query().SubQuery(), - VpcManager.Query().SubQuery(), - WireManager.Query().SubQuery(), - NetworkManager.Query().SubQuery(), - usableNet, usableVpc) - queries[1] = usableZoneQ2(VpcManager.Query().SubQuery(), - WireManager.Query().SubQuery(), - NetworkManager.Query().SubQuery(), - usableNet, usableVpc) - queries[2] = usableZoneQ3(CloudproviderManager.Query().SubQuery(), - VpcManager.Query().SubQuery(), - WireManager.Query().SubQuery(), - NetworkManager.Query().SubQuery(), - ZoneManager.Query().SubQuery(), - usableNet, usableVpc) - queries[3] = usableZoneQ4(VpcManager.Query().SubQuery(), - WireManager.Query().SubQuery(), - NetworkManager.Query().SubQuery(), - ZoneManager.Query().SubQuery(), - usableNet, usableVpc) - return queries -} - -func NetworkUsableZoneQueries(field sqlchemy.IQueryField, usableNet, usableVpc bool) []sqlchemy.ICondition { - queries := networkUsableZoneQueries(usableNet, usableVpc) - iconditions := make([]sqlchemy.ICondition, 0) - for i := range queries { - iconditions = append(iconditions, sqlchemy.In(field, queries[i])) +func zoneWireFilterResult(q *sqlchemy.SQuery) ([]string, []string, error) { + rows, err := q.Rows() + if err != nil && err != sql.ErrNoRows { + return nil, nil, errors.Wrap(err, "zoneWireFilterResult.rows") + } + defer rows.Close() + zoneIds := make([]string, 0) + vpcIds := make([]string, 0) + for rows.Next() { + var vpcId string + var zoneId string + err := rows.Scan(&vpcId, &zoneId) + if err != nil { + return nil, nil, errors.Wrap(err, "rows.Scan") + } + zoneIds = append(zoneIds, zoneId) + vpcIds = append(vpcIds, vpcId) } - return iconditions + return zoneIds, vpcIds, nil +} + +// avaliable providers +func zoneUsableProviderQuery(query *api.ZoneListInput) ([]string, error) { + q := usableCloudProviders() + if query != nil { + if len(query.CloudproviderId) > 0 { + q = q.Filter(sqlchemy.OR(sqlchemy.Equals(q.Field("id"), query.CloudproviderId), sqlchemy.Equals(q.Field("name"), query.CloudproviderId))) + } + } + + return filterResult(q) +} + +func zoneRegionFilter(cloudregionIds []string) ([]string, error) { + q := ZoneManager.Query("id").In("cloudregion_id", cloudregionIds) + return filterResult(q) +} + +func filterResult(q *sqlchemy.SQuery) ([]string, error) { + rows, err := q.Rows() + if err != nil && err != sql.ErrNoRows { + return nil, errors.Wrap(err, "filterResult.rows") + } + results := make([]string, 0) + for rows.Next() { + var id string + err := rows.Scan(&id) + if err != nil { + return nil, errors.Wrap(err, "filterResult.Scan") + } + results = append(results, id) + } + + return results, nil +} + +func NetworkUsableZoneIds(usableNet, usableVpc bool, query *api.ZoneListInput) ([]string, error) { + vpcs, err := zoneUsableVpc(usableVpc, query) + if err != nil { + return nil, errors.Wrap(err, "zoneUsableVpc") + } + ret, err := usableZoneQ1(vpcs, usableNet) + if err != nil { + return nil, errors.Wrap(err, "usableZoneQ1") + } + r2, err := usableZoneQ2(vpcs, usableNet) + if err != nil { + return nil, errors.Wrap(err, "usableZoneQ2") + } + + ret = append(ret, r2...) + return ret, nil } // 可用区列表 @@ -577,8 +638,11 @@ func (manager *SZoneManager) ListItemFilter( usableNet := (query.Usable != nil && *query.Usable) usableVpc := (query.UsableVpc != nil && *query.UsableVpc) if usableNet || usableVpc { - iconditions := NetworkUsableZoneQueries(q.Field("id"), usableNet, usableVpc) - q = q.Filter(sqlchemy.OR(iconditions...)) + zoneIds, err := NetworkUsableZoneIds(usableNet, usableVpc, &query) + if err != nil { + return nil, err + } + q = q.In("id", zoneIds) q = q.Equals("status", api.ZONE_ENABLE) service := query.Service