Files
cloudpods/pkg/compute/models/wires.go
Jian Qiu 6a93723d8d fix: vpcagent sync error (#18381)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2023-10-23 09:27:23 +08:00

1851 lines
59 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"sort"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/rbacscope"
"yunion.io/x/pkg/util/sets"
"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/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SWireManager struct {
db.SStatusInfrasResourceBaseManager
db.SExternalizedResourceBaseManager
SManagedResourceBaseManager
SVpcResourceBaseManager
SZoneResourceBaseManager
}
var WireManager *SWireManager
func init() {
WireManager = &SWireManager{
SStatusInfrasResourceBaseManager: db.NewStatusInfrasResourceBaseManager(
SWire{},
"wires_tbl",
"wire",
"wires",
),
}
WireManager.SetVirtualObject(WireManager)
}
type SWire struct {
db.SStatusInfrasResourceBase
db.SExternalizedResourceBase
SManagedResourceBase
SVpcResourceBase `wdith:"36" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:""`
SZoneResourceBase `width:"36" charset:"ascii" nullable:"true" list:"domain" create:"domain_required" update:""`
// 带宽大小, 单位Mbps
// example: 1000
Bandwidth int `list:"domain" update:"domain" nullable:"false" create:"domain_required" json:"bandwidth"`
// MTU
// example: 1500
Mtu int `list:"domain" update:"domain" nullable:"false" create:"domain_optional" default:"1500" json:"mtu"`
// swagger:ignore
ScheduleRank int `list:"domain" update:"domain" json:"schedule_rank"`
// 可用区Id
// ZoneId string `width:"36" charset:"ascii" nullable:"true" list:"domain" create:"domain_required"`
// VPC Id
// VpcId string `wdith:"36" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"`
}
func (manager *SWireManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{
{ZoneManager},
{VpcManager},
}
}
func (manager *SWireManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input api.WireCreateInput,
) (api.WireCreateInput, error) {
var err error
if input.Bandwidth < 0 {
return input, httperrors.NewOutOfRangeError("bandwidth must be greater than 0")
}
if input.Mtu < 0 || input.Mtu > 1000000 {
return input, httperrors.NewOutOfRangeError("mtu must be range of 0~1000000")
}
if input.VpcId == "" {
input.VpcId = api.DEFAULT_VPC_ID
}
_vpc, err := validators.ValidateModel(userCred, VpcManager, &input.VpcId)
if err != nil {
return input, err
}
vpc := _vpc.(*SVpc)
if len(vpc.ManagerId) > 0 {
return input, httperrors.NewNotSupportedError("Currently only onpremise classic VPC supports creating wire")
}
if len(input.ZoneId) == 0 {
return input, httperrors.NewMissingParameterError("zone")
}
_, input.ZoneResourceInput, err = ValidateZoneResourceInput(userCred, input.ZoneResourceInput)
if err != nil {
return input, errors.Wrap(err, "ValidateZoneResourceInput")
}
input.StatusInfrasResourceBaseCreateInput, err = manager.SStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StatusInfrasResourceBaseCreateInput)
if err != nil {
return input, err
}
return input, nil
}
func (wire *SWire) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.WireUpdateInput) (api.WireUpdateInput, error) {
data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
keysV := []validators.IValidator{
validators.NewNonNegativeValidator("bandwidth"),
validators.NewRangeValidator("mtu", 1, 1000000).Optional(true),
}
for _, v := range keysV {
v.Optional(true)
if err := v.Validate(data); err != nil {
return input, err
}
}
var err error
input.InfrasResourceBaseUpdateInput, err = wire.SInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.InfrasResourceBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SInfrasResourceBase.ValidateUpdateData")
}
return input, nil
}
func (wire *SWire) ValidateDeleteCondition(ctx context.Context, info *api.WireDetails) error {
if gotypes.IsNil(info) {
info = &api.WireDetails{}
usage, err := WireManager.TotalResourceCount([]string{wire.Id})
if err != nil {
return err
}
info.WireUsage, _ = usage[wire.Id]
}
if info.HostCount > 0 {
return httperrors.NewNotEmptyError("wire contains hosts")
}
if info.Networks > 0 {
return httperrors.NewNotEmptyError("wire contains networks")
}
return wire.SInfrasResourceBase.ValidateDeleteCondition(ctx, nil)
}
func (manager *SWireManager) getWireExternalIdForClassicNetwork(provider string, vpcId string, zoneId string) string {
if !utils.IsInStringArray(provider, api.REGIONAL_NETWORK_PROVIDERS) {
return fmt.Sprintf("%s-%s", vpcId, zoneId)
}
return vpcId
}
func (manager *SWireManager) GetOrCreateWireForClassicNetwork(ctx context.Context, vpc *SVpc, zone *SZone) (*SWire, error) {
cloudprovider := vpc.GetCloudprovider()
if cloudprovider == nil {
return nil, fmt.Errorf("failed to found cloudprovider for vpc %s(%s)", vpc.Id, vpc.Id)
}
externalId := manager.getWireExternalIdForClassicNetwork(cloudprovider.Provider, vpc.Id, zone.Id)
name := fmt.Sprintf("emulate for vpc %s classic network", vpc.Id)
zoneId := zone.Id
if utils.IsInStringArray(cloudprovider.Provider, api.REGIONAL_NETWORK_PROVIDERS) { //reginal network
zoneId = ""
} else {
name = fmt.Sprintf("emulate for zone %s vpc %s classic network", zone.Name, vpc.Id)
}
_wire, err := db.FetchByExternalIdAndManagerId(manager, externalId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
q = q.Equals("manager_id", vpc.ManagerId)
return q
})
if err == nil {
return _wire.(*SWire), nil
}
if errors.Cause(err) != sql.ErrNoRows {
return nil, errors.Wrap(err, "db.FetchByExternalIdAndManagerId")
}
wire := &SWire{}
wire.VpcId = vpc.Id
wire.ZoneId = zoneId
wire.SetModelManager(manager, wire)
wire.ExternalId = externalId
wire.IsEmulated = true
wire.Name = name
wire.ManagerId = vpc.ManagerId
err = manager.TableSpec().Insert(ctx, wire)
if err != nil {
return nil, errors.Wrap(err, "Insert wire for classic network")
}
return wire, nil
}
func (wire *SWire) HostCount() (int, error) {
q := NetInterfaceManager.Query().Equals("wire_id", wire.Id).GroupBy("baremetal_id")
return q.CountWithError()
}
func (swire *SWire) GetHosts() ([]SHost, error) {
sq := NetInterfaceManager.Query("baremetal_id").Equals("wire_id", swire.Id)
q := HostManager.Query().In("id", sq)
hosts := []SHost{}
err := db.FetchModelObjects(HostManager, q, &hosts)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return hosts, nil
}
func (wire *SWire) NetworkCount() (int, error) {
q := NetworkManager.Query().Equals("wire_id", wire.Id)
return q.CountWithError()
}
func (wire *SWire) GetVpcId() string {
if len(wire.VpcId) == 0 {
return "default"
} else {
return wire.VpcId
}
}
func (manager *SWireManager) getWiresByVpcAndZone(vpc *SVpc, zone *SZone) ([]SWire, error) {
wires := make([]SWire, 0)
q := manager.Query()
if vpc != nil {
q = q.Equals("vpc_id", vpc.Id)
}
if zone != nil {
q = q.Equals("zone_id", zone.Id)
}
err := db.FetchModelObjects(manager, q, &wires)
if err != nil {
return nil, err
}
return wires, nil
}
func (manager *SWireManager) SyncWires(
ctx context.Context,
userCred mcclient.TokenCredential,
vpc *SVpc,
wires []cloudprovider.ICloudWire,
provider *SCloudprovider,
xor bool,
zone *SZone,
) ([]SWire, []cloudprovider.ICloudWire, compare.SyncResult) {
lockman.LockRawObject(ctx, manager.Keyword(), vpc.Id)
defer lockman.ReleaseRawObject(ctx, manager.Keyword(), vpc.Id)
localWires := make([]SWire, 0)
remoteWires := make([]cloudprovider.ICloudWire, 0)
syncResult := compare.SyncResult{}
dbWires, err := manager.getWiresByVpcAndZone(vpc, zone)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
for i := range dbWires {
if taskman.TaskManager.IsInTask(&dbWires[i]) {
syncResult.Error(fmt.Errorf("object in task"))
return nil, nil, syncResult
}
}
removed := make([]SWire, 0)
commondb := make([]SWire, 0)
commonext := make([]cloudprovider.ICloudWire, 0)
added := make([]cloudprovider.ICloudWire, 0)
err = compare.CompareSets(dbWires, wires, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
for i := 0; i < len(removed); i += 1 {
err = removed[i].syncRemoveCloudWire(ctx, userCred)
if err != nil { // cannot delete
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
for i := 0; i < len(commondb); i += 1 {
if !xor {
err = commondb[i].syncWithCloudWire(ctx, userCred, commonext[i], vpc, provider, zone)
if err != nil {
syncResult.UpdateError(err)
}
syncResult.Update()
}
localWires = append(localWires, commondb[i])
remoteWires = append(remoteWires, commonext[i])
}
for i := 0; i < len(added); i += 1 {
wire, err := manager.newFromCloudWire(ctx, userCred, added[i], vpc, provider, zone)
if err != nil {
syncResult.AddError(err)
continue
}
localWires = append(localWires, *wire)
remoteWires = append(remoteWires, added[i])
syncResult.Add()
}
return localWires, remoteWires, syncResult
}
func (swire *SWire) syncRemoveCloudWire(ctx context.Context, userCred mcclient.TokenCredential) error {
lockman.LockObject(ctx, swire)
defer lockman.ReleaseObject(ctx, swire)
vpc, _ := swire.GetVpc()
cloudprovider := vpc.GetCloudprovider()
if cloudprovider == nil || swire.ExternalId == WireManager.getWireExternalIdForClassicNetwork(cloudprovider.Provider, swire.VpcId, swire.ZoneId) {
return nil
}
err := swire.ValidateDeleteCondition(ctx, nil)
if err != nil { // cannot delete
err = swire.markNetworkUnknown(userCred)
} else {
err = swire.Delete(ctx, userCred)
}
return err
}
func (swire *SWire) syncWithCloudWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire, vpc *SVpc, provider *SCloudprovider, zone *SZone) error {
diff, err := db.UpdateWithLock(ctx, swire, func() error {
// swire.Name = extWire.GetName()
swire.Bandwidth = extWire.GetBandwidth() // 10G
swire.IsEmulated = extWire.IsEmulated()
swire.Status = extWire.GetStatus()
if len(swire.Description) == 0 {
swire.Description = extWire.GetDescription()
}
swire.ManagerId = provider.Id
if zone != nil {
swire.ZoneId = zone.Id
} else {
vpc, _ := swire.GetVpc()
if vpc != nil {
region, err := vpc.GetRegion()
if err != nil {
return errors.Wrapf(err, "vpc.GetRegion")
}
if utils.IsInStringArray(region.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
swire.ZoneId = ""
}
}
}
if swire.IsEmulated {
swire.DomainId = vpc.DomainId
// swire.IsPublic = vpc.IsPublic
// swire.PublicScope = vpc.PublicScope
// swire.PublicSrc = vpc.PublicSrc
}
return nil
})
if err != nil {
log.Errorf("syncWithCloudWire error %s", err)
}
if provider != nil && !swire.IsEmulated {
SyncCloudDomain(userCred, swire, provider.GetOwnerId())
swire.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
} else if swire.IsEmulated {
swire.SaveSharedInfo(apis.TOwnerSource(vpc.PublicSrc), ctx, userCred, vpc.GetSharedInfo())
}
syncMetadata(ctx, userCred, swire, extWire)
db.OpsLog.LogSyncUpdate(swire, diff, userCred)
return err
}
func (swire *SWire) markNetworkUnknown(userCred mcclient.TokenCredential) error {
nets, err := swire.getNetworks(nil, nil, rbacscope.ScopeNone)
if err != nil {
return err
}
for i := 0; i < len(nets); i += 1 {
nets[i].SetStatus(userCred, api.NETWORK_STATUS_UNKNOWN, "wire sync to remove")
}
return nil
}
func (manager *SWireManager) newFromCloudWire(ctx context.Context, userCred mcclient.TokenCredential, extWire cloudprovider.ICloudWire, vpc *SVpc, provider *SCloudprovider, zone *SZone) (*SWire, error) {
wire := SWire{}
wire.SetModelManager(manager, &wire)
wire.ExternalId = extWire.GetGlobalId()
wire.Bandwidth = extWire.GetBandwidth()
wire.Status = extWire.GetStatus()
wire.Description = extWire.GetDescription()
wire.VpcId = vpc.Id
wire.ManagerId = provider.Id
region, err := vpc.GetRegion()
if err != nil {
return nil, errors.Wrapf(err, "GetRegion for vpc %s(%s)", vpc.Name, vpc.Id)
}
if zone != nil {
wire.ZoneId = zone.Id
} else if !utils.IsInStringArray(region.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
izone := extWire.GetIZone()
if gotypes.IsNil(izone) {
return nil, fmt.Errorf("missing zone for wire %s(%s)", wire.Name, wire.ExternalId)
}
zone, err := vpc.getZoneByExternalId(izone.GetGlobalId())
if err != nil {
return nil, errors.Wrapf(err, "newFromCloudWire.getZoneByExternalId")
}
wire.ZoneId = zone.Id
} else {
// regional network, wire belongs to region
}
wire.IsEmulated = extWire.IsEmulated()
wire.DomainId = vpc.DomainId
wire.IsPublic = vpc.IsPublic
wire.PublicScope = vpc.PublicScope
wire.PublicSrc = vpc.PublicSrc
err = func() error {
lockman.LockRawObject(ctx, manager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
newName, err := db.GenerateName(ctx, manager, userCred, extWire.GetName())
if err != nil {
return err
}
wire.Name = newName
return manager.TableSpec().Insert(ctx, &wire)
}()
if err != nil {
return nil, errors.Wrapf(err, "Insert")
}
if provider != nil && !wire.IsEmulated {
SyncCloudDomain(userCred, &wire, provider.GetOwnerId())
wire.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
}
syncMetadata(ctx, userCred, &wire, extWire)
db.OpsLog.LogEvent(&wire, db.ACT_CREATE, wire.GetShortDesc(ctx), userCred)
return &wire, nil
}
func filterByScopeOwnerId(q *sqlchemy.SQuery, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, domainResource bool) *sqlchemy.SQuery {
switch scope {
case rbacscope.ScopeSystem:
case rbacscope.ScopeDomain:
q = q.Equals("domain_id", ownerId.GetProjectDomainId())
case rbacscope.ScopeProject:
if domainResource {
q = q.Equals("domain_id", ownerId.GetProjectId())
} else {
q = q.Equals("tenant_id", ownerId.GetProjectId())
}
}
return q
}
func fixVmwareProvider(providers []string) (bool, []string) {
findVmware := false
findOnecloud := false
newp := make([]string, 0)
for _, p := range providers {
if p == api.CLOUD_PROVIDER_VMWARE {
findVmware = true
} else {
if p == api.CLOUD_PROVIDER_ONECLOUD {
findOnecloud = true
}
newp = append(newp, p)
}
}
if findVmware && !findOnecloud {
newp = append(newp, api.CLOUD_PROVIDER_ONECLOUD)
}
return findVmware, newp
}
func (manager *SWireManager) totalCountQ(
rangeObjs []db.IStandaloneModel,
hostTypes []string, hostProviders, hostBrands []string,
providers []string, brands []string, cloudEnv string,
scope rbacscope.TRbacScope,
ownerId mcclient.IIdentityProvider,
policyResult rbacutils.SPolicyResult,
) *sqlchemy.SQuery {
guestsQ := GuestManager.Query()
guestsQ = filterByScopeOwnerId(guestsQ, scope, ownerId, false)
guestsQ = db.ObjectIdQueryWithPolicyResult(guestsQ, GuestManager, policyResult)
guests := guestsQ.SubQuery()
// hosts no filter, for guest networks
hostsQ := HostManager.Query()
if len(hostTypes) > 0 {
hostsQ = hostsQ.In("host_type", hostTypes)
}
if len(hostProviders) > 0 || len(hostBrands) > 0 || len(cloudEnv) > 0 {
hostsQ = CloudProviderFilter(hostsQ, hostsQ.Field("manager_id"), providers, brands, cloudEnv)
}
if len(rangeObjs) > 0 {
hostsQ = RangeObjectsFilter(hostsQ, rangeObjs, nil, hostsQ.Field("zone_id"), hostsQ.Field("manager_id"), hostsQ.Field("id"), nil)
}
hosts := hostsQ.SubQuery()
// hosts filter by owner, for host networks
hostsQ2 := HostManager.Query()
hostsQ2 = db.ObjectIdQueryWithPolicyResult(hostsQ2, HostManager, policyResult)
hostsQ2 = filterByScopeOwnerId(hostsQ2, scope, ownerId, true)
if len(hostTypes) > 0 {
hostsQ2 = hostsQ2.In("host_type", hostTypes)
}
if len(hostProviders) > 0 || len(hostBrands) > 0 || len(cloudEnv) > 0 {
hostsQ2 = CloudProviderFilter(hostsQ2, hostsQ2.Field("manager_id"), providers, brands, cloudEnv)
}
if len(rangeObjs) > 0 {
hostsQ2 = RangeObjectsFilter(hostsQ2, rangeObjs, nil, hostsQ2.Field("zone_id"), hostsQ2.Field("manager_id"), hostsQ2.Field("id"), nil)
}
hosts2 := hostsQ2.SubQuery()
groupsQ := GroupManager.Query()
groupsQ = db.ObjectIdQueryWithPolicyResult(groupsQ, GroupManager, policyResult)
groupsQ = filterByScopeOwnerId(groupsQ, scope, ownerId, false)
groups := groupsQ.SubQuery()
lbsQ := LoadbalancerManager.Query()
lbsQ = db.ObjectIdQueryWithPolicyResult(lbsQ, LoadbalancerManager, policyResult)
lbsQ = filterByScopeOwnerId(lbsQ, scope, ownerId, false)
if len(providers) > 0 || len(brands) > 0 || len(cloudEnv) > 0 {
lbsQ = CloudProviderFilter(lbsQ, lbsQ.Field("manager_id"), providers, brands, cloudEnv)
}
if len(rangeObjs) > 0 {
lbsQ = RangeObjectsFilter(lbsQ, rangeObjs, lbsQ.Field("cloudregion_id"), lbsQ.Field("zone_id"), lbsQ.Field("manager_id"), nil, nil)
}
lbs := lbsQ.SubQuery()
dbsQ := DBInstanceManager.Query()
dbsQ = db.ObjectIdQueryWithPolicyResult(dbsQ, DBInstanceManager, policyResult)
dbsQ = filterByScopeOwnerId(dbsQ, scope, ownerId, false)
if len(providers) > 0 || len(brands) > 0 || len(cloudEnv) > 0 {
dbsQ = CloudProviderFilter(dbsQ, dbsQ.Field("manager_id"), providers, brands, cloudEnv)
}
if len(rangeObjs) > 0 {
dbsQ = RangeObjectsFilter(dbsQ, rangeObjs, dbsQ.Field("cloudregion_id"), dbsQ.Field("zone_id"), dbsQ.Field("manager_id"), nil, nil)
}
dbs := dbsQ.SubQuery()
gNics := GuestnetworkManager.Query().SubQuery()
gNicQ := gNics.Query(
gNics.Field("network_id"),
sqlchemy.COUNT("gnic_count"),
sqlchemy.SUM("pending_deleted_gnic_count", guests.Field("pending_deleted")),
)
gNicQ = gNicQ.Join(guests, sqlchemy.Equals(guests.Field("id"), gNics.Field("guest_id")))
gNicQ = gNicQ.Join(hosts, sqlchemy.Equals(guests.Field("host_id"), hosts.Field("id")))
gNicQ = gNicQ.Filter(sqlchemy.IsTrue(hosts.Field("enabled")))
hNics := HostnetworkManager.Query().SubQuery()
hNicQ := hNics.Query(
hNics.Field("network_id"),
sqlchemy.COUNT("hnic_count"),
)
hNicQ = hNicQ.Join(hosts2, sqlchemy.Equals(hNics.Field("baremetal_id"), hosts2.Field("id")))
hNicQ = hNicQ.Filter(sqlchemy.IsTrue(hosts2.Field("enabled")))
groupNics := GroupnetworkManager.Query().SubQuery()
grpNicQ := groupNics.Query(
groupNics.Field("network_id"),
sqlchemy.COUNT("grpnic_count"),
)
grpNicQ = grpNicQ.Join(groups, sqlchemy.Equals(groups.Field("id"), groupNics.Field("group_id")))
lbNics := LoadbalancernetworkManager.Query().SubQuery()
lbNicQ := lbNics.Query(
lbNics.Field("network_id"),
sqlchemy.COUNT("lbnic_count"),
)
lbNicQ = lbNicQ.Join(lbs, sqlchemy.Equals(lbs.Field("id"), lbNics.Field("loadbalancer_id")))
lbNicQ = lbNicQ.Filter(sqlchemy.IsFalse(lbs.Field("pending_deleted")))
eipNicsQ := ElasticipManager.Query().IsNotEmpty("network_id")
eipNicsQ = db.ObjectIdQueryWithPolicyResult(eipNicsQ, ElasticipManager, policyResult)
eipNics := filterByScopeOwnerId(eipNicsQ, scope, ownerId, false).SubQuery()
eipNicQ := eipNics.Query(
eipNics.Field("network_id"),
sqlchemy.COUNT("eipnic_count"),
)
if len(providers) > 0 || len(brands) > 0 || len(cloudEnv) > 0 {
eipNicQ = CloudProviderFilter(eipNicQ, eipNicQ.Field("manager_id"), providers, brands, cloudEnv)
}
if len(rangeObjs) > 0 {
eipNicQ = RangeObjectsFilter(eipNicQ, rangeObjs, eipNicQ.Field("cloudregion_id"), nil, eipNicQ.Field("manager_id"), nil, nil)
}
netifsQ := NetworkInterfaceManager.Query()
netifsQ = db.ObjectIdQueryWithPolicyResult(netifsQ, NetworkInterfaceManager, policyResult)
netifsQ = filterByScopeOwnerId(netifsQ, scope, ownerId, true)
if len(providers) > 0 || len(brands) > 0 || len(cloudEnv) > 0 {
netifsQ = CloudProviderFilter(netifsQ, netifsQ.Field("manager_id"), providers, brands, cloudEnv)
}
if len(rangeObjs) > 0 {
netifsQ = RangeObjectsFilter(netifsQ, rangeObjs, netifsQ.Field("cloudregion_id"), nil, netifsQ.Field("manager_id"), nil, nil)
}
netifs := netifsQ.SubQuery()
netifNics := NetworkinterfacenetworkManager.Query().SubQuery()
netifNicQ := netifNics.Query(
netifNics.Field("network_id"),
sqlchemy.COUNT("netifnic_count"),
)
netifNicQ = netifNicQ.Join(netifs, sqlchemy.Equals(netifNics.Field("networkinterface_id"), netifs.Field("id")))
dbNics := DBInstanceNetworkManager.Query().SubQuery()
dbNicQ := dbNics.Query(
dbNics.Field("network_id"),
sqlchemy.COUNT("dbnic_count"),
)
dbNicQ = dbNicQ.Join(dbs, sqlchemy.Equals(dbs.Field("id"), dbNics.Field("dbinstance_id")))
dbNicQ = dbNicQ.Filter(sqlchemy.IsFalse(dbs.Field("pending_deleted")))
gNicSQ := gNicQ.GroupBy(gNics.Field("network_id")).SubQuery()
hNicSQ := hNicQ.GroupBy(hNics.Field("network_id")).SubQuery()
grpNicSQ := grpNicQ.GroupBy(groupNics.Field("network_id")).SubQuery()
lbNicSQ := lbNicQ.GroupBy(lbNics.Field("network_id")).SubQuery()
eipNicSQ := eipNicQ.GroupBy(eipNics.Field("network_id")).SubQuery()
netifNicSQ := netifNicQ.GroupBy(netifNics.Field("network_id")).SubQuery()
dbNicSQ := dbNicQ.GroupBy(dbNics.Field("network_id")).SubQuery()
networks := NetworkManager.Query().SubQuery()
netQ := networks.Query(
sqlchemy.SUM("guest_nic_count", gNicSQ.Field("gnic_count")),
sqlchemy.SUM("pending_deleted_guest_nic_count", gNicSQ.Field("pending_deleted_gnic_count")),
sqlchemy.SUM("host_nic_count", hNicSQ.Field("hnic_count")),
sqlchemy.SUM("group_nic_count", grpNicSQ.Field("grpnic_count")),
sqlchemy.SUM("lb_nic_count", lbNicSQ.Field("lbnic_count")),
sqlchemy.SUM("eip_nic_count", eipNicSQ.Field("eipnic_count")),
sqlchemy.SUM("netif_nic_count", netifNicSQ.Field("netifnic_count")),
sqlchemy.SUM("db_nic_count", dbNicSQ.Field("dbnic_count")),
)
netQ = netQ.LeftJoin(gNicSQ, sqlchemy.Equals(gNicSQ.Field("network_id"), networks.Field("id")))
netQ = netQ.LeftJoin(hNicSQ, sqlchemy.Equals(hNicSQ.Field("network_id"), networks.Field("id")))
netQ = netQ.LeftJoin(grpNicSQ, sqlchemy.Equals(grpNicSQ.Field("network_id"), networks.Field("id")))
netQ = netQ.LeftJoin(lbNicSQ, sqlchemy.Equals(lbNicSQ.Field("network_id"), networks.Field("id")))
netQ = netQ.LeftJoin(eipNicSQ, sqlchemy.Equals(eipNicSQ.Field("network_id"), networks.Field("id")))
netQ = netQ.LeftJoin(netifNicSQ, sqlchemy.Equals(netifNicSQ.Field("network_id"), networks.Field("id")))
netQ = netQ.LeftJoin(dbNicSQ, sqlchemy.Equals(dbNicSQ.Field("network_id"), networks.Field("id")))
return netQ
}
func (manager *SWireManager) totalCountQ2(
rangeObjs []db.IStandaloneModel,
hostTypes []string,
providers []string, brands []string, cloudEnv string,
scope rbacscope.TRbacScope,
ownerId mcclient.IIdentityProvider,
policyResult rbacutils.SPolicyResult,
) *sqlchemy.SQuery {
revIps := filterExpiredReservedIps(ReservedipManager.Query()).SubQuery()
revQ := revIps.Query(
revIps.Field("network_id"),
sqlchemy.COUNT("rnic_count"),
)
revSQ := revQ.GroupBy(revIps.Field("network_id")).SubQuery()
ownerNetQ1 := NetworkManager.Query()
ownerNetQ1 = db.ObjectIdQueryWithPolicyResult(ownerNetQ1, NetworkManager, policyResult)
ownerNetworks := filterByScopeOwnerId(ownerNetQ1, scope, ownerId, false).SubQuery()
ownerNetQ := ownerNetworks.Query(
ownerNetworks.Field("wire_id"),
sqlchemy.COUNT("id").Label("net_count"),
sqlchemy.SUM("rev_count", revSQ.Field("rnic_count")),
)
ownerNetQ = ownerNetQ.LeftJoin(revSQ, sqlchemy.Equals(revSQ.Field("network_id"), ownerNetworks.Field("id")))
ownerNetQ = ownerNetQ.GroupBy(ownerNetworks.Field("wire_id"))
ownerNetSQ := ownerNetQ.SubQuery()
wires := WireManager.Query().SubQuery()
q := wires.Query(
sqlchemy.SUM("net_count", ownerNetSQ.Field("net_count")),
sqlchemy.SUM("reserved_count", ownerNetSQ.Field("rev_count")),
)
q = q.LeftJoin(ownerNetSQ, sqlchemy.Equals(wires.Field("id"), ownerNetSQ.Field("wire_id")))
return filterWiresCountQuery(q, hostTypes, providers, brands, cloudEnv, rangeObjs)
}
func (manager *SWireManager) totalCountQ3(
rangeObjs []db.IStandaloneModel,
hostTypes []string,
providers []string, brands []string, cloudEnv string,
scope rbacscope.TRbacScope,
ownerId mcclient.IIdentityProvider,
policyResult rbacutils.SPolicyResult,
) *sqlchemy.SQuery {
wiresQ := WireManager.Query()
wiresQ = db.ObjectIdQueryWithPolicyResult(wiresQ, WireManager, policyResult)
wires := filterByScopeOwnerId(WireManager.Query(), scope, ownerId, true).SubQuery()
q := wires.Query(
sqlchemy.COUNT("id").Label("wires_count"),
sqlchemy.SUM("emulated_wires_count", wires.Field("is_emulated")),
)
return filterWiresCountQuery(q, hostTypes, providers, brands, cloudEnv, rangeObjs)
}
func filterWiresCountQuery(q *sqlchemy.SQuery, hostTypes, providers, brands []string, cloudEnv string, rangeObjs []db.IStandaloneModel) *sqlchemy.SQuery {
if len(hostTypes) > 0 {
hostwires := NetInterfaceManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
hostWireQ := hostwires.Query(hostwires.Field("wire_id"))
hostWireQ = hostWireQ.Join(hosts, sqlchemy.Equals(hostWireQ.Field("baremetal_id"), hosts.Field("id")))
hostWireQ = hostWireQ.Filter(sqlchemy.In(hosts.Field("host_type"), hostTypes))
hostWireQ = hostWireQ.GroupBy(hostwires.Field("wire_id"))
hostWireSQ := hostWireQ.SubQuery()
q = q.Join(hostWireSQ, sqlchemy.Equals(hostWireSQ.Field("wire_id"), q.Field("id")))
}
if len(rangeObjs) > 0 || len(providers) > 0 || len(brands) > 0 || len(cloudEnv) > 0 {
vpcs := VpcManager.Query().SubQuery()
q = q.Join(vpcs, sqlchemy.Equals(q.Field("vpc_id"), vpcs.Field("id")))
q = CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
q = RangeObjectsFilter(q, rangeObjs, vpcs.Field("cloudregion_id"), q.Field("zone_id"), q.Field("manager_id"), nil, nil)
}
return q
}
type WiresCountStat struct {
WiresCount int
EmulatedWiresCount int
NetCount int
GuestNicCount int
HostNicCount int
ReservedCount int
GroupNicCount int
LbNicCount int
EipNicCount int
NetifNicCount int
DbNicCount int
PendingDeletedGuestNicCount int
}
func (wstat WiresCountStat) NicCount() int {
return wstat.GuestNicCount + wstat.HostNicCount + wstat.ReservedCount + wstat.GroupNicCount + wstat.LbNicCount + wstat.NetifNicCount + wstat.EipNicCount + wstat.DbNicCount
}
func (manager *SWireManager) TotalCount(
rangeObjs []db.IStandaloneModel,
hostTypes []string,
providers []string, brands []string, cloudEnv string,
scope rbacscope.TRbacScope,
ownerId mcclient.IIdentityProvider,
policyResult rbacutils.SPolicyResult,
) WiresCountStat {
vmwareP, hostProviders := fixVmwareProvider(providers)
vmwareB, hostBrands := fixVmwareProvider(brands)
if vmwareP || vmwareB {
if !utils.IsInStringArray(api.HOST_TYPE_ESXI, hostTypes) {
hostTypes = append(hostTypes, api.HOST_TYPE_ESXI)
}
} else {
if utils.IsInStringArray(api.HOST_TYPE_ESXI, hostTypes) {
providers = append(providers, api.CLOUD_PROVIDER_VMWARE)
brands = append(brands, api.CLOUD_PROVIDER_VMWARE)
}
}
if len(hostTypes) > 0 {
for _, p := range providers {
if hs, ok := api.CLOUD_PROVIDER_HOST_TYPE_MAP[p]; ok {
hostTypes = append(hostTypes, hs...)
}
}
for _, p := range brands {
if hs, ok := api.CLOUD_PROVIDER_HOST_TYPE_MAP[p]; ok {
hostTypes = append(hostTypes, hs...)
}
}
}
log.Debugf("providers: %#v hostProviders: %#v brands: %#v hostBrands: %#v hostTypes: %#v", providers, hostProviders, brands, hostBrands, hostTypes)
stat := WiresCountStat{}
err := manager.totalCountQ(
rangeObjs,
hostTypes, hostProviders, hostBrands,
providers, brands, cloudEnv,
scope, ownerId,
policyResult,
).First(&stat)
if err != nil {
log.Errorf("Wire total count: %v", err)
}
err = manager.totalCountQ2(
rangeObjs,
hostTypes,
providers, brands, cloudEnv,
scope, ownerId,
policyResult,
).First(&stat)
if err != nil {
log.Errorf("Wire total count 2: %v", err)
}
err = manager.totalCountQ3(
rangeObjs,
hostTypes,
providers, brands, cloudEnv,
scope, ownerId,
policyResult,
).First(&stat)
if err != nil {
log.Errorf("Wire total count 2: %v", err)
}
return stat
}
func (swire *SWire) getNetworkQuery(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
additionalNicIds := NetworkAdditionalWireManager.networkIdQuery(swire.Id)
q := NetworkManager.Query()
q = q.Filter(sqlchemy.OR(
sqlchemy.Equals(q.Field("wire_id"), swire.Id),
sqlchemy.In(q.Field("id"), additionalNicIds.SubQuery()),
))
if ownerId != nil {
q = NetworkManager.FilterByOwner(q, NetworkManager, userCred, ownerId, scope)
}
return q
}
func (swire *SWire) GetNetworks(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) ([]SNetwork, error) {
return swire.getNetworks(userCred, ownerId, scope)
}
func (swire *SWire) getNetworks(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) ([]SNetwork, error) {
q := swire.getNetworkQuery(userCred, ownerId, scope)
nets := make([]SNetwork, 0)
err := db.FetchModelObjects(NetworkManager, q, &nets)
if err != nil {
return nil, err
}
return nets, nil
}
func (swire *SWire) getGatewayNetworkQuery(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
q := swire.getNetworkQuery(userCred, ownerId, scope)
q = q.IsNotNull("guest_gateway").IsNotEmpty("guest_gateway")
q = q.Equals("status", api.NETWORK_STATUS_AVAILABLE)
return q
}
func (swire *SWire) getAutoAllocNetworks(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) ([]SNetwork, error) {
q := swire.getGatewayNetworkQuery(userCred, ownerId, scope)
q = q.IsTrue("is_auto_alloc")
nets := make([]SNetwork, 0)
err := db.FetchModelObjects(NetworkManager, q, &nets)
if err != nil {
return nil, err
}
return nets, nil
}
func (swire *SWire) getPublicNetworks(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) ([]SNetwork, error) {
q := swire.getGatewayNetworkQuery(userCred, ownerId, scope)
q = q.IsTrue("is_public")
nets := make([]SNetwork, 0)
err := db.FetchModelObjects(NetworkManager, q, &nets)
if err != nil {
return nil, err
}
return nets, nil
}
func (swire *SWire) getPrivateNetworks(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) ([]SNetwork, error) {
q := swire.getGatewayNetworkQuery(userCred, ownerId, scope)
q = q.IsFalse("is_public")
nets := make([]SNetwork, 0)
err := db.FetchModelObjects(NetworkManager, q, &nets)
if err != nil {
return nil, err
}
return nets, nil
}
func (swire *SWire) GetCandidatePrivateNetwork(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope, isExit bool, serverTypes []string) (*SNetwork, error) {
nets, err := swire.getPrivateNetworks(userCred, ownerId, scope)
if err != nil {
return nil, err
}
return ChooseCandidateNetworks(nets, isExit, serverTypes), nil
}
func (swire *SWire) GetCandidateAutoAllocNetwork(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope, isExit bool, serverTypes []string) (*SNetwork, error) {
nets, err := swire.getAutoAllocNetworks(userCred, ownerId, scope)
if err != nil {
return nil, err
}
return ChooseCandidateNetworks(nets, isExit, serverTypes), nil
}
func (swire *SWire) GetCandidateNetworkForIp(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope, ipAddr string) (*SNetwork, error) {
ip, err := netutils.NewIPV4Addr(ipAddr)
if err != nil {
return nil, err
}
netPrivates, err := swire.getPrivateNetworks(userCred, ownerId, scope)
if err != nil {
return nil, err
}
for _, net := range netPrivates {
if net.IsAddressInRange(ip) {
return &net, nil
}
}
netPublics, err := swire.getPublicNetworks(userCred, ownerId, scope)
if err != nil {
return nil, err
}
for _, net := range netPublics {
if net.IsAddressInRange(ip) {
return &net, nil
}
}
return nil, nil
}
func ChooseNetworkByAddressCount(nets []*SNetwork) (*SNetwork, *SNetwork) {
return chooseNetworkByAddressCount(nets)
}
func chooseNetworkByAddressCount(nets []*SNetwork) (*SNetwork, *SNetwork) {
minCnt := 65535
maxCnt := 0
var minSel *SNetwork
var maxSel *SNetwork
for _, net := range nets {
cnt, err := net.getFreeAddressCount()
if err != nil || cnt <= 0 {
continue
}
if minSel == nil || minCnt > cnt {
minSel = net
minCnt = cnt
}
if maxSel == nil || maxCnt < cnt {
maxSel = net
maxCnt = cnt
}
}
return minSel, maxSel
}
func ChooseCandidateNetworks(nets []SNetwork, isExit bool, serverTypes []string) *SNetwork {
matchingNets := make([]*SNetwork, 0)
notMatchingNets := make([]*SNetwork, 0)
for _, s := range serverTypes {
net := chooseCandidateNetworksByNetworkType(nets, isExit, s)
if net != nil {
if utils.IsInStringArray(net.ServerType, serverTypes) {
matchingNets = append(matchingNets, net)
} else {
notMatchingNets = append(notMatchingNets, net)
}
}
}
if len(matchingNets) >= 1 {
return matchingNets[0]
}
if len(notMatchingNets) >= 1 {
return notMatchingNets[0]
}
return nil
}
func chooseCandidateNetworksByNetworkType(nets []SNetwork, isExit bool, serverType string) *SNetwork {
matchingNets := make([]*SNetwork, 0)
notMatchingNets := make([]*SNetwork, 0)
for i := 0; i < len(nets); i++ {
net := nets[i]
if isExit != net.IsExitNetwork() {
continue
}
if serverType == net.ServerType || (len(net.ServerType) == 0 && serverType == api.NETWORK_TYPE_GUEST) {
matchingNets = append(matchingNets, &net)
} else {
notMatchingNets = append(notMatchingNets, &net)
}
}
minSel, maxSel := chooseNetworkByAddressCount(matchingNets)
if (isExit && minSel == nil) || (!isExit && maxSel == nil) {
minSel, maxSel = chooseNetworkByAddressCount(notMatchingNets)
}
if isExit {
return minSel
} else {
return maxSel
}
}
func (manager *SWireManager) InitializeData() error {
{
err := manager.initVpcId()
if err != nil {
return errors.Wrap(err, "initVpcId")
}
}
{
err := manager.initManagerId()
if err != nil {
return errors.Wrap(err, "initManagerId")
}
}
{
err := manager.cleanNoVpcWires()
if err != nil {
return errors.Wrap(err, "cleanNoVpcWires")
}
}
return nil
}
func (manager *SWireManager) initVpcId() error {
wires, err := manager.FetchWires(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
q = q.Filter(sqlchemy.OR(
sqlchemy.IsEmpty(q.Field("vpc_id")),
sqlchemy.IsEmpty(q.Field("status")),
sqlchemy.Equals(q.Field("status"), "init"),
sqlchemy.Equals(q.Field("status"), api.WIRE_STATUS_READY_DEPRECATED),
))
return q
})
if err != nil {
return errors.Wrap(err, "FetchWires")
}
for i := range wires {
w := wires[i]
_, err := db.Update(&w, func() error {
if len(w.VpcId) == 0 {
w.VpcId = api.DEFAULT_VPC_ID
}
if len(w.Status) == 0 || w.Status == "init" || w.Status == api.WIRE_STATUS_READY_DEPRECATED {
w.Status = api.WIRE_STATUS_AVAILABLE
}
return nil
})
if err != nil {
return errors.Wrap(err, "Update")
}
}
return nil
}
func (manager *SWireManager) initManagerId() error {
wires, err := manager.FetchWires(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
vpcs := VpcManager.Query().SubQuery()
q = q.Join(vpcs, sqlchemy.Equals(q.Field("vpc_id"), vpcs.Field("id")))
q = q.Filter(sqlchemy.OR(
sqlchemy.IsNull(q.Field("manager_id")),
sqlchemy.NotEquals(q.Field("manager_id"), vpcs.Field("manager_id")),
))
q = q.IsNullOrEmpty("manager_id")
return q
})
if err != nil {
return errors.Wrap(err, "FetchWires")
}
for i := range wires {
w := wires[i]
vpc, err := w.GetVpc()
if err != nil {
return errors.Wrap(err, "GetVpc")
}
_, err = db.Update(&w, func() error {
w.ManagerId = vpc.ManagerId
return nil
})
if err != nil {
return errors.Wrap(err, "Update")
}
}
return nil
}
func (manager *SWireManager) cleanNoVpcWires() error {
wires, err := manager.FetchWires(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
// find wires whose VPC was deleted
deletedVpcs := VpcManager.RawQuery("id").IsTrue("deleted").SubQuery()
q = q.Join(deletedVpcs, sqlchemy.Equals(q.Field("vpc_id"), deletedVpcs.Field("id")))
return q
})
if err != nil {
return errors.Wrap(err, "FetchWires")
}
for i := range wires {
err := wires[i].Delete(context.Background(), auth.AdminCredential())
if err != nil {
return errors.Wrapf(err, "Delete wire %s", wires[i].Id)
}
}
return nil
}
func (manager *SWireManager) FetchWires(filter func(q *sqlchemy.SQuery) *sqlchemy.SQuery) ([]SWire, error) {
wires := make([]SWire, 0)
q := manager.Query()
q = filter(q)
err := db.FetchModelObjects(manager, q, &wires)
if err != nil {
return nil, errors.Wrap(err, "FetchModelObjects")
}
return wires, nil
}
func (wire *SWire) isOneCloudVpcWire() bool {
return IsOneCloudVpcResource(wire)
}
func (wire *SWire) getEnabledHosts() []SHost {
hosts := make([]SHost, 0)
hostQuery := HostManager.Query().SubQuery()
hostNetifQuery := NetInterfaceManager.Query().SubQuery()
q := hostQuery.Query()
q = q.Join(hostNetifQuery, sqlchemy.Equals(hostQuery.Field("id"), hostNetifQuery.Field("baremetal_id")))
q = q.Filter(sqlchemy.IsTrue(hostQuery.Field("enabled")))
q = q.Filter(sqlchemy.Equals(hostQuery.Field("host_status"), api.HOST_ONLINE))
if wire.isOneCloudVpcWire() {
q = q.Filter(sqlchemy.NOT(sqlchemy.IsNullOrEmpty(hostQuery.Field("ovn_version"))))
} else {
q = q.Filter(sqlchemy.Equals(hostNetifQuery.Field("wire_id"), wire.Id))
}
err := db.FetchModelObjects(HostManager, q, &hosts)
if err != nil {
log.Errorf("getEnabledHosts fail %s", err)
return nil
}
return hosts
}
func (wire *SWire) clearHostSchedDescCache() error {
hosts := wire.getEnabledHosts()
if hosts != nil {
for i := 0; i < len(hosts); i += 1 {
host := hosts[i]
if err := host.ClearSchedDescCache(); err != nil {
return errors.Wrapf(err, "wire %s clear host %s sched cache", wire.GetName(), host.GetName())
}
}
}
return nil
}
func (swire *SWire) GetIWire(ctx context.Context) (cloudprovider.ICloudWire, error) {
vpc, err := swire.GetVpc()
if err != nil {
return nil, errors.Wrapf(err, "GetVpc")
}
ivpc, err := vpc.GetIVpc(ctx)
if err != nil {
return nil, err
}
return ivpc.GetIWireById(swire.GetExternalId())
}
func (manager *SWireManager) FetchWireById(wireId string) *SWire {
wireObj, err := manager.FetchById(wireId)
if err != nil {
log.Errorf("FetchWireById fail %s", err)
return nil
}
return wireObj.(*SWire)
}
func (manager *SWireManager) FetchWireByExternalId(managerId, extId string) (*SWire, error) {
wires, err := manager.FetchWires(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
q = q.Equals("manager_id", managerId).Equals("external_id", extId)
return q
})
if err != nil {
return nil, errors.Wrap(err, "FetchWires")
}
switch len(wires) {
case 0:
return nil, errors.Wrap(sql.ErrNoRows, "not found")
case 1:
return &wires[0], nil
default:
return nil, errors.Wrapf(httperrors.ErrDuplicateId, "duplicate wires externalId %s", extId)
}
}
func (manager *SWireManager) GetOnPremiseWireOfIp(ipAddr string) (*SWire, error) {
net, err := NetworkManager.GetOnPremiseNetworkOfIP(ipAddr, "", tristate.None)
if err != nil {
return nil, err
}
wire, _ := net.GetWire()
if wire != nil {
return wire, nil
} else {
return nil, fmt.Errorf("Wire not found")
}
}
func (w *SWire) PerformMergeNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.WireMergeNetworkInput) (jsonutils.JSONObject, error) {
return nil, w.StartMergeNetwork(ctx, userCred, "")
}
func (sm *SWireManager) FetchByIdsOrNames(idOrNames []string) ([]SWire, error) {
if len(idOrNames) == 0 {
return nil, nil
}
q := sm.Query()
if len(idOrNames) == 1 {
q.Filter(sqlchemy.OR(sqlchemy.Equals(q.Field("id"), idOrNames[0]), sqlchemy.Equals(q.Field("name"), idOrNames[0])))
} else {
q.Filter(sqlchemy.OR(sqlchemy.In(q.Field("id"), stringutils2.RemoveUtf8Strings(idOrNames)), sqlchemy.In(q.Field("name"), idOrNames)))
}
ret := make([]SWire, 0, len(idOrNames))
err := db.FetchModelObjects(sm, q, &ret)
if err != nil {
return nil, err
}
return ret, nil
}
func (w *SWire) PerformMergeFrom(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.WireMergeFromInput) (ret jsonutils.JSONObject, err error) {
if len(input.Sources) == 0 {
return nil, httperrors.NewMissingParameterError("sources")
}
defer func() {
if err != nil {
logclient.AddActionLogWithContext(ctx, w, logclient.ACT_MERGE, err.Error(), userCred, false)
}
}()
wires, err := WireManager.FetchByIdsOrNames(input.Sources)
if err != nil {
return
}
wireIdOrNameSet := sets.NewString(input.Sources...)
for i := range wires {
id, name := wires[i].GetId(), wires[i].GetName()
if wireIdOrNameSet.Has(id) {
wireIdOrNameSet.Delete(id)
continue
}
if wireIdOrNameSet.Has(name) {
wireIdOrNameSet.Delete(name)
}
}
if wireIdOrNameSet.Len() > 0 {
return nil, httperrors.NewInputParameterError("invalid wire id or name %v", wireIdOrNameSet.UnsortedList())
}
lockman.LockClass(ctx, WireManager, db.GetLockClassKey(WireManager, userCred))
defer lockman.ReleaseClass(ctx, WireManager, db.GetLockClassKey(WireManager, userCred))
for _, tw := range wires {
err = WireManager.handleWireIdChange(ctx, &wireIdChangeArgs{
oldWire: &tw,
newWire: w,
})
if err != nil {
return nil, errors.Wrapf(err, "unable to merge wire %s to %s", tw.GetId(), w.GetId())
}
if err = tw.Delete(ctx, userCred); err != nil {
return nil, err
}
}
logclient.AddActionLogWithContext(ctx, w, logclient.ACT_MERGE_FROM, "", userCred, true)
if input.MergeNetwork {
err = w.StartMergeNetwork(ctx, userCred, "")
if err != nil {
return nil, errors.Wrap(err, "unableto StartMergeNetwork")
}
}
return
}
func (w *SWire) PerformMergeTo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.WireMergeInput) (ret jsonutils.JSONObject, err error) {
if len(input.Target) == 0 {
return nil, httperrors.NewMissingParameterError("target")
}
defer func() {
if err != nil {
logclient.AddActionLogWithContext(ctx, w, logclient.ACT_MERGE, err.Error(), userCred, false)
}
}()
iw, err := WireManager.FetchByIdOrName(userCred, input.Target)
if err == sql.ErrNoRows {
err = httperrors.NewNotFoundError("Wire %q", input.Target)
return
}
if err != nil {
return
}
tw := iw.(*SWire)
lockman.LockClass(ctx, WireManager, db.GetLockClassKey(WireManager, userCred))
defer lockman.ReleaseClass(ctx, WireManager, db.GetLockClassKey(WireManager, userCred))
err = WireManager.handleWireIdChange(ctx, &wireIdChangeArgs{
oldWire: w,
newWire: tw,
})
if err != nil {
return
}
logclient.AddActionLogWithContext(ctx, w, logclient.ACT_MERGE, "", userCred, true)
if err = w.Delete(ctx, userCred); err != nil {
return nil, err
}
if input.MergeNetwork {
err = tw.StartMergeNetwork(ctx, userCred, "")
if err != nil {
return nil, errors.Wrap(err, "unableto StartMergeNetwork")
}
}
return
}
func (w *SWire) StartMergeNetwork(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "NetworksUnderWireMergeTask", w, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (wm *SWireManager) handleWireIdChange(ctx context.Context, args *wireIdChangeArgs) error {
handlers := []wireIdChangeHandler{
// HostwireManager,
NetworkManager,
LoadbalancerClusterManager,
NetInterfaceManager,
}
errs := []error{}
for _, h := range handlers {
if err := h.handleWireIdChange(ctx, args); err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
err := errors.NewAggregate(errs)
return httperrors.NewGeneralError(err)
}
return nil
}
func (wire *SWire) PerformSetClassMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformSetClassMetadataInput) (jsonutils.JSONObject, error) {
vpc, err := wire.GetVpc()
if err != nil {
return nil, errors.Wrapf(err, "unable to get vpc of wire %s", wire.GetId())
}
if vpc.GetId() != api.DEFAULT_VPC_ID {
ok, err := db.IsInSameClass(ctx, vpc, db.ClassMetadataOwner(input))
if err != nil {
return nil, errors.Wrapf(err, "unable to check if vpc and wire are in same class")
}
if !ok {
return nil, httperrors.NewForbiddenError("the vpc %s and this wire have different class metadata", vpc.GetName())
}
}
return wire.SStatusInfrasResourceBase.PerformSetClassMetadata(ctx, userCred, query, input)
}
// 二层网络列表
func (manager *SWireManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.WireListInput,
) (*sqlchemy.SQuery, error) {
var err error
{
managedFilter := query.ManagedResourceListInput
query.ManagedResourceListInput = api.ManagedResourceListInput{}
q, err = manager.SVpcResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VpcFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemFilter")
}
q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, managedFilter)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
query.ManagedResourceListInput = managedFilter
}
q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
zoneQuery := api.ZonalFilterListInput{
ZonalFilterListBase: query.ZonalFilterListBase,
}
q, err = manager.SZoneResourceBaseManager.ListItemFilter(ctx, q, userCred, zoneQuery)
if err != nil {
return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemFilter")
}
q, err = manager.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemFilter")
}
hostStr := query.HostId
if len(hostStr) > 0 {
hostObj, err := HostManager.FetchByIdOrName(userCred, hostStr)
if err != nil {
return nil, httperrors.NewResourceNotFoundError2(HostManager.Keyword(), hostStr)
}
sq := NetInterfaceManager.Query("wire_id").Equals("baremetal_id", hostObj.GetId())
q = q.Filter(sqlchemy.In(q.Field("id"), sq.SubQuery()))
}
if len(query.HostType) > 0 {
hs := HostManager.Query("id").Equals("host_type", query.HostType).SubQuery()
sq := NetInterfaceManager.Query("wire_id")
sq = sq.Join(hs, sqlchemy.Equals(sq.Field("baremetal_id"), hs.Field("id")))
q = q.Filter(sqlchemy.In(q.Field("id"), sq.SubQuery()))
}
if query.Bandwidth != nil {
q = q.Equals("bandwidth", *query.Bandwidth)
}
return q, nil
}
func (manager *SWireManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.WireListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SInfrasResourceBaseManager.OrderByExtraFields")
}
q, err = manager.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VpcFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SVpcResourceBaseManager.OrderByExtraFields")
}
zoneQuery := api.ZonalFilterListInput{
ZonalFilterListBase: query.ZonalFilterListBase,
}
q, err = manager.SZoneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, zoneQuery)
if err != nil {
return nil, errors.Wrap(err, "SZoneResourceBaseManager.OrderByExtraFields")
}
if db.NeedOrderQuery([]string{query.OrderByNetworkCount}) {
networkQ := NetworkManager.Query()
networkQ = networkQ.AppendField(networkQ.Field("wire_id"), sqlchemy.COUNT("network_count"))
networkQ = networkQ.GroupBy(networkQ.Field("wire_id"))
networkSQ := networkQ.SubQuery()
q = q.LeftJoin(networkSQ, sqlchemy.Equals(networkSQ.Field("wire_id"), q.Field("id")))
q = q.AppendField(q.QueryFields()...)
q = q.AppendField(networkSQ.Field("network_count"))
q = db.OrderByFields(q, []string{query.OrderByNetworkCount}, []sqlchemy.IQueryField{q.Field("network_count")})
}
return q, nil
}
func (manager *SWireManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SVpcResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SZoneResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
type SWireUsageCount struct {
Id string
api.WireUsage
}
func wmquery(manager db.IModelManager, uniqField, field string, wireIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SSubQuery {
q := manager.Query("wire_id", uniqField)
if filter != nil {
q = filter(q)
}
sq := q.Distinct().SubQuery()
return sq.Query(
sq.Field("wire_id"),
sqlchemy.COUNT(field),
).In("wire_id", wireIds).GroupBy(sq.Field("wire_id")).SubQuery()
}
func (manager *SWireManager) TotalResourceCount(wireIds []string) (map[string]api.WireUsage, error) {
// network
networkSQ := wmquery(NetworkManager, "id", "network_cnt", wireIds, nil)
hostSQ := wmquery(NetInterfaceManager, "baremetal_id", "host_cnt", wireIds, nil)
wires := manager.Query().SubQuery()
wireQ := wires.Query(
sqlchemy.SUM("networks", networkSQ.Field("network_cnt")),
sqlchemy.SUM("host_count", hostSQ.Field("host_cnt")),
)
wireQ.AppendField(wireQ.Field("id"))
wireQ = wireQ.LeftJoin(networkSQ, sqlchemy.Equals(wireQ.Field("id"), networkSQ.Field("wire_id")))
wireQ = wireQ.LeftJoin(hostSQ, sqlchemy.Equals(wireQ.Field("id"), hostSQ.Field("wire_id")))
wireQ = wireQ.Filter(sqlchemy.In(wireQ.Field("id"), wireIds)).GroupBy(wireQ.Field("id"))
wireCount := []SWireUsageCount{}
err := wireQ.All(&wireCount)
if err != nil {
return nil, errors.Wrapf(err, "wireQ.All")
}
result := map[string]api.WireUsage{}
for i := range wireCount {
result[wireCount[i].Id] = wireCount[i].WireUsage
}
return result, nil
}
func (manager *SWireManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.WireDetails {
rows := make([]api.WireDetails, len(objs))
stdRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
managerList := make([]interface{}, len(rows))
wireIds := make([]string, len(objs))
for i := range rows {
rows[i] = api.WireDetails{
StatusInfrasResourceBaseDetails: stdRows[i],
VpcResourceInfo: vpcRows[i],
ZoneResourceInfoBase: zoneRows[i].ZoneResourceInfoBase,
}
wire := objs[i].(*SWire)
wireIds[i] = wire.Id
managerList[i] = &SManagedResourceBase{wire.ManagerId}
}
managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, managerList, fields, isList)
usage, err := manager.TotalResourceCount(wireIds)
if err != nil {
log.Errorf("TotalResourceCount error: %v", err)
return rows
}
for i := range rows {
rows[i].WireUsage = usage[wireIds[i]]
rows[i].ManagedResourceInfo = managerRows[i]
}
return rows
}
func (man *SWireManager) removeWiresByVpc(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc) error {
wires, err := man.FetchWires(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
q = q.Equals("vpc_id", vpc.Id)
return q
})
if err != nil {
return errors.Wrap(err, "FetchWires")
}
var errs []error
for i := range wires {
wire := &wires[i]
if err := wire.Delete(ctx, userCred); err != nil {
errs = append(errs, err)
}
}
return errors.NewAggregate(errs)
}
/*func (swire *SWire) IsManaged() bool {
vpc, _ := swire.GetVpc()
if vpc == nil {
return false
}
return vpc.IsManaged()
}*/
func (model *SWire) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
vpc, _ := model.GetVpc()
if !data.Contains("public_scope") {
if !model.IsManaged() && db.IsAdminAllowPerform(ctx, userCred, model, "public") && ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() && vpc != nil && vpc.IsPublic && vpc.PublicScope == string(rbacscope.ScopeSystem) {
model.SetShare(rbacscope.ScopeSystem)
} else {
model.SetShare(rbacscope.ScopeNone)
}
data.(*jsonutils.JSONDict).Set("public_scope", jsonutils.NewString(model.PublicScope))
}
model.Status = api.WIRE_STATUS_AVAILABLE
model.ManagerId = vpc.ManagerId
return model.SInfrasResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
}
func (model *SWire) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
model.SStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
vpc, err := model.GetVpc()
if err != nil {
log.Errorf("unable to getvpc of wire %s: %s", model.GetId(), vpc.GetId())
}
err = db.InheritFromTo(ctx, userCred, vpc, model)
if err != nil {
log.Errorf("unable to inhert vpc to model %s: %s", model.GetId(), err.Error())
}
}
func (wire *SWire) GetChangeOwnerCandidateDomainIds() []string {
candidates := [][]string{}
vpc, _ := wire.GetVpc()
if vpc != nil {
candidates = append(candidates,
vpc.GetChangeOwnerCandidateDomainIds(),
db.ISharableChangeOwnerCandidateDomainIds(vpc))
}
return db.ISharableMergeChangeOwnerCandidateDomainIds(wire, candidates...)
}
func (wire *SWire) GetChangeOwnerRequiredDomainIds() []string {
requires := stringutils2.SSortedStrings{}
networks, _ := wire.getNetworks(nil, nil, rbacscope.ScopeNone)
for i := range networks {
requires = stringutils2.Append(requires, networks[i].DomainId)
}
return requires
}
func (wire *SWire) GetRequiredSharedDomainIds() []string {
networks, _ := wire.getNetworks(nil, nil, rbacscope.ScopeNone)
if len(networks) == 0 {
return wire.SInfrasResourceBase.GetRequiredSharedDomainIds()
}
requires := make([][]string, len(networks))
for i := range networks {
requires[i] = db.ISharableChangeOwnerCandidateDomainIds(&networks[i])
}
return db.ISharableMergeShareRequireDomainIds(requires...)
}
func (manager *SWireManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SStatusInfrasResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SStatusInfrasResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SZoneResourceBaseManager.GetExportKeys()...) {
q, err = manager.SZoneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SZoneResourceBaseManager.ListItemExportKeys")
}
}
if keys.ContainsAny(manager.SVpcResourceBaseManager.GetExportKeys()...) {
q, err = manager.SVpcResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (swire *SWire) GetDetailsTopology(ctx context.Context, userCred mcclient.TokenCredential, input *api.WireTopologyInput) (*api.WireTopologyOutput, error) {
ret := &api.WireTopologyOutput{
Name: swire.Name,
Status: swire.Status,
Bandwidth: swire.Bandwidth,
Networks: []api.NetworkTopologyOutput{},
Hosts: []api.HostTopologyOutput{},
}
if len(swire.ZoneId) > 0 {
zone, _ := swire.GetZone()
if zone != nil {
ret.Zone = zone.Name
}
}
hosts, err := swire.GetHosts()
if err != nil {
return nil, errors.Wrapf(err, "GetHosts for wire %s", swire.Id)
}
for i := range hosts {
hns := hosts[i].GetBaremetalnetworks()
hss := hosts[i]._getAttachedStorages(tristate.None, tristate.None, nil)
host := api.HostTopologyOutput{
Name: hosts[i].Name,
Id: hosts[i].Id,
Status: hosts[i].Status,
HostStatus: hosts[i].HostStatus,
HostType: hosts[i].HostType,
Networks: []api.HostnetworkTopologyOutput{},
Schedtags: GetSchedtagsDetailsToResourceV2(&hosts[i], ctx),
}
for j := range hns {
host.Networks = append(host.Networks, api.HostnetworkTopologyOutput{
IpAddr: hns[j].IpAddr,
MacAddr: hns[j].MacAddr,
})
}
for j := range hss {
host.Storages = append(host.Storages, api.StorageShortDesc{
Name: hss[j].Name,
Id: hss[j].Id,
Status: hss[j].Status,
Enabled: hss[j].Enabled.Bool(),
StorageType: hss[j].StorageType,
CapacityMb: hss[j].Capacity,
})
}
ret.Hosts = append(ret.Hosts, host)
}
networks, err := swire.GetNetworks(nil, nil, rbacscope.ScopeSystem)
if err != nil {
return nil, errors.Wrapf(err, "GetNetworks")
}
for j := range networks {
network := api.NetworkTopologyOutput{
Name: networks[j].Name,
Status: networks[j].Status,
GuestIpStart: networks[j].GuestIpStart,
GuestIpEnd: networks[j].GuestIpEnd,
GuestIpMask: networks[j].GuestIpMask,
ServerType: networks[j].ServerType,
VlanId: networks[j].VlanId,
Address: []api.SNetworkUsedAddress{},
}
netAddrs := make([]api.SNetworkUsedAddress, 0)
q := networks[j].getUsedAddressQuery(userCred, userCred, rbacscope.ScopeSystem, false)
err = q.All(&netAddrs)
if err != nil {
return nil, errors.Wrapf(err, "q.All")
}
sort.Sort(SNetworkUsedAddressList(netAddrs))
network.Address = netAddrs
ret.Networks = append(ret.Networks, network)
}
return ret, nil
}
func (wire *SWire) GetCloudproviderId() string {
return wire.SManagedResourceBase.GetCloudproviderId()
}
func (wire *SWire) GetCloudprovider() *SCloudprovider {
return wire.SManagedResourceBase.GetCloudprovider()
}
func (wire *SWire) GetProviderName() string {
return wire.SManagedResourceBase.GetProviderName()
}
func (wire *SWire) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
if err := NetworkAdditionalWireManager.DeleteWire(ctx, wire.Id); err != nil {
return errors.Wrap(err, "NetworkAdditionalWireManager.DeleteWire")
}
return wire.SStatusInfrasResourceBase.Delete(ctx, userCred)
}