diff --git a/cmd/climc/shell/compute/vpc_peering_connection.go b/cmd/climc/shell/compute/vpc_peering_connection.go new file mode 100644 index 0000000000..4c1965266b --- /dev/null +++ b/cmd/climc/shell/compute/vpc_peering_connection.go @@ -0,0 +1,31 @@ +// 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 compute + +import ( + "yunion.io/x/onecloud/cmd/climc/shell" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + cmd := shell.NewResourceCmd(&modules.VpcPeeringConnections).WithKeyword("vpc-peering-connection") + cmd.List(&options.VpcPeeringConnectionListOptions{}) + cmd.Show(&options.VpcPeeringConnectionIdOptions{}) + cmd.Create(&options.VpcPeeringConnectionCreateOptions{}) + cmd.Delete(&options.VpcPeeringConnectionIdOptions{}) + cmd.Perform("sync", &options.VpcPeeringConnectionIdOptions{}) + cmd.Perform("syncstatus", &options.VpcPeeringConnectionIdOptions{}) +} diff --git a/pkg/apis/compute/vpc_peering_connection.go b/pkg/apis/compute/vpc_peering_connection.go new file mode 100644 index 0000000000..482074dea2 --- /dev/null +++ b/pkg/apis/compute/vpc_peering_connection.go @@ -0,0 +1,52 @@ +// 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 compute + +import "yunion.io/x/onecloud/pkg/apis" + +const ( + VPC_PEERING_CONNECTION_STATUS_CREATING = "creating" + VPC_PEERING_CONNECTION_STATUS_CREATE_FAILED = "create_failed" + VPC_PEERING_CONNECTION_STATUS_DELETE_FAILED = "delete_failed" + VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT = "pending-acceptance" + VPC_PEERING_CONNECTION_STATUS_ACTIVE = "active" + VPC_PEERING_CONNECTION_STATUS_DELETING = "deleting" + VPC_PEERING_CONNECTION_STATUS_UNKNOWN = "unknown" +) + +type VpcPeeringConnectionDetails struct { + apis.EnabledStatusInfrasResourceBaseDetails + VpcName string + PeerVpcName string +} + +type VpcPeeringConnectionCreateInput struct { + apis.EnabledStatusInfrasResourceBaseCreateInput + SVpcResourceBase + PeerVpcId string + //跨区域vpc对等连接带宽,仅对腾讯云有效 + //单位Mbps,可选值 10,20,50,100,200,500,1000 + Bandwidth int +} + +type VpcPeeringConnectionListInput struct { + apis.EnabledStatusInfrasResourceBaseListInput + VpcId string + PeerVpcId string +} + +type VpcPeeringConnectionUpdateInput struct { + apis.EnabledStatusInfrasResourceBaseUpdateInput +} diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 9804348f17..9faaa07946 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -204,6 +204,11 @@ type ICloudProviderFactory interface { IsSystemCloudpolicyUnified() bool // 国内国外权限是否一致 + IsSupportCrossCloudEnvVpcPeering() bool + IsSupportCrossRegionVpcPeering() bool + IsSupportVpcPeeringVpcCidrOverlap() bool + ValidateCrossRegionVpcPeeringBandWidth(bandwidth int) error + GetSupportedDnsZoneTypes() []TDnsZoneType GetSupportedDnsTypes() map[TDnsZoneType][]TDnsType GetSupportedDnsPolicyTypes() map[TDnsZoneType][]TDnsPolicyType @@ -598,6 +603,22 @@ func (factory *baseProviderFactory) IsSystemCloudpolicyUnified() bool { return true } +func (factory *baseProviderFactory) IsSupportCrossCloudEnvVpcPeering() bool { + return false +} + +func (factory *baseProviderFactory) IsSupportCrossRegionVpcPeering() bool { + return false +} + +func (factory *baseProviderFactory) IsSupportVpcPeeringVpcCidrOverlap() bool { + return false +} + +func (factory *baseProviderFactory) ValidateCrossRegionVpcPeeringBandWidth(bandwidth int) error { + return nil +} + func (factory *baseProviderFactory) GetSupportedDnsZoneTypes() []TDnsZoneType { return []TDnsZoneType{} } diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 5eef0ebbcc..91055ab145 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -447,6 +447,12 @@ type ICloudVpc interface { GetIWireById(wireId string) (ICloudWire, error) GetINatGateways() ([]ICloudNatGateway, error) + + GetICloudVpcPeeringConnections() ([]ICloudVpcPeeringConnection, error) + GetICloudVpcPeeringConnectionById(id string) (ICloudVpcPeeringConnection, error) + CreateICloudVpcPeeringConnection(opts *VpcPeeringConnectionCreateOptions) (ICloudVpcPeeringConnection, error) + AcceptICloudVpcPeeringConnection(id string) error + GetAuthorityOwnerId() string } type ICloudWire interface { @@ -1053,3 +1059,13 @@ type ICloudDnsRecordSet interface { GetPolicyValue() TDnsPolicyValue GetPolicyOptions() *jsonutils.JSONDict } + +type ICloudVpcPeeringConnection interface { + ICloudResource + + GetPeerVpcId() string + GetPeerAccountId() string + GetEnabled() bool + + Delete() error +} diff --git a/pkg/cloudprovider/vpc_peering_connection.go b/pkg/cloudprovider/vpc_peering_connection.go new file mode 100644 index 0000000000..a5adb53c41 --- /dev/null +++ b/pkg/cloudprovider/vpc_peering_connection.go @@ -0,0 +1,24 @@ +// 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 cloudprovider + +type VpcPeeringConnectionCreateOptions struct { + Name string + Desc string + PeerVpcId string + PeerAccountId string + PeerRegionId string + Bandwidth int //qcloud cross region,Mbps +} diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 765db3bcbc..ae571c4b22 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -250,10 +250,30 @@ func syncRegionVPCs(ctx context.Context, userCred mcclient.TokenCredential, sync } syncVpcRouteTables(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) syncVpcNatgateways(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) + syncVpcPeerConnections(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) }() } } +func syncVpcPeerConnections(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localVpc *SVpc, remoteVpc cloudprovider.ICloudVpc, syncRange *SSyncRange) { + peerConnections, err := remoteVpc.GetICloudVpcPeeringConnections() + if err != nil { + if errors.Cause(err) == cloudprovider.ErrNotImplemented || errors.Cause(err) == cloudprovider.ErrNotSupported { + return + } + log.Errorf("GetICloudVpcPeeringConnections for vpc %s failed %v", localVpc.Name, err) + return + } + + result := localVpc.SyncVpcPeeringConnections(ctx, userCred, peerConnections) + syncResults.Add(VpcPeeringConnectionManager, result) + + log.Infof("SyncVpcPeeringConnections for vpc %s result: %s", localVpc.Name, result.Result()) + if result.IsError() { + return + } +} + func syncVpcSecGroup(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localVpc *SVpc, remoteVpc cloudprovider.ICloudVpc, syncRange *SSyncRange) { secgroups, err := remoteVpc.GetISecurityGroups() if err != nil { diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index 36b220a028..f09c65a2fa 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -1014,11 +1014,30 @@ func (vpc *SVpc) purgeWires(ctx context.Context, userCred mcclient.TokenCredenti return nil } +func (vpc *SVpc) purgeVpcPeeringConnections(ctx context.Context, userCred mcclient.TokenCredential) error { + vpcPCs, err := vpc.GetVpcPeeringConnections() + if err != nil { + return err + } + for i := range vpcPCs { + err := vpcPCs[i].purge(ctx, userCred) + if err != nil { + return err + } + } + return nil +} + func (vpc *SVpc) Purge(ctx context.Context, userCred mcclient.TokenCredential) error { lockman.LockObject(ctx, vpc) defer lockman.ReleaseObject(ctx, vpc) - err := vpc.purgeWires(ctx, userCred) + err := vpc.purgeVpcPeeringConnections(ctx, userCred) + if err != nil { + return err + } + + err = vpc.purgeWires(ctx, userCred) if err != nil { return err } @@ -1718,3 +1737,9 @@ func (manager *SPolicyDefinitionManager) purgeAll(ctx context.Context, userCred } return nil } + +func (vpcPC *SVpcPeeringConnection) purge(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, vpcPC) + defer lockman.ReleaseObject(ctx, vpcPC) + return vpcPC.RealDelete(ctx, userCred) +} diff --git a/pkg/compute/models/vpc_peering_connections.go b/pkg/compute/models/vpc_peering_connections.go new file mode 100644 index 0000000000..e24f8d64c8 --- /dev/null +++ b/pkg/compute/models/vpc_peering_connections.go @@ -0,0 +1,370 @@ +// 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" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/netutils" + "yunion.io/x/sqlchemy" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SVpcPeeringConnectionManager struct { + db.SEnabledStatusInfrasResourceBaseManager + db.SExternalizedResourceBaseManager +} + +var VpcPeeringConnectionManager *SVpcPeeringConnectionManager + +func init() { + VpcPeeringConnectionManager = &SVpcPeeringConnectionManager{ + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( + SVpcPeeringConnection{}, + "vpc_peering_connections_tbl", + "vpc_peering_connection", + "vpc_peering_connections", + ), + } + VpcPeeringConnectionManager.SetVirtualObject(VpcPeeringConnectionManager) +} + +type SVpcPeeringConnection struct { + db.SEnabledStatusInfrasResourceBase + db.SExternalizedResourceBase + + SVpcResourceBase + PeerVpcId string `width:"36" charset:"ascii" nullable:"true" list:"domain" create:"required" json:"peer_vpc_id"` + PeerAccountId string `width:"36" charset:"ascii" nullable:"true" list:"domain"` + Bandwidth int `nullable:"false" default:"0" list:"user" create:"optional"` +} + +func (manager *SVpcPeeringConnectionManager) GetContextManagers() [][]db.IModelManager { + return [][]db.IModelManager{ + {VpcManager}, + } +} + +// 列表 +func (manager *SVpcPeeringConnectionManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.VpcPeeringConnectionListInput, +) (*sqlchemy.SQuery, error) { + var err error + q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter") + } + + if len(query.VpcId) > 0 { + vpc, err := VpcManager.FetchByIdOrName(userCred, query.VpcId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2("vpc_id", query.VpcId) + } + return nil, httperrors.NewGeneralError(err) + } + q = q.Equals("vpc_id", vpc.GetId()) + } + if len(query.PeerVpcId) > 0 { + peerVpc, err := VpcManager.FetchByIdOrName(userCred, query.PeerVpcId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError("peer_vpc_id", query.PeerVpcId) + } + return nil, httperrors.NewGeneralError(err) + } + q = q.Equals("peer_vpc_id", peerVpc.GetId()) + } + return q, nil +} + +// 创建 +func (manager *SVpcPeeringConnectionManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.VpcPeeringConnectionCreateInput, +) (api.VpcPeeringConnectionCreateInput, error) { + var err error + input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput) + if err != nil { + return input, err + } + if len(input.VpcId) == 0 { + return input, httperrors.NewInputParameterError("vpc_id") + } + + // get vpc ,peerVpc + _vpc, err := VpcManager.FetchByIdOrName(userCred, input.VpcId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return input, httperrors.NewResourceNotFoundError2("vpc", input.VpcId) + } + return input, httperrors.NewGeneralError(err) + } + vpc := _vpc.(*SVpc) + + _peerVpc, err := VpcManager.FetchByIdOrName(userCred, input.PeerVpcId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return input, httperrors.NewResourceNotFoundError2("Peervpc", input.PeerVpcId) + } + return input, httperrors.NewGeneralError(err) + } + peerVpc := _peerVpc.(*SVpc) + + // get account,providerFactory + account := vpc.GetCloudaccount() + peerAccount := peerVpc.GetCloudaccount() + if account.Provider != peerAccount.Provider { + return input, httperrors.NewNotSupportedError("vpc on different cloudprovider peering is not supported") + } + + factory, err := cloudprovider.GetProviderFactory(account.Provider) + if err != nil { + return input, httperrors.NewGeneralError(errors.Wrapf(err, "cloudprovider.GetProviderFactory(%s)", account.Provider)) + } + + // check vpc ip range overlap + if !factory.IsSupportVpcPeeringVpcCidrOverlap() { + vpcIpv4Ranges := []netutils.IPV4AddrRange{} + peervpcIpv4Ranges := []netutils.IPV4AddrRange{} + vpcCidrBlocks := strings.Split(vpc.CidrBlock, ",") + peervpcCidrBlocks := strings.Split(peerVpc.CidrBlock, ",") + for i := range vpcCidrBlocks { + vpcIpv4Range, err := newIPv4RangeFromCIDR(vpcCidrBlocks[i]) + if err != nil { + return input, httperrors.NewGeneralError(errors.Wrapf(err, "convert vpc cidr %s to ipv4range error", vpcCidrBlocks[i])) + } + vpcIpv4Ranges = append(vpcIpv4Ranges, vpcIpv4Range) + } + + for i := range peervpcCidrBlocks { + peervpcIpv4Range, err := newIPv4RangeFromCIDR(peervpcCidrBlocks[i]) + if err != nil { + return input, httperrors.NewGeneralError(errors.Wrapf(err, "convert vpc cidr %s to ipv4range error", peervpcCidrBlocks[i])) + } + peervpcIpv4Ranges = append(peervpcIpv4Ranges, peervpcIpv4Range) + } + for i := range vpcIpv4Ranges { + for j := range peervpcIpv4Ranges { + if vpcIpv4Ranges[i].IsOverlap(peervpcIpv4Ranges[j]) { + return input, httperrors.NewNotSupportedError("ipv4 range overlap") + } + } + } + } + + CrossCloudEnv := account.AccessUrl != peerAccount.AccessUrl + CrossRegion := vpc.CloudregionId != peerVpc.CloudregionId + if CrossCloudEnv && !factory.IsSupportCrossCloudEnvVpcPeering() { + return input, httperrors.NewNotSupportedError("cloudprovider %s %s %s %s %s not supported CrossCloud vpcpeering", account.Provider, account.AccessUrl, vpc.CloudregionId, peerAccount.AccessUrl, peerVpc.CloudregionId) + } + if CrossRegion && !factory.IsSupportCrossRegionVpcPeering() { + return input, httperrors.NewNotSupportedError("cloudprovider %s %s %s %s %s not supported CrossRegion vpcpeering", account.Provider, account.AccessUrl, vpc.CloudregionId, peerAccount.AccessUrl, peerVpc.CloudregionId) + } + if CrossRegion { + err := factory.ValidateCrossRegionVpcPeeringBandWidth(input.Bandwidth) + if err != nil { + return input, err + } + } + + // existed peer check + vpcPC := SVpcPeeringConnection{} + err = manager.Query().Equals("vpc_id", vpc.Id).Equals("peer_vpc_id", peerVpc.Id).First(&vpcPC) + if err == nil { + return input, httperrors.NewNotSupportedError("vpc %s and vpc %s have already connected", input.VpcId, input.PeerVpcId) + } else { + if errors.Cause(err) != sql.ErrNoRows { + return input, httperrors.NewGeneralError(err) + } + } + + return input, nil +} + +func (self *SVpcPeeringConnection) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { + params := jsonutils.NewDict() + task, err := taskman.TaskManager.NewTask(ctx, "VpcPeeringConnectionCreateTask", self, userCred, params, "", "", nil) + if err != nil { + return + } + self.SetStatus(userCred, api.VPC_PEERING_CONNECTION_STATUS_CREATING, "") + task.ScheduleRun(nil) +} + +func (self *SVpcPeeringConnection) GetExtraDetails( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + isList bool, +) (api.VpcPeeringConnectionDetails, error) { + return api.VpcPeeringConnectionDetails{}, nil +} + +func (manager *SVpcPeeringConnectionManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []api.VpcPeeringConnectionDetails { + rows := make([]api.VpcPeeringConnectionDetails, len(objs)) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + vpcIds := make([]string, len(objs)) + peerVpcIds := make([]string, len(objs)) + for i := range rows { + rows[i] = api.VpcPeeringConnectionDetails{ + EnabledStatusInfrasResourceBaseDetails: stdRows[i], + } + vpcPC := objs[i].(*SVpcPeeringConnection) + vpcIds[i] = vpcPC.VpcId + peerVpcIds[i] = vpcPC.PeerVpcId + } + + vpcMap, err := db.FetchIdNameMap2(VpcManager, vpcIds) + if err != nil { + return rows + } + peerVpcMap, err := db.FetchIdNameMap2(VpcManager, peerVpcIds) + if err != nil { + return rows + } + + for i := range rows { + rows[i].VpcName, _ = vpcMap[vpcIds[i]] + rows[i].PeerVpcName, _ = peerVpcMap[peerVpcIds[i]] + } + return rows +} + +func (self *SVpcPeeringConnection) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { + return self.StartDeleteVpcPeeringConnectionTask(ctx, userCred) +} + +func (self *SVpcPeeringConnection) StartDeleteVpcPeeringConnectionTask(ctx context.Context, userCred mcclient.TokenCredential) error { + self.SetStatus(userCred, api.VPC_PEERING_CONNECTION_STATUS_DELETING, "") + task, err := taskman.TaskManager.NewTask(ctx, "VpcPeeringConnectionDeleteTask", self, userCred, nil, "", "", nil) + if err != nil { + return errors.Wrap(err, "Start VpcPeeringConnectionDeleteTask fail") + } + task.ScheduleRun(nil) + return nil +} + +func (self *SVpcPeeringConnection) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { + return nil +} + +func (self *SVpcPeeringConnection) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error { + return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) +} + +func (self *SVpcPeeringConnection) AllowPerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "syncstatus") +} + +// 同步状态 +func (self *SVpcPeeringConnection) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.VpcSyncstatusInput) (jsonutils.JSONObject, error) { + return nil, StartResourceSyncStatusTask(ctx, userCred, self, "VpcPeeringConnectionSyncstatusTask", "") +} + +func (manager *SVpcPeeringConnectionManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.VpcPeeringConnectionListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") + } + return q, nil +} + +func (manager *SVpcPeeringConnectionManager) ListItemExportKeys(ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + keys stringutils2.SSortedStrings, +) (*sqlchemy.SQuery, error) { + q, err := manager.SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys") + } + return q, nil +} + +func (self *SVpcPeeringConnection) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.VpcPeeringConnectionUpdateInput) (api.VpcPeeringConnectionUpdateInput, error) { + var err error + input.EnabledStatusInfrasResourceBaseUpdateInput, err = self.SEnabledStatusInfrasResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusInfrasResourceBaseUpdateInput) + if err != nil { + return input, err + } + return input, nil +} + +func (self *SVpcPeeringConnection) syncRemove(ctx context.Context, userCred mcclient.TokenCredential) error { + return self.RealDelete(ctx, userCred) +} + +func (self *SVpcPeeringConnection) SyncWithCloudPeerConnection(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudVpcPeeringConnection, provider *SCloudprovider) error { + _, err := db.Update(self, func() error { + self.Status = ext.GetStatus() + self.ExternalId = ext.GetGlobalId() + self.PeerAccountId = ext.GetPeerAccountId() + return nil + }) + if err != nil { + return errors.Wrapf(err, "db.Update") + } + if provider != nil { + SyncCloudDomain(userCred, self, provider.GetOwnerId()) + self.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) + } + return nil +} + +func (self *SVpcPeeringConnection) GetVpc() (*SVpc, error) { + vpc, err := VpcManager.FetchById(self.VpcId) + if err != nil { + return nil, errors.Wrapf(err, "VpcManager.FetchById(%s)", self.VpcId) + } + return vpc.(*SVpc), nil +} + +func (self *SVpcPeeringConnection) GetPeerVpc() (*SVpc, error) { + vpc, err := VpcManager.FetchById(self.PeerVpcId) + if err != nil { + return nil, errors.Wrapf(err, "VpcManager.FetchById(%s)", self.VpcId) + } + return vpc.(*SVpc), nil +} diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 68bbfe101b..4a2c13432a 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -23,6 +23,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/utils" @@ -143,6 +144,45 @@ func (self *SVpc) GetNatgateways() ([]SNatGateway, error) { return nats, nil } +func (self *SVpc) getRequesterVpcPeeringConnectionQuery() *sqlchemy.SQuery { + return VpcPeeringConnectionManager.Query().Equals("vpc_id", self.Id) +} + +func (self *SVpc) getAccepterVpcPeeringConnectionQuery() *sqlchemy.SQuery { + return VpcPeeringConnectionManager.Query().Equals("peer_vpc_id", self.Id) +} + +func (self *SVpc) GetRequesterVpcPeeringConnections() ([]SVpcPeeringConnection, error) { + vpcPC := []SVpcPeeringConnection{} + err := db.FetchModelObjects(VpcPeeringConnectionManager, self.getRequesterVpcPeeringConnectionQuery(), &vpcPC) + if err != nil { + return nil, err + } + return vpcPC, nil +} + +func (self *SVpc) GetAccepterVpcPeeringConnections() ([]SVpcPeeringConnection, error) { + vpcPC := []SVpcPeeringConnection{} + err := db.FetchModelObjects(VpcPeeringConnectionManager, self.getAccepterVpcPeeringConnectionQuery(), &vpcPC) + if err != nil { + return nil, err + } + return vpcPC, nil +} +func (self *SVpc) GetVpcPeeringConnectionCount() (int, error) { + q := self.getRequesterVpcPeeringConnectionQuery() + requesterPeerCount, err := q.CountWithError() + if err != nil { + return 0, err + } + q = self.getRequesterVpcPeeringConnectionQuery() + accepterPeerCount, err := q.CountWithError() + if err != nil { + return 0, err + } + return requesterPeerCount + accepterPeerCount, nil +} + func (self *SVpc) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.VpcUpdateInput) (api.VpcUpdateInput, error) { if input.ExternalAccessMode != "" { if !utils.IsInStringArray(input.ExternalAccessMode, api.VPC_EXTERNAL_ACCESS_MODES) { @@ -174,6 +214,14 @@ func (self *SVpc) ValidateDeleteCondition(ctx context.Context) error { if self.Id == api.DEFAULT_VPC_ID { return httperrors.NewProtectedResourceError("not allow to delete default vpc") } + cnt, err = self.GetVpcPeeringConnectionCount() + if err != nil { + return httperrors.NewInternalServerError("GetRequesterVpcPeeringConnections fail %v", err) + } + if cnt > 0 { + return httperrors.NewNotEmptyError("VPC peering not empty, please delete vpc peering first") + } + return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx) } @@ -589,6 +637,22 @@ func (self *SVpc) markAllNetworksUnknown(userCred mcclient.TokenCredential) erro return nil } +func (self *SVpc) SyncRouteTables(ctx context.Context, userCred mcclient.TokenCredential) error { + ivpc, err := self.GetIVpc() + if err != nil { + return errors.Wrap(err, "self.GetIVpc()") + } + routeTables, err := ivpc.GetIRouteTables() + if err != nil { + return errors.Wrapf(err, "GetIRouteTables for vpc %s failed", ivpc.GetId()) + } + _, _, result := RouteTableManager.SyncRouteTables(ctx, userCred, self, routeTables, self.GetCloudprovider()) + if result.IsError() { + return errors.Wrapf(result.AllError(), "RouteTableManager.SyncRouteTables(%s,%s)", jsonutils.Marshal(self).String(), jsonutils.Marshal(routeTables).String()) + } + return nil +} + func (manager *SVpcManager) InitializeData() error { if vpcObj, err := manager.FetchById(api.DEFAULT_VPC_ID); err != nil { if err == sql.ErrNoRows { @@ -1357,3 +1421,100 @@ func (vpc *SVpc) PerformChangeOwner(ctx context.Context, userCred mcclient.Token return nil, nil } + +func (self *SVpc) GetVpcPeeringConnections() ([]SVpcPeeringConnection, error) { + q := VpcPeeringConnectionManager.Query().Equals("vpc_id", self.Id) + peers := []SVpcPeeringConnection{} + err := db.FetchModelObjects(VpcPeeringConnectionManager, q, &peers) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return peers, nil +} + +func (self *SVpc) SyncVpcPeeringConnections(ctx context.Context, userCred mcclient.TokenCredential, exts []cloudprovider.ICloudVpcPeeringConnection) compare.SyncResult { + result := compare.SyncResult{} + + dbPeers, err := self.GetVpcPeeringConnections() + if err != nil { + result.Error(errors.Wrapf(err, "GetVpcPeeringConnections")) + return result + } + + provider := self.GetCloudprovider() + + removed := make([]SVpcPeeringConnection, 0) + commondb := make([]SVpcPeeringConnection, 0) + commonext := make([]cloudprovider.ICloudVpcPeeringConnection, 0) + added := make([]cloudprovider.ICloudVpcPeeringConnection, 0) + + err = compare.CompareSets(dbPeers, exts, &removed, &commondb, &commonext, &added) + if err != nil { + result.Error(err) + return result + } + + for i := 0; i < len(removed); i += 1 { + if len(removed[i].ExternalId) > 0 { + err = removed[i].syncRemove(ctx, userCred) + if err != nil { + result.DeleteError(err) + continue + } + result.Delete() + } + } + + for i := 0; i < len(commondb); i += 1 { + err = commondb[i].SyncWithCloudPeerConnection(ctx, userCred, commonext[i], provider) + if err != nil { + result.UpdateError(err) + continue + } + syncMetadata(ctx, userCred, &commondb[i], commonext[i]) + result.Update() + } + + for i := 0; i < len(added); i += 1 { + _, err := self.newFromCloudPeerConnection(ctx, userCred, added[i], provider) + if err != nil { + result.AddError(errors.Wrapf(err, "newFromCloudPeerConnection")) + continue + } + result.Add() + } + + return result +} + +func (self *SVpc) newFromCloudPeerConnection(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudVpcPeeringConnection, provider *SCloudprovider) (*SVpcPeeringConnection, error) { + peer := &SVpcPeeringConnection{} + peer.SetModelManager(VpcPeeringConnectionManager, peer) + peer.Name, _ = db.GenerateName(VpcPeeringConnectionManager, provider.GetOwnerId(), ext.GetName()) + peer.ExternalId = ext.GetGlobalId() + peer.Status = ext.GetStatus() + peer.VpcId = self.Id + peer.Enabled = tristate.True + peer.PeerVpcId = ext.GetPeerVpcId() + manager := self.GetCloudprovider() + peerVpc, _ := db.FetchByExternalIdAndManagerId(VpcManager, peer.PeerVpcId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { + managerQ := CloudproviderManager.Query("id").Equals("provider", manager.Provider) + return q.In("manager_id", managerQ.SubQuery()) + }) + if peerVpc != nil { + peer.PeerVpcId = peerVpc.GetId() + } + peer.PeerAccountId = ext.GetPeerAccountId() + err := VpcPeeringConnectionManager.TableSpec().Insert(ctx, peer) + if err != nil { + return nil, errors.Wrapf(err, "Insert") + } + + if provider != nil { + SyncCloudDomain(userCred, peer, provider.GetOwnerId()) + peer.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) + } + + db.OpsLog.LogEvent(peer, db.ACT_CREATE, peer.GetShortDesc(ctx), userCred) + return peer, nil +} diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index 13e33debf9..759bfcaad1 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -194,6 +194,8 @@ func InitHandlers(app *appsrv.Application) { models.DnsZoneCacheManager, models.DnsRecordSetManager, models.DnsTrafficPolicyManager, + + models.VpcPeeringConnectionManager, } { db.RegisterModelManager(manager) handler := db.NewModelHandler(manager) diff --git a/pkg/compute/tasks/vpc_peering_connection_create_task.go b/pkg/compute/tasks/vpc_peering_connection_create_task.go new file mode 100644 index 0000000000..3d96a61e5b --- /dev/null +++ b/pkg/compute/tasks/vpc_peering_connection_create_task.go @@ -0,0 +1,104 @@ +// 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 tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type VpcPeeringConnectionCreateTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(VpcPeeringConnectionCreateTask{}) +} + +func (self *VpcPeeringConnectionCreateTask) taskFailed(ctx context.Context, peer *models.SVpcPeeringConnection, err error) { + peer.SetStatus(self.UserCred, api.VPC_PEERING_CONNECTION_STATUS_CREATE_FAILED, err.Error()) + db.OpsLog.LogEvent(peer, db.ACT_ALLOCATE_FAIL, err, self.UserCred) + logclient.AddActionLogWithStartable(self, peer, logclient.ACT_ALLOCATE, err, self.UserCred, false) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *VpcPeeringConnectionCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + peer := obj.(*models.SVpcPeeringConnection) + + vpc, err := peer.GetVpc() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetVpc")) + return + } + + peerVpc, err := peer.GetPeerVpc() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetPeerVpc")) + return + } + + iVpc, err := vpc.GetIVpc() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetIVpc")) + return + } + + iPeerVpc, err := peerVpc.GetIVpc() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetIVpc")) + return + } + + opts := &cloudprovider.VpcPeeringConnectionCreateOptions{ + Name: peer.Name, + Desc: peer.Description, + Bandwidth: peer.Bandwidth, + PeerVpcId: iPeerVpc.GetId(), + PeerRegionId: iPeerVpc.GetRegion().GetId(), + PeerAccountId: iPeerVpc.GetAuthorityOwnerId(), + } + iPeerConnection, err := iVpc.CreateICloudVpcPeeringConnection(opts) + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "CreateICloudVpcPeeringConnection")) + return + } + err = iPeerVpc.AcceptICloudVpcPeeringConnection(iPeerConnection.GetGlobalId()) + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "AcceptICloudVpcPeeringConnection")) + return + } + + iPeerConnection.Refresh() + err = peer.SyncWithCloudPeerConnection(ctx, self.GetUserCred(), iPeerConnection, nil) + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "SyncWithCloudPeerConnection")) + return + } + self.taskComplete(ctx, peer) +} + +func (self *VpcPeeringConnectionCreateTask) taskComplete(ctx context.Context, peer *models.SVpcPeeringConnection) { + logclient.AddActionLogWithStartable(self, peer, logclient.ACT_CREATE, nil, self.UserCred, true) + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/compute/tasks/vpc_peering_connection_delete_task.go b/pkg/compute/tasks/vpc_peering_connection_delete_task.go new file mode 100644 index 0000000000..cb5e28f2e1 --- /dev/null +++ b/pkg/compute/tasks/vpc_peering_connection_delete_task.go @@ -0,0 +1,89 @@ +// 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 tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type VpcPeeringConnectionDeleteTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(VpcPeeringConnectionDeleteTask{}) +} + +func (self *VpcPeeringConnectionDeleteTask) taskFailed(ctx context.Context, peer *models.SVpcPeeringConnection, err error) { + peer.SetStatus(self.UserCred, api.VPC_PEERING_CONNECTION_STATUS_DELETE_FAILED, err.Error()) + db.OpsLog.LogEvent(peer, db.ACT_DELOCATE_FAIL, err, self.UserCred) + logclient.AddActionLogWithStartable(self, peer, logclient.ACT_DELETE, err, self.UserCred, false) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *VpcPeeringConnectionDeleteTask) taskComplete(ctx context.Context, peer *models.SVpcPeeringConnection) { + logclient.AddActionLogWithStartable(self, peer, logclient.ACT_DELETE, nil, self.UserCred, true) + peer.RealDelete(ctx, self.GetUserCred()) + self.SetStageComplete(ctx, nil) +} + +func (self *VpcPeeringConnectionDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + peer := obj.(*models.SVpcPeeringConnection) + + vpc, err := peer.GetVpc() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetVpc")) + return + } + + iVpc, err := vpc.GetIVpc() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetIVpc")) + return + } + + if len(peer.ExternalId) == 0 { + self.taskComplete(ctx, peer) + return + } + + iPeer, err := iVpc.GetICloudVpcPeeringConnectionById(peer.ExternalId) + if err != nil { + if errors.Cause(err) != cloudprovider.ErrNotFound { + self.taskFailed(ctx, peer, errors.Wrapf(err, "GetICloudVpcPeeringConnectionById(%s)", peer.ExternalId)) + return + } + self.taskComplete(ctx, peer) + return + } + + err = iPeer.Delete() + if err != nil { + self.taskFailed(ctx, peer, errors.Wrapf(err, "Delete")) + return + } + + self.taskComplete(ctx, peer) +} diff --git a/pkg/compute/tasks/vpc_peering_connection_syncstatus_task.go b/pkg/compute/tasks/vpc_peering_connection_syncstatus_task.go new file mode 100644 index 0000000000..ec2da1449a --- /dev/null +++ b/pkg/compute/tasks/vpc_peering_connection_syncstatus_task.go @@ -0,0 +1,79 @@ +// 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 tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type VpcPeeringConnectionSyncstatusTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(VpcPeeringConnectionSyncstatusTask{}) +} + +func (self *VpcPeeringConnectionSyncstatusTask) taskFail(ctx context.Context, peer *models.SVpcPeeringConnection, err error) { + peer.SetStatus(self.UserCred, api.VPC_PEERING_CONNECTION_STATUS_UNKNOWN, err.Error()) + db.OpsLog.LogEvent(peer, db.ACT_SYNC_STATUS, err, self.GetUserCred()) + logclient.AddActionLogWithStartable(self, peer, logclient.ACT_SYNC_STATUS, err, self.UserCred, false) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *VpcPeeringConnectionSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + peer := obj.(*models.SVpcPeeringConnection) + + svpc, err := peer.GetVpc() + if err != nil { + self.taskFail(ctx, peer, errors.Wrap(err, "peer.GetVpc()")) + return + } + + extVpc, err := svpc.GetIVpc() + if err != nil { + self.taskFail(ctx, peer, errors.Wrap(err, "svpc.GetIVpc()")) + return + } + + ipeer, err := extVpc.GetICloudVpcPeeringConnectionById(peer.GetExternalId()) + if err != nil { + self.taskFail(ctx, peer, errors.Wrapf(err, "extVpc.GetICloudVpcPeeringConnectionById(%s)", peer.GetExternalId())) + return + } + + err = ipeer.Refresh() + if err != nil { + self.taskFail(ctx, peer, errors.Wrap(err, "ipeer.Refresh()")) + return + } + + err = peer.SyncWithCloudPeerConnection(ctx, self.UserCred, ipeer, nil) + if err != nil { + self.taskFail(ctx, peer, errors.Wrapf(err, "SyncWithCloudPeerConnection(ctx, self.UserCred, %s, nil)", jsonutils.Marshal(ipeer).String())) + return + } + + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/mcclient/modules/mod_vpc_peering_connection.go b/pkg/mcclient/modules/mod_vpc_peering_connection.go new file mode 100644 index 0000000000..8d8e40efd2 --- /dev/null +++ b/pkg/mcclient/modules/mod_vpc_peering_connection.go @@ -0,0 +1,29 @@ +// 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 modules + +import "yunion.io/x/onecloud/pkg/mcclient/modulebase" + +var ( + VpcPeeringConnections modulebase.ResourceManager +) + +func init() { + VpcPeeringConnections = NewComputeManager("vpc_peering_connection", "vpc_peering_connections", + []string{"ID", "Name", "Enabled", "Status", "vpc_id", "peer_vpc_id", "peer_account_id", "Public_Scope", "Domain_Id", "Domain"}, + []string{}) + + registerCompute(&VpcPeeringConnections) +} diff --git a/pkg/mcclient/options/vpc_peering_connection.go b/pkg/mcclient/options/vpc_peering_connection.go new file mode 100644 index 0000000000..facd68f0c6 --- /dev/null +++ b/pkg/mcclient/options/vpc_peering_connection.go @@ -0,0 +1,48 @@ +// 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 options + +import "yunion.io/x/jsonutils" + +type VpcPeeringConnectionListOptions struct { + BaseListOptions +} + +func (opts *VpcPeeringConnectionListOptions) Params() (jsonutils.JSONObject, error) { + return ListStructToParams(opts) +} + +type VpcPeeringConnectionIdOptions struct { + ID string `json:"Vpc peering connection ID"` +} + +func (opts *VpcPeeringConnectionIdOptions) GetId() string { + return opts.ID +} + +func (opts *VpcPeeringConnectionIdOptions) Params() (jsonutils.JSONObject, error) { + return nil, nil +} + +type VpcPeeringConnectionCreateOptions struct { + EnabledStatusCreateOptions + VpcId string + PeerVpcId string + Bandwidth int +} + +func (opts *VpcPeeringConnectionCreateOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(opts).(*jsonutils.JSONDict), nil +} diff --git a/pkg/multicloud/aws/provider/provider.go b/pkg/multicloud/aws/provider/provider.go index 1a65205caf..898aed2173 100644 --- a/pkg/multicloud/aws/provider/provider.go +++ b/pkg/multicloud/aws/provider/provider.go @@ -123,6 +123,18 @@ func (self *SAwsProviderFactory) GetSupportedDnsPolicyValues() map[cloudprovider } } +func (factory *SAwsProviderFactory) IsSupportCrossCloudEnvVpcPeering() bool { + return false +} + +func (factory *SAwsProviderFactory) IsSupportCrossRegionVpcPeering() bool { + return true +} + +func (factory *SAwsProviderFactory) IsSupportVpcPeeringVpcCidrOverlap() bool { + return false +} + func (self *SAwsProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) { output := cloudprovider.SCloudaccount{} if len(input.AccessKeyId) == 0 { diff --git a/pkg/multicloud/aws/region.go b/pkg/multicloud/aws/region.go index c4c30bc710..3d73d3d23e 100644 --- a/pkg/multicloud/aws/region.go +++ b/pkg/multicloud/aws/region.go @@ -342,20 +342,29 @@ func (self *SRegion) fetchIVpcs() error { self.ivpcs = make([]cloudprovider.ICloudVpc, 0) for _, vpc := range vpcs.Vpcs { + cidrBlockAssociationSet := []string{} + for i := range vpc.CidrBlockAssociationSet { + cidr := vpc.CidrBlockAssociationSet[i] + if cidr.CidrBlockState.State != nil && *cidr.CidrBlockState.State == "associated" { + cidrBlockAssociationSet = append(cidrBlockAssociationSet, *cidr.CidrBlock) + } + } + tags := make(map[string]string, 0) for _, tag := range vpc.Tags { tags[*tag.Key] = *tag.Value } self.ivpcs = append(self.ivpcs, &SVpc{region: self, - CidrBlock: *vpc.CidrBlock, - Tags: tags, - IsDefault: *vpc.IsDefault, - RegionId: self.RegionId, - Status: *vpc.State, - VpcId: *vpc.VpcId, - VpcName: tags["Name"], - InstanceTenancy: *vpc.InstanceTenancy, + CidrBlock: *vpc.CidrBlock, + CidrBlockAssociationSet: cidrBlockAssociationSet, + Tags: tags, + IsDefault: *vpc.IsDefault, + RegionId: self.RegionId, + Status: *vpc.State, + VpcId: *vpc.VpcId, + VpcName: tags["Name"], + InstanceTenancy: *vpc.InstanceTenancy, }) } diff --git a/pkg/multicloud/aws/route.go b/pkg/multicloud/aws/route.go index 97a103d740..0ab3b3a00b 100644 --- a/pkg/multicloud/aws/route.go +++ b/pkg/multicloud/aws/route.go @@ -23,11 +23,17 @@ import ( type SRoute struct { routetable *SRouteTable - DestinationCIDRBlock string `json:"DestinationCidrBlock"` - GatewayID *string `json:"GatewayId,omitempty"` - Origin string `json:"Origin"` - State string `json:"State"` - NatGatewayID *string `json:"NatGatewayId,omitempty"` + DestinationCIDRBlock string `json:"DestinationCidrBlock"` + Origin string `json:"Origin"` + State string `json:"State"` + // only one exist + GatewayID *string `json:"GatewayId,omitempty"` + NatGatewayID *string `json:"NatGatewayId,omitempty"` + InstanceID *string `json:"InstanceId,omitempty"` + LocalGatewayID *string `json:"LocalGatewayId,omitempty"` + NetworkInterfaceID *string `json:"NetworkInterfaceId,omitempty"` + TransitGatewayID *string `json:"TransitGatewayId,omitempty"` + VpcPeeringConnectionID *string `json:"VpcPeeringConnectionId,omitempty"` } func (self *SRoute) GetType() string { @@ -69,9 +75,27 @@ func (self *SRoute) GetNextHopType() string { } func (self *SRoute) GetNextHop() string { - if self.GatewayID == nil { - return "" + if self.NatGatewayID != nil { + return *self.NatGatewayID + } + if self.GatewayID != nil { + return *self.GatewayID + } + if self.InstanceID != nil { + return *self.InstanceID + } + if self.LocalGatewayID != nil { + return *self.LocalGatewayID + } + if self.NetworkInterfaceID != nil { + return *self.NetworkInterfaceID + } + if self.TransitGatewayID != nil { + return *self.TransitGatewayID + } + if self.VpcPeeringConnectionID != nil { + return *self.VpcPeeringConnectionID } - return *self.GatewayID + return "" } diff --git a/pkg/multicloud/aws/routetable.go b/pkg/multicloud/aws/routetable.go index c8fcebc34a..ce72990bbc 100644 --- a/pkg/multicloud/aws/routetable.go +++ b/pkg/multicloud/aws/routetable.go @@ -15,6 +15,9 @@ package aws import ( + "fmt" + "strings" + "github.com/aws/aws-sdk-go/service/ec2" "yunion.io/x/jsonutils" @@ -135,6 +138,48 @@ func (self *SRegion) GetRouteTables(vpcId string, mainRouteOnly bool) ([]SRouteT return routeTables, nil } +func (self *SRegion) CreateRoute(routeTableId string, DestinationCIDRBlock string, targetId string) error { + input := &ec2.CreateRouteInput{} + input.RouteTableId = &routeTableId + input.DestinationCidrBlock = &DestinationCIDRBlock + segs := strings.Split(targetId, "-") + if len(segs) == 0 { + return fmt.Errorf("invalid aws vpc targetid:%s", targetId) + } + switch segs[0] { + case "i": + input.InstanceId = &targetId + case "igw", "vgw": + input.GatewayId = &targetId + case "pcx": + input.VpcPeeringConnectionId = &targetId + case "eni": + input.NetworkInterfaceId = &targetId + case "nat": + input.NatGatewayId = &targetId + case "eigw": + input.EgressOnlyInternetGatewayId = &targetId + default: + return fmt.Errorf("invalid aws vpc targetid:%s", targetId) + } + _, err := self.ec2Client.CreateRoute(input) + if err != nil { + return errors.Wrapf(err, "self.ec2Client.CreateRoute(%s)", jsonutils.Marshal(input).String()) + } + return nil +} + +func (self *SRegion) RemoveRoute(routeTableId string, DestinationCIDRBlock string) error { + input := &ec2.DeleteRouteInput{} + input.RouteTableId = &routeTableId + input.DestinationCidrBlock = &DestinationCIDRBlock + _, err := self.ec2Client.DeleteRoute(input) + if err != nil { + return errors.Wrapf(err, "self.ec2Client.DeleteRoute(%s)", jsonutils.Marshal(input).String()) + } + return nil +} + func (self *SRegion) GetRouteTablesByNetworkId(netId string) ([]SRouteTable, error) { input := &ec2.DescribeRouteTablesInput{} filter := &ec2.Filter{} diff --git a/pkg/multicloud/aws/shell/routetable.go b/pkg/multicloud/aws/shell/routetable.go index c5b32afd57..2c53870541 100644 --- a/pkg/multicloud/aws/shell/routetable.go +++ b/pkg/multicloud/aws/shell/routetable.go @@ -21,10 +21,11 @@ import ( func init() { type RouteTableListOptions struct { - VpcId string `help:"vpc id"` + VpcId string `help:"vpc id"` + MainOnly bool } shellutils.R(&RouteTableListOptions{}, "routetable-list", "List route tables", func(cli *aws.SRegion, args *RouteTableListOptions) error { - routetables, err := cli.GetRouteTables(args.VpcId, false) + routetables, err := cli.GetRouteTables(args.VpcId, args.MainOnly) if err != nil { printObject(err) return nil @@ -33,4 +34,17 @@ func init() { printList(routetables, 0, 0, 0, nil) return nil }) + + type RouteCreateOptions struct { + ROUTETABLEID string `help:"routetable id"` + CIDRBLOCK string + TARGETID string + } + shellutils.R(&RouteCreateOptions{}, "route-create", "create route", func(cli *aws.SRegion, args *RouteCreateOptions) error { + err := cli.CreateRoute(args.ROUTETABLEID, args.CIDRBLOCK, args.TARGETID) + if err != nil { + return err + } + return nil + }) } diff --git a/pkg/multicloud/aws/shell/vpc.go b/pkg/multicloud/aws/shell/vpc.go index 12497e644b..169c6c94c8 100644 --- a/pkg/multicloud/aws/shell/vpc.go +++ b/pkg/multicloud/aws/shell/vpc.go @@ -15,6 +15,7 @@ package shell import ( + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/multicloud/aws" "yunion.io/x/onecloud/pkg/util/shellutils" ) @@ -32,4 +33,87 @@ func init() { printList(vpcs, total, args.Offset, args.Limit, []string{}) return nil }) + + type VpcPeeringConnectionListOptions struct { + VPCID string + } + shellutils.R(&VpcPeeringConnectionListOptions{}, "vpc-peering-connection-list", "List vpcPeeringConnections", func(cli *aws.SRegion, args *VpcPeeringConnectionListOptions) error { + vpcPCs, err := cli.DescribeVpcPeeringConnections(args.VPCID) + if err != nil { + return err + } + printList(vpcPCs, len(vpcPCs), len(vpcPCs), len(vpcPCs), []string{}) + return nil + }) + + type VpcPeeringConnectionShowOptions struct { + ID string + } + shellutils.R(&VpcPeeringConnectionShowOptions{}, "vpc-peering-connection-show", "show vpcPeeringConnections", func(cli *aws.SRegion, args *VpcPeeringConnectionShowOptions) error { + vpcPC, err := cli.GetVpcPeeringConnectionById(args.ID) + if err != nil { + return err + } + printObject(vpcPC) + return nil + }) + + type VpcPeeringConnectionCreateOptions struct { + NAME string + VPCID string + PEERVPCID string + PEERACCOUNTID string + PEERREGIONID string + Desc string + } + shellutils.R(&VpcPeeringConnectionCreateOptions{}, "vpc-peering-connection-create", "create vpcPeeringConnection", func(cli *aws.SRegion, args *VpcPeeringConnectionCreateOptions) error { + opts := cloudprovider.VpcPeeringConnectionCreateOptions{} + opts.Desc = args.Desc + opts.Name = args.NAME + opts.PeerAccountId = args.PEERACCOUNTID + opts.PeerRegionId = args.PEERREGIONID + opts.PeerVpcId = args.PEERVPCID + + vpcPC, err := cli.CreateVpcPeeringConnection(args.VPCID, &opts) + if err != nil { + return err + } + printObject(vpcPC) + return nil + }) + + type VpcPeeringConnectionAcceptOptions struct { + ID string + } + shellutils.R(&VpcPeeringConnectionAcceptOptions{}, "vpc-peering-connection-accept", "accept vpcPeeringConnection", func(cli *aws.SRegion, args *VpcPeeringConnectionAcceptOptions) error { + vpcPC, err := cli.AcceptVpcPeeringConnection(args.ID) + if err != nil { + return err + } + printObject(vpcPC) + return nil + }) + + type VpcPeeringConnectionDeleteOptions struct { + ID string + } + shellutils.R(&VpcPeeringConnectionDeleteOptions{}, "vpc-peering-connection-delete", "delete vpcPeeringConnection", func(cli *aws.SRegion, args *VpcPeeringConnectionDeleteOptions) error { + err := cli.DeleteVpcPeeringConnection(args.ID) + if err != nil { + return err + } + return nil + }) + + type VpcPeeringConnectionRouteDeleteOptions struct { + ID string + } + shellutils.R(&VpcPeeringConnectionAcceptOptions{}, "vpc-peering-connection-route-delete", "delete vpc-peering-connection route", func(cli *aws.SRegion, args *VpcPeeringConnectionAcceptOptions) error { + err := cli.DeleteVpcPeeringConnectionRoute(args.ID) + if err != nil { + printObject(err) + return err + } + return nil + }) } diff --git a/pkg/multicloud/aws/vpc.go b/pkg/multicloud/aws/vpc.go index 65adc92e52..1373a758a5 100644 --- a/pkg/multicloud/aws/vpc.go +++ b/pkg/multicloud/aws/vpc.go @@ -17,10 +17,12 @@ package aws import ( "fmt" "strings" + "time" "github.com/aws/aws-sdk-go/service/ec2" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -218,6 +220,130 @@ func (self *SVpc) fetchSecurityGroups() error { return nil } +func (self *SVpc) GetICloudVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) { + svpcPCs, err := self.getRequesterVpcPeeringConnections() + if err != nil { + return nil, errors.Wrap(err, "self.getSVpcPeeringConnections()") + } + ret := []cloudprovider.ICloudVpcPeeringConnection{} + for i := range svpcPCs { + ret = append(ret, svpcPCs[i]) + } + return ret, nil +} + +func (self *SVpc) GetICloudVpcPeeringConnectionById(id string) (cloudprovider.ICloudVpcPeeringConnection, error) { + vpcPc, err := self.getSVpcPeeringConnectionById(id) + if err != nil { + return nil, errors.Wrapf(err, "getSVpcPeeringConnectionById(%s)", id) + } + return vpcPc, nil +} + +func (self *SVpc) CreateICloudVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (cloudprovider.ICloudVpcPeeringConnection, error) { + return self.createSVpcPeeringConnection(opts) +} + +func (self *SVpc) AcceptICloudVpcPeeringConnection(id string) error { + return self.acceptSVpcPeeringConnection(id) +} + +func (self *SVpc) GetAuthorityOwnerId() string { + identity, err := self.region.client.GetCallerIdentity() + if err != nil { + log.Errorf(err.Error() + "self.region.client.GetCallerIdentity()") + return "" + } + return identity.Account +} + +func (self *SVpc) getSVpcPeeringConnectionById(id string) (*SVpcPeeringConnection, error) { + vpcPC, err := self.region.GetVpcPeeringConnectionById(id) + if err != nil { + return nil, errors.Wrapf(err, "self.region.GetVpcPeeringConnectionById(%s)", id) + } + if vpcPC.Status.Code != nil && (*vpcPC.Status.Code == ec2.VpcPeeringConnectionStateReasonCodeDeleted || + *vpcPC.Status.Code == ec2.VpcPeeringConnectionStateReasonCodeDeleting) { + return nil, cloudprovider.ErrNotFound + } + svpcPC := SVpcPeeringConnection{} + svpcPC.vpc = self + svpcPC.vpcPC = vpcPC + return &svpcPC, nil +} + +func (self *SVpc) getRequesterVpcPeeringConnections() ([]*SVpcPeeringConnection, error) { + vpcPCs, err := self.region.DescribeRequesterVpcPeeringConnections(self.VpcId) + if err != nil { + return nil, errors.Wrap(err, "self.region.DescribeVpcPeeringConnections()") + } + ivpcPCs := []*SVpcPeeringConnection{} + for i := range vpcPCs { + if vpcPCs[i].Status.Code != nil && (*vpcPCs[i].Status.Code == ec2.VpcPeeringConnectionStateReasonCodeDeleted || + *vpcPCs[i].Status.Code == ec2.VpcPeeringConnectionStateReasonCodeDeleting) { + continue + } + svpcPC := SVpcPeeringConnection{} + svpcPC.vpc = self + svpcPC.vpcPC = vpcPCs[i] + ivpcPCs = append(ivpcPCs, &svpcPC) + } + return ivpcPCs, nil +} + +func (self *SVpc) createSVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (*SVpcPeeringConnection, error) { + svpcPC := SVpcPeeringConnection{} + vpcPC, err := self.region.CreateVpcPeeringConnection(self.VpcId, opts) + if err != nil { + return nil, errors.Wrapf(err, " self.region.CreateVpcPeeringConnection(%s,%s)", self.VpcId, jsonutils.Marshal(opts).String()) + } + svpcPC.vpc = self + svpcPC.vpcPC = vpcPC + err = cloudprovider.WaitMultiStatus(&svpcPC, []string{api.VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT, + api.VPC_PEERING_CONNECTION_STATUS_ACTIVE, + api.VPC_PEERING_CONNECTION_STATUS_DELETING}, 5*time.Second, 60*time.Second) + if err != nil { + return nil, errors.Wrap(err, "cloudprovider.WaitMultiStatus") + } + if svpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_DELETING { + return nil, errors.Wrapf(cloudprovider.ErrInvalidStatus, "vpcpeeringconnection:%s invalidate status", jsonutils.Marshal(svpcPC.vpcPC).String()) + } + return &svpcPC, nil +} + +func (self *SVpc) acceptSVpcPeeringConnection(id string) error { + svpcPC, err := self.getSVpcPeeringConnectionById(id) + if err != nil { + return errors.Wrapf(err, "self.getSVpcPeeringConnectionById(%s)", id) + } + // 其他region 创建的连接请求,有短暂的provisioning状态 + err = cloudprovider.WaitMultiStatus(svpcPC, []string{api.VPC_PEERING_CONNECTION_STATUS_ACTIVE, + api.VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT, + api.VPC_PEERING_CONNECTION_STATUS_DELETING}, 5*time.Second, 60*time.Second) + if err != nil { + return errors.Wrap(err, "cloudprovider.WaitMultiStatus") + } + if svpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_DELETING { + return errors.Wrapf(cloudprovider.ErrInvalidStatus, "vpcpeeringconnection:%s invalidate status", jsonutils.Marshal(svpcPC.vpcPC).String()) + } + + if svpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT { + _, err := self.region.AcceptVpcPeeringConnection(id) + if err != nil { + return errors.Wrapf(err, "self.region.AcceptVpcPeeringConnection(%s)", id) + } + } + err = cloudprovider.WaitMultiStatus(svpcPC, []string{api.VPC_PEERING_CONNECTION_STATUS_ACTIVE, + api.VPC_PEERING_CONNECTION_STATUS_DELETING}, 5*time.Second, 60*time.Second) + if err != nil { + return errors.Wrap(err, "cloudprovider.WaitMultiStatus") + } + if svpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_DELETING { + return errors.Wrapf(cloudprovider.ErrInvalidStatus, "vpcpeeringconnection:%s invalidate status", jsonutils.Marshal(svpcPC.vpcPC).String()) + } + return nil +} + func (self *SRegion) getVpc(vpcId string) (*SVpc, error) { if len(vpcId) == 0 { return nil, fmt.Errorf("GetVpc vpc id should not be empty.") @@ -299,7 +425,7 @@ func (self *SRegion) GetVpcs(vpcId []string, offset int, limit int) ([]SVpc, int cidrBlockAssociationSet := []string{} for i := range item.CidrBlockAssociationSet { cidr := item.CidrBlockAssociationSet[i] - if *cidr.CidrBlockState.State == "associated" { + if cidr.CidrBlockState.State != nil && *cidr.CidrBlockState.State == "associated" { cidrBlockAssociationSet = append(cidrBlockAssociationSet, *cidr.CidrBlock) } } diff --git a/pkg/multicloud/aws/vpc_peering_connection.go b/pkg/multicloud/aws/vpc_peering_connection.go new file mode 100644 index 0000000000..198f4d6fe6 --- /dev/null +++ b/pkg/multicloud/aws/vpc_peering_connection.go @@ -0,0 +1,287 @@ +// 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 aws + +import ( + "github.com/aws/aws-sdk-go/service/ec2" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +func (self *SRegion) DescribeVpcPeeringConnections(vpcId string) ([]*ec2.VpcPeeringConnection, error) { + result := []*ec2.VpcPeeringConnection{} + requestvpcPCs, err := self.DescribeRequesterVpcPeeringConnections(vpcId) + if err != nil { + return nil, errors.Wrapf(err, "self.DescribeRequesterVpcPeeringConnections(%s)", vpcId) + } + result = append(result, requestvpcPCs...) + acceptvpcPCs, err := self.DescribeAccepterVpcPeeringConnections(vpcId) + if err != nil { + return nil, errors.Wrapf(err, "self.DescribeRequesterVpcPeeringConnections(%s)", vpcId) + } + result = append(result, acceptvpcPCs...) + return result, nil +} + +func (self *SRegion) DescribeRequesterVpcPeeringConnections(vpcId string) ([]*ec2.VpcPeeringConnection, error) { + params := ec2.DescribeVpcPeeringConnectionsInput{} + result := []*ec2.VpcPeeringConnection{} + var maxResult int64 = 20 + params.MaxResults = &maxResult + filter := ec2.Filter{} + filter.Values = []*string{&vpcId} + // request peeringConnection + filterName := "requester-vpc-info.vpc-id" + + filter.Name = &filterName + params.Filters = []*ec2.Filter{&filter} + for { + ret, err := self.ec2Client.DescribeVpcPeeringConnections(¶ms) + if err != nil { + return nil, errors.Wrapf(err, "self.ec2Client.DescribeVpcPeeringConnections(%s)", jsonutils.Marshal(params).String()) + } + result = append(result, ret.VpcPeeringConnections...) + if ret.NextToken == nil { + break + } + params.NextToken = ret.NextToken + } + return result, nil +} + +func (self *SRegion) DescribeAccepterVpcPeeringConnections(vpcId string) ([]*ec2.VpcPeeringConnection, error) { + params := ec2.DescribeVpcPeeringConnectionsInput{} + result := []*ec2.VpcPeeringConnection{} + var maxResult int64 = 20 + params.MaxResults = &maxResult + filter := ec2.Filter{} + filter.Values = []*string{&vpcId} + // accept peeringConnection + filterName := "accepter-vpc-info.vpc-id" + filter.Name = &filterName + params.Filters = []*ec2.Filter{&filter} + for { + ret, err := self.ec2Client.DescribeVpcPeeringConnections(¶ms) + if err != nil { + return nil, errors.Wrapf(err, "self.ec2Client.DescribeVpcPeeringConnections(%s)", jsonutils.Marshal(params).String()) + } + result = append(result, ret.VpcPeeringConnections...) + if ret.NextToken == nil { + break + } + params.NextToken = ret.NextToken + } + return result, nil +} + +func (self *SRegion) GetVpcPeeringConnectionById(Id string) (*ec2.VpcPeeringConnection, error) { + params := ec2.DescribeVpcPeeringConnectionsInput{} + result := []*ec2.VpcPeeringConnection{} + var maxResult int64 = 20 + params.MaxResults = &maxResult + filter := ec2.Filter{} + filter.Values = []*string{&Id} + filterName := "vpc-peering-connection-id" + filter.Name = &filterName + params.Filters = []*ec2.Filter{&filter} + for { + ret, err := self.ec2Client.DescribeVpcPeeringConnections(¶ms) + if err != nil { + return nil, errors.Wrapf(err, "self.ec2Client.DescribeVpcPeeringConnections(%s)", jsonutils.Marshal(params).String()) + } + result = append(result, ret.VpcPeeringConnections...) + if ret.NextToken == nil { + break + } + params.NextToken = ret.NextToken + } + if len(result) < 1 { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetVpcPeeringConnectionById(%s)", Id) + } + if len(result) > 1 { + return nil, errors.Wrapf(cloudprovider.ErrDuplicateId, "GetVpcPeeringConnectionById(%s)", Id) + } + return result[0], nil +} + +func (self *SRegion) CreateVpcPeeringConnection(vpcId string, opts *cloudprovider.VpcPeeringConnectionCreateOptions) (*ec2.VpcPeeringConnection, error) { + params := ec2.CreateVpcPeeringConnectionInput{} + params.VpcId = &vpcId + params.PeerVpcId = &opts.PeerVpcId + params.PeerRegion = &opts.PeerRegionId + params.PeerOwnerId = &opts.PeerAccountId + ret, err := self.ec2Client.CreateVpcPeeringConnection(¶ms) + if err != nil { + return nil, errors.Wrapf(err, "self.ec2Client.CreateVpcPeeringConnection(%s)", jsonutils.Marshal(params).String()) + } + // add tags + tagParams := ec2.CreateTagsInput{} + tagParams.Resources = []*string{ret.VpcPeeringConnection.VpcPeeringConnectionId} + nametag := "Name" + desctag := "Description" + tagParams.Tags = []*ec2.Tag{{Key: &nametag, Value: &opts.Name}, {Key: &desctag, Value: &opts.Desc}} + _, err = self.ec2Client.CreateTags(&tagParams) + if err != nil { + return nil, errors.Wrapf(err, "self.ec2Client.CreateTags(%s)", jsonutils.Marshal(tagParams).String()) + } + ret.VpcPeeringConnection.Tags = tagParams.Tags + return ret.VpcPeeringConnection, nil +} + +func (self *SRegion) DeleteVpcPeeringConnection(vpcPeeringConnectionId string) error { + params := ec2.DeleteVpcPeeringConnectionInput{} + params.VpcPeeringConnectionId = &vpcPeeringConnectionId + _, err := self.ec2Client.DeleteVpcPeeringConnection(¶ms) + if err != nil { + return errors.Wrapf(err, "self.ec2Client.DeleteVpcPeeringConnection(%s)", jsonutils.Marshal(params).String()) + } + return nil +} + +func (self *SRegion) AcceptVpcPeeringConnection(vpcPeeringConnectionId string) (*ec2.VpcPeeringConnection, error) { + params := ec2.AcceptVpcPeeringConnectionInput{} + params.VpcPeeringConnectionId = &vpcPeeringConnectionId + ret, err := self.ec2Client.AcceptVpcPeeringConnection(¶ms) + if err != nil { + return nil, errors.Wrapf(err, "self.ec2Client.AcceptVpcPeeringConnection(%s)", jsonutils.Marshal(params).String()) + } + return ret.VpcPeeringConnection, nil +} + +func (self *SRegion) DeleteVpcPeeringConnectionRoute(vpcPeeringConnectionId string) error { + input := &ec2.DescribeRouteTablesInput{} + filters := make([]*ec2.Filter, 0) + filters = AppendSingleValueFilter(filters, "association.main", "true") + filters = AppendSingleValueFilter(filters, "route.vpc-peering-connection-id", vpcPeeringConnectionId) + input.SetFilters(filters) + routeTables := []*ec2.RouteTable{} + for { + ret, err := self.ec2Client.DescribeRouteTables(input) + if err != nil { + return errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables") + } + routeTables = append(routeTables, ret.RouteTables...) + input.NextToken = ret.NextToken + if ret.NextToken == nil { + break + } + } + for i := range routeTables { + if routeTables[i] != nil && routeTables[i].RouteTableId != nil && routeTables[i].Routes != nil { + for j := range routeTables[i].Routes { + if routeTables[i].Routes[j] != nil && + routeTables[i].Routes[j].VpcPeeringConnectionId != nil && + *routeTables[i].Routes[j].VpcPeeringConnectionId == vpcPeeringConnectionId && + routeTables[i].Routes[j].DestinationCidrBlock != nil { + err := self.RemoveRoute(*routeTables[i].RouteTableId, *routeTables[i].Routes[j].DestinationCidrBlock) + if err != nil { + return errors.Wrapf(err, "self.RemoveRoute(%s,%s)", *routeTables[i].RouteTableId, *routeTables[i].Routes[j].DestinationCidrBlock) + } + } + } + } + } + return nil +} + +type SVpcPeeringConnection struct { + multicloud.SResourceBase + vpc *SVpc + vpcPC *ec2.VpcPeeringConnection +} + +func (self *SVpcPeeringConnection) GetId() string { + if self.vpcPC.VpcPeeringConnectionId != nil { + return *self.vpcPC.VpcPeeringConnectionId + } + return "" +} + +// tags? +func (self *SVpcPeeringConnection) GetName() string { + for i := range self.vpcPC.Tags { + if self.vpcPC.Tags[i] != nil && self.vpcPC.Tags[i].Key != nil && + *self.vpcPC.Tags[i].Key == "Name" && self.vpcPC.Tags[i].Value != nil { + return *self.vpcPC.Tags[i].Value + } + } + return self.GetId() +} + +func (self *SVpcPeeringConnection) GetGlobalId() string { + return self.GetId() +} + +func (self *SVpcPeeringConnection) GetStatus() string { + if self.vpcPC.Status.Code != nil { + switch *self.vpcPC.Status.Code { + case ec2.VpcPeeringConnectionStateReasonCodeInitiatingRequest, ec2.VpcPeeringConnectionStateReasonCodeProvisioning: + return api.VPC_PEERING_CONNECTION_STATUS_CREATING + case ec2.VpcPeeringConnectionStateReasonCodePendingAcceptance: + return api.VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT + case ec2.VpcPeeringConnectionStateReasonCodeActive: + return api.VPC_PEERING_CONNECTION_STATUS_ACTIVE + case ec2.VpcPeeringConnectionStateReasonCodeDeleted, ec2.VpcPeeringConnectionStateReasonCodeDeleting: + return api.VPC_PEERING_CONNECTION_STATUS_DELETING + default: + return api.VPC_PEERING_CONNECTION_STATUS_UNKNOWN + } + } + return api.VPC_PEERING_CONNECTION_STATUS_UNKNOWN +} + +func (self *SVpcPeeringConnection) Refresh() error { + vpcPC, err := self.vpc.region.GetVpcPeeringConnectionById(self.GetId()) + if err != nil { + return errors.Wrapf(err, "self.vpc.region.GetVpcPeeringConnectionById(%s)", self.GetId()) + } + self.vpcPC = vpcPC + return nil +} + +func (self *SVpcPeeringConnection) GetPeerVpcId() string { + if self.vpcPC.AccepterVpcInfo != nil { + if self.vpcPC.AccepterVpcInfo.VpcId != nil { + return *self.vpcPC.AccepterVpcInfo.VpcId + } + } + return "" +} + +func (self *SVpcPeeringConnection) GetPeerAccountId() string { + if self.vpcPC.AccepterVpcInfo != nil { + if self.vpcPC.AccepterVpcInfo.OwnerId != nil { + return *self.vpcPC.AccepterVpcInfo.OwnerId + } + } + return "" +} + +func (self *SVpcPeeringConnection) Delete() error { + err := self.vpc.region.DeleteVpcPeeringConnection(self.GetId()) + if err != nil { + return errors.Wrapf(err, "self.region.DeleteVpcPeeringConnection(%s)", self.GetId()) + } + return nil +} + +func (self *SVpcPeeringConnection) GetEnabled() bool { + return true +} diff --git a/pkg/multicloud/billing_base.go b/pkg/multicloud/billing_base.go index 069d1cb556..899812cf56 100644 --- a/pkg/multicloud/billing_base.go +++ b/pkg/multicloud/billing_base.go @@ -15,8 +15,11 @@ package multicloud import ( - "fmt" "time" + + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/cloudprovider" ) type SBillingBase struct{} @@ -34,7 +37,7 @@ func (self *SBillingBase) GetExpiredAt() time.Time { } func (self *SBillingBase) SetAutoRenew(autoRenew bool) error { - return fmt.Errorf("Not Implement SetAutoRenew") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "SetAutoRenew") } func (self *SBillingBase) IsAutoRenew() bool { diff --git a/pkg/multicloud/dbinstance_account_base.go b/pkg/multicloud/dbinstance_account_base.go index 3720568fbc..98471a8cb9 100644 --- a/pkg/multicloud/dbinstance_account_base.go +++ b/pkg/multicloud/dbinstance_account_base.go @@ -28,7 +28,7 @@ package multicloud import ( - "fmt" + "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/cloudprovider" ) @@ -42,17 +42,17 @@ func (account *SDBInstanceAccountBase) GetIDBInstanceAccountPrivileges() ([]clou } func (account *SDBInstanceAccountBase) Delete() error { - return fmt.Errorf("Not Implemented Delete") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "Delete") } func (account *SDBInstanceAccountBase) ResetPassword(password string) error { - return fmt.Errorf("Not Implemented ResetPassword") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "ResetPassword") } func (backup *SDBInstanceAccountBase) GrantPrivilege(database, privilege string) error { - return fmt.Errorf("Not Implement GrantPrivilege") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "GrantPrivilege") } func (backup *SDBInstanceAccountBase) RevokePrivilege(database string) error { - return fmt.Errorf("Not Implement RevokePrivilege") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "RevokePrivilege") } diff --git a/pkg/multicloud/dbinstance_backup_base.go b/pkg/multicloud/dbinstance_backup_base.go index 411ff8f6b8..94a0bc057f 100644 --- a/pkg/multicloud/dbinstance_backup_base.go +++ b/pkg/multicloud/dbinstance_backup_base.go @@ -15,9 +15,10 @@ package multicloud import ( - "fmt" + "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" ) type SDBInstanceBackupBase struct { @@ -29,7 +30,7 @@ func (backup *SDBInstanceBackupBase) GetBackMode() string { } func (backup *SDBInstanceBackupBase) Delete() error { - return fmt.Errorf("Not Implement Delete") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "Delete") } func (backup *SDBInstanceBackupBase) GetProjectId() string { diff --git a/pkg/multicloud/dbinstance_base.go b/pkg/multicloud/dbinstance_base.go index 8718a214dd..5f7fac739c 100644 --- a/pkg/multicloud/dbinstance_base.go +++ b/pkg/multicloud/dbinstance_base.go @@ -16,7 +16,8 @@ package multicloud import ( "context" - "fmt" + + "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/util/billing" @@ -36,23 +37,23 @@ func (instance *SDBInstanceBase) GetInternalConnectionStr() string { } func (instance *SDBInstanceBase) GetDBNetwork() (*cloudprovider.SDBInstanceNetwork, error) { - return nil, fmt.Errorf("Not Implemented GetDBNetwork") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetDBNetwork") } func (instance *SDBInstanceBase) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) { - return nil, fmt.Errorf("Not Implemented GetIDBInstanceParameters") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceParameters") } func (instance *SDBInstanceBase) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) { - return nil, fmt.Errorf("Not Implemented GetIDBInstanceDatabases") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceDatabases") } func (instance *SDBInstanceBase) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) { - return nil, fmt.Errorf("Not Implemented GetIDBInstanceAccounts") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceAccounts") } func (instance *SDBInstanceBase) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) { - return nil, fmt.Errorf("Not Implemented GetIDBInstanceBackups") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceBackups") } func (instance *SDBInstanceBase) GetCategory() string { @@ -60,11 +61,11 @@ func (instance *SDBInstanceBase) GetCategory() string { } func (instance *SDBInstanceBase) Reboot() error { - return fmt.Errorf("Not Implemmented Reboot") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "Reboot") } func (instance *SDBInstanceBase) Delete() error { - return fmt.Errorf("Not Implemmented Delete") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "Delete") } func (instance *SDBInstanceBase) GetMasterInstanceId() string { @@ -76,33 +77,33 @@ func (instance *SDBInstanceBase) GetSecurityGroupId() string { } func (instance *SDBInstanceBase) Renew(bc billing.SBillingCycle) error { - return fmt.Errorf("Not Implemented Renew") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "Renew") } func (instance *SDBInstanceBase) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedDBInstanceChangeConfig) error { - return fmt.Errorf("Not Implemented ChnageConfig") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "ChangeConfig") } func (instance *SDBInstanceBase) OpenPublicConnection() error { - return fmt.Errorf("Not Implemented OpenPublicConnection") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "OpenPublicConnection") } func (instance *SDBInstanceBase) ClosePublicConnection() error { - return fmt.Errorf("Not Implemented ClosePublicConnection") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "ClosePublicConnection") } func (instance *SDBInstanceBase) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCreateConfig) error { - return fmt.Errorf("Not Implemented CreateDatabase") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateDatabase") } func (instance *SDBInstanceBase) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error { - return fmt.Errorf("Not Implemented CreateAccount") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateAccount") } func (instance *SDBInstanceBase) CreateIBackup(conf *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) { - return "", fmt.Errorf("Not Implemented CreateIBackup") + return "", errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateIBackup") } func (instance *SDBInstanceBase) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error { - return fmt.Errorf("Not Implemented RecoveryFromBackup") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "RecoveryFromBackup") } diff --git a/pkg/multicloud/dbinstance_database_base.go b/pkg/multicloud/dbinstance_database_base.go index cc448ccdc2..a7e4852ca4 100644 --- a/pkg/multicloud/dbinstance_database_base.go +++ b/pkg/multicloud/dbinstance_database_base.go @@ -14,12 +14,16 @@ package multicloud -import "fmt" +import ( + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) type SDBInstanceDatabaseBase struct { SResourceBase } func (db *SDBInstanceDatabaseBase) Delete() error { - return fmt.Errorf("Not Implemented Delete") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "Delete") } diff --git a/pkg/multicloud/huawei/client/client.go b/pkg/multicloud/huawei/client/client.go index c4c9b37119..00cc286051 100644 --- a/pkg/multicloud/huawei/client/client.go +++ b/pkg/multicloud/huawei/client/client.go @@ -76,6 +76,7 @@ type Client struct { SNatRules *modules.SNatSRuleManager DNatRules *modules.SNatDRuleManager NatGateways *modules.SNatGatewayManager + VpcPeerings *modules.SVpcPeeringManager DBInstance *modules.SDBInstanceManager DBInstanceBackup *modules.SDBInstanceBackupManager DBInstanceFlavor *modules.SDBInstanceFlavorManager @@ -214,6 +215,7 @@ func (self *Client) initManagers() { self.SNatRules = modules.NewNatSManager(self.regionId, self.projectId, self.signer, self.debug) self.DNatRules = modules.NewNatDManager(self.regionId, self.projectId, self.signer, self.debug) self.NatGateways = modules.NewNatGatewayManager(self.regionId, self.projectId, self.signer, self.debug) + self.VpcPeerings = modules.NewVpcPeeringManager(self.regionId, self.projectId, self.signer, self.debug) self.DBInstance = modules.NewDBInstanceManager(self.regionId, self.projectId, self.signer, self.debug) self.DBInstanceBackup = modules.NewDBInstanceBackupManager(self.regionId, self.projectId, self.signer, self.debug) self.DBInstanceFlavor = modules.NewDBInstanceFlavorManager(self.regionId, self.projectId, self.signer, self.debug) diff --git a/pkg/multicloud/huawei/client/modules/mod_vpc_peerings.go b/pkg/multicloud/huawei/client/modules/mod_vpc_peerings.go new file mode 100644 index 0000000000..393f23ac08 --- /dev/null +++ b/pkg/multicloud/huawei/client/modules/mod_vpc_peerings.go @@ -0,0 +1,37 @@ +// 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 modules + +import ( + "yunion.io/x/onecloud/pkg/multicloud/huawei/client/auth" +) + +type SVpcPeeringManager struct { + SResourceManager +} + +func NewVpcPeeringManager(regionId string, projectId string, signer auth.Signer, debug bool) *SVpcPeeringManager { + return &SVpcPeeringManager{SResourceManager: SResourceManager{ + SBaseManager: NewBaseManager(signer, debug), + ServiceName: ServiceNameVPC, + Region: regionId, + ProjectId: "", + version: "v2.0", + Keyword: "peering", + KeywordPlural: "peerings", + + ResourceKeyword: "vpc/peerings", + }} +} diff --git a/pkg/multicloud/huawei/provider/provider.go b/pkg/multicloud/huawei/provider/provider.go index 50ddca92bb..b88067a2cc 100644 --- a/pkg/multicloud/huawei/provider/provider.go +++ b/pkg/multicloud/huawei/provider/provider.go @@ -64,6 +64,18 @@ func (self *SHuaweiProviderFactory) IsSupportCreateCloudgroup() bool { return true } +func (factory *SHuaweiProviderFactory) IsSupportCrossCloudEnvVpcPeering() bool { + return false +} + +func (factory *SHuaweiProviderFactory) IsSupportCrossRegionVpcPeering() bool { + return false +} + +func (factory *SHuaweiProviderFactory) IsSupportVpcPeeringVpcCidrOverlap() bool { + return true +} + func (self *SHuaweiProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) { output := cloudprovider.SCloudaccount{} if len(input.AccessKeyId) == 0 { diff --git a/pkg/multicloud/huawei/routetable.go b/pkg/multicloud/huawei/routetable.go index ef778c04d1..1c24e0c076 100644 --- a/pkg/multicloud/huawei/routetable.go +++ b/pkg/multicloud/huawei/routetable.go @@ -70,7 +70,7 @@ type SRouteTable struct { VpcId string Description string Type string - Routes []cloudprovider.ICloudRoute + Routes []*SRouteEntry } func NewSRouteTable(vpc *SVpc, Type string) SRouteTable { @@ -134,7 +134,11 @@ func (self *SRouteTable) GetIRoutes() ([]cloudprovider.ICloudRoute, error) { return nil, err } } - return self.Routes, nil + ret := []cloudprovider.ICloudRoute{} + for i := range self.Routes { + ret = append(ret, self.Routes[i]) + } + return ret, nil } // fetchRoutes fetch Routes @@ -152,7 +156,7 @@ func (self *SRouteTable) fetchRoutesForIP() error { return errors.Wrap(err, "get vpc info error") } routeArray, err := ret.GetArray("routes") - routes := make([]cloudprovider.ICloudRoute, 0, len(routeArray)) + routes := make([]*SRouteEntry, 0, len(routeArray)) for i := range routeArray { destination, err := routeArray[i].GetString("destination") if err != nil { @@ -180,7 +184,7 @@ func (self *SRouteTable) fetchRoutesForPeer() error { if err != nil { return errors.Wrap(err, "get peer route error") } - routesPeer := make([]cloudprovider.ICloudRoute, 0, retPeer.Total) + routesPeer := make([]*SRouteEntry, 0, retPeer.Total) for i := range retPeer.Data { route := retPeer.Data[i] id, err := route.GetString("id") @@ -254,3 +258,26 @@ func (self *SRegion) GetRouteTables(vpcId string) ([]SRouteTable, error) { } return ret, nil } + +func (self *SRegion) CreatePeeringRoute(vpcId, destinationCidr, target string) error { + params := jsonutils.NewDict() + routeObj := jsonutils.NewDict() + routeObj.Set("type", jsonutils.NewString("peering")) + routeObj.Set("nexthop", jsonutils.NewString(target)) + routeObj.Set("destination", jsonutils.NewString(destinationCidr)) + routeObj.Set("vpc_id", jsonutils.NewString(vpcId)) + params.Set("route", routeObj) + err := DoCreate(self.ecsClient.VpcRoutes.Create, params, nil) + if err != nil { + return errors.Wrapf(err, "DoCreate(self.ecsClient.VpcRoutes.Create, %s, &ret)", jsonutils.Marshal(params).String()) + } + return nil +} + +func (self *SRegion) DeletePeeringRoute(routeId string) error { + err := DoDelete(self.ecsClient.VpcRoutes.Delete, routeId, nil, nil) + if err != nil { + return errors.Wrapf(err, "DoDelete(self.ecsClient.VpcRoutes.Delete,%s,nil)", routeId) + } + return nil +} diff --git a/pkg/multicloud/huawei/shell/vpc.go b/pkg/multicloud/huawei/shell/vpc.go index 80f85493f0..b19165bd4e 100644 --- a/pkg/multicloud/huawei/shell/vpc.go +++ b/pkg/multicloud/huawei/shell/vpc.go @@ -15,6 +15,7 @@ package shell import ( + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/multicloud/huawei" "yunion.io/x/onecloud/pkg/util/shellutils" ) @@ -54,4 +55,68 @@ func init() { return cli.DeleteVpc(args.ID) }) + type VpcPeeringListOPtion struct { + VPCID string + } + shellutils.R(&VpcPeeringListOPtion{}, "vpcPeering-list", "List vpcPeering", func(cli *huawei.SRegion, args *VpcPeeringListOPtion) error { + vpcPeerings, err := cli.GetVpcPeerings(args.VPCID) + if err != nil { + return err + } + printList(vpcPeerings, 0, 0, 0, nil) + return nil + }) + + type VpcPeeringShowOPtion struct { + VPCPEERINGID string + } + shellutils.R(&VpcPeeringShowOPtion{}, "vpcPeering-show", "show vpcPeering", func(cli *huawei.SRegion, args *VpcPeeringShowOPtion) error { + vpcPeering, err := cli.GetVpcPeering(args.VPCPEERINGID) + if err != nil { + return err + } + printObject(vpcPeering) + return nil + }) + + type VpcPeeringCreateOPtion struct { + NAME string + VPCID string + PEERVPCID string + PEEROWNERID string + } + shellutils.R(&VpcPeeringCreateOPtion{}, "vpcPeering-create", "create vpcPeering", func(cli *huawei.SRegion, args *VpcPeeringCreateOPtion) error { + opts := cloudprovider.VpcPeeringConnectionCreateOptions{} + opts.Name = args.NAME + opts.PeerVpcId = args.PEERVPCID + opts.PeerAccountId = args.PEEROWNERID + vpcPeering, err := cli.CreateVpcPeering(args.VPCID, &opts) + if err != nil { + return err + } + printObject(vpcPeering) + return nil + }) + + type VpcPeeringAcceptOPtion struct { + VPCPEERINGID string + } + shellutils.R(&VpcPeeringAcceptOPtion{}, "vpcPeering-accept", "Accept vpcPeering", func(cli *huawei.SRegion, args *VpcPeeringAcceptOPtion) error { + err := cli.AcceptVpcPeering(args.VPCPEERINGID) + if err != nil { + return err + } + return nil + }) + + type VpcPeeringDeleteOPtion struct { + VPCPEERINGID string + } + shellutils.R(&VpcPeeringDeleteOPtion{}, "vpcPeering-delete", "Delete vpcPeering", func(cli *huawei.SRegion, args *VpcPeeringDeleteOPtion) error { + err := cli.DeleteVpcPeering(args.VPCPEERINGID) + if err != nil { + return err + } + return nil + }) } diff --git a/pkg/multicloud/huawei/vpc.go b/pkg/multicloud/huawei/vpc.go index 00bc953051..037073bb70 100644 --- a/pkg/multicloud/huawei/vpc.go +++ b/pkg/multicloud/huawei/vpc.go @@ -214,6 +214,79 @@ func (self *SVpc) GetINatGateways() ([]cloudprovider.ICloudNatGateway, error) { return ret, nil } +func (self *SVpc) GetICloudVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) { + svpcPCs, err := self.getVpcPeeringConnections() + if err != nil { + return nil, errors.Wrap(err, "self.getVpcPeeringConnections()") + } + ivpcPCs := []cloudprovider.ICloudVpcPeeringConnection{} + for i := range svpcPCs { + ivpcPCs = append(ivpcPCs, &svpcPCs[i]) + } + return ivpcPCs, nil +} +func (self *SVpc) GetICloudVpcPeeringConnectionById(id string) (cloudprovider.ICloudVpcPeeringConnection, error) { + svpcPC, err := self.getVpcPeeringConnectionById(id) + if err != nil { + return nil, errors.Wrapf(err, "self.getVpcPeeringConnectionById(%s)", id) + } + return svpcPC, nil +} + +func (self *SVpc) CreateICloudVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (cloudprovider.ICloudVpcPeeringConnection, error) { + svpcPC, err := self.region.CreateVpcPeering(self.GetId(), opts) + if err != nil { + return nil, errors.Wrapf(err, "self.region.CreateVpcPeering(%s,%s)", self.GetId(), jsonutils.Marshal(opts).String()) + } + svpcPC.vpc = self + return svpcPC, nil +} +func (self *SVpc) AcceptICloudVpcPeeringConnection(id string) error { + vpcPC, err := self.getVpcPeeringConnectionById(id) + if err != nil { + return errors.Wrapf(err, "self.getVpcPeeringConnectionById(%s)", id) + } + if vpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_ACTIVE { + return nil + } + if vpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_UNKNOWN { + return errors.Wrapf(cloudprovider.ErrInvalidStatus, "vpcPC: %s", jsonutils.Marshal(vpcPC).String()) + } + err = self.region.AcceptVpcPeering(id) + if err != nil { + return errors.Wrapf(err, "self.region.AcceptVpcPeering(%s)", id) + } + return nil +} + +func (self *SVpc) GetAuthorityOwnerId() string { + return self.region.client.projectId +} + +func (self *SVpc) getVpcPeeringConnections() ([]SVpcPeering, error) { + svpcPeerings, err := self.region.GetVpcPeerings(self.GetId()) + if err != nil { + return nil, errors.Wrapf(err, "self.region.GetVpcPeerings(%s)", self.GetId()) + } + vpcPCs := []SVpcPeering{} + for i := range svpcPeerings { + if svpcPeerings[i].GetVpcId() == self.GetId() { + svpcPeerings[i].vpc = self + vpcPCs = append(vpcPCs, svpcPeerings[i]) + } + } + return vpcPCs, nil +} + +func (self *SVpc) getVpcPeeringConnectionById(id string) (*SVpcPeering, error) { + svpcPC, err := self.region.GetVpcPeering(id) + if err != nil { + return nil, errors.Wrapf(err, "self.region.GetVpcPeering(%s)", id) + } + svpcPC.vpc = self + return svpcPC, nil +} + func (self *SRegion) getVpc(vpcId string) (*SVpc, error) { vpc := SVpc{} err := DoGet(self.ecsClient.Vpcs.Get, vpcId, nil, &vpc) diff --git a/pkg/multicloud/huawei/vpc_peering.go b/pkg/multicloud/huawei/vpc_peering.go new file mode 100644 index 0000000000..d360824c32 --- /dev/null +++ b/pkg/multicloud/huawei/vpc_peering.go @@ -0,0 +1,158 @@ +// 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 huawei + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type RequestVpcInfo struct { + VpcID string `json:"vpc_id"` + TenantID string `json:"tenant_id"` +} +type AcceptVpcInfo struct { + VpcID string `json:"vpc_id"` + TenantID string `json:"tenant_id"` +} +type SVpcPeering struct { + multicloud.SResourceBase + vpc *SVpc + + RequestVpcInfo RequestVpcInfo `json:"request_vpc_info"` + AcceptVpcInfo AcceptVpcInfo `json:"accept_vpc_info"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` +} + +func (self *SRegion) GetVpcPeerings(vpcId string) ([]SVpcPeering, error) { + querys := make(map[string]string) + querys["vpc_id"] = vpcId + vpcPeerings := make([]SVpcPeering, 0) + err := doListAllWithMarker(self.ecsClient.VpcPeerings.List, querys, &vpcPeerings) + if err != nil { + return nil, errors.Wrapf(err, "oListAllWithMarker(self.ecsClient.VpcPeerings.List, %s, &vpcPeerings)", jsonutils.Marshal(querys).String()) + } + return vpcPeerings, nil +} + +func (self *SRegion) GetVpcPeering(vpcPeeringId string) (*SVpcPeering, error) { + if len(vpcPeeringId) == 0 { + return nil, cloudprovider.ErrNotFound + } + vpcPeering := SVpcPeering{} + err := DoGet(self.ecsClient.VpcPeerings.Get, vpcPeeringId, nil, &vpcPeering) + if err != nil { + return nil, errors.Wrapf(err, "DoGet(self.ecsClient.VpcPeerings.Get, %s, nil, &vpcPeering)", vpcPeeringId) + } + return &vpcPeering, nil +} + +func (self *SRegion) CreateVpcPeering(vpcId string, opts *cloudprovider.VpcPeeringConnectionCreateOptions) (*SVpcPeering, error) { + params := jsonutils.NewDict() + vpcPeeringObj := jsonutils.NewDict() + requestVpcObj := jsonutils.NewDict() + acceptVpcObj := jsonutils.NewDict() + vpcPeeringObj.Set("name", jsonutils.NewString(opts.Name)) + requestVpcObj.Set("vpc_id", jsonutils.NewString(vpcId)) + requestVpcObj.Set("tenant_id", jsonutils.NewString(self.client.projectId)) + vpcPeeringObj.Set("request_vpc_info", requestVpcObj) + acceptVpcObj.Set("vpc_id", jsonutils.NewString(opts.PeerVpcId)) + acceptVpcObj.Set("tenant_id", jsonutils.NewString(opts.PeerAccountId)) + vpcPeeringObj.Set("accept_vpc_info", acceptVpcObj) + params.Set("peering", vpcPeeringObj) + ret := SVpcPeering{} + err := DoCreate(self.ecsClient.VpcPeerings.Create, params, &ret) + if err != nil { + return nil, errors.Wrapf(err, "DoCreate(self.ecsClient.VpcPeerings.Create, %s, &ret)", jsonutils.Marshal(params).String()) + } + return &ret, nil +} + +func (self *SRegion) AcceptVpcPeering(vpcPeeringId string) error { + err := DoUpdateWithSpec(self.ecsClient.VpcPeerings.UpdateInContextWithSpec, vpcPeeringId, "accept", nil) + if err != nil { + return errors.Wrapf(err, "DoUpdateWithSpec(self.ecsClient.VpcPeerings.UpdateInContextWithSpec, %s, accept, nil)", vpcPeeringId) + } + return nil +} + +func (self *SRegion) DeleteVpcPeering(vpcPeeringId string) error { + err := DoDelete(self.ecsClient.VpcPeerings.Delete, vpcPeeringId, nil, nil) + if err != nil { + return errors.Wrapf(err, "DoDelete(self.ecsClient.VpcPeerings.Delete,%s,nil)", vpcPeeringId) + } + return nil +} + +func (self *SVpcPeering) GetId() string { + return self.ID +} + +func (self *SVpcPeering) GetName() string { + return self.Name +} + +func (self *SVpcPeering) GetGlobalId() string { + return self.GetId() +} + +func (self *SVpcPeering) GetStatus() string { + switch self.Status { + case "PENDING_ACCEPTANCE": + return api.VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT + case "ACTIVE": + return api.VPC_PEERING_CONNECTION_STATUS_ACTIVE + default: + return api.VPC_PEERING_CONNECTION_STATUS_UNKNOWN + } +} + +func (self *SVpcPeering) Refresh() error { + peer, err := self.vpc.region.GetVpcPeering(self.ID) + if err != nil { + return errors.Wrapf(err, "self.region.GetVpcPeering(%s)", self.ID) + } + return jsonutils.Update(self, peer) +} + +func (self *SVpcPeering) GetVpcId() string { + return self.RequestVpcInfo.VpcID +} + +func (self *SVpcPeering) GetPeerVpcId() string { + return self.AcceptVpcInfo.VpcID +} + +func (self *SVpcPeering) GetPeerAccountId() string { + return self.AcceptVpcInfo.TenantID +} + +func (self *SVpcPeering) GetEnabled() bool { + return true +} + +func (self *SVpcPeering) Delete() error { + err := self.vpc.region.DeleteVpcPeering(self.ID) + if err != nil { + return errors.Wrapf(err, "self.region.DeleteVpcPeering(%s)", self.ID) + } + return nil +} diff --git a/pkg/multicloud/qcloud/provider/provider.go b/pkg/multicloud/qcloud/provider/provider.go index 56aa0910c9..ec724b4fc2 100644 --- a/pkg/multicloud/qcloud/provider/provider.go +++ b/pkg/multicloud/qcloud/provider/provider.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -61,6 +62,27 @@ func (self *SQcloudProviderFactory) IsSupportCreateCloudgroup() bool { return true } +func (self *SQcloudProviderFactory) IsSupportCrossCloudEnvVpcPeering() bool { + return false +} + +func (self *SQcloudProviderFactory) IsSupportCrossRegionVpcPeering() bool { + return true +} + +func (self *SQcloudProviderFactory) IsSupportVpcPeeringVpcCidrOverlap() bool { + return false +} + +func (self *SQcloudProviderFactory) ValidateCrossRegionVpcPeeringBandWidth(bandwidth int) error { + validatedBandwidths := []int{10, 20, 50, 100, 200, 500, 1000} + ok, _ := utils.InArray(bandwidth, validatedBandwidths) + if ok { + return nil + } + return httperrors.NewInputParameterError("require validated qcloud cross region vpcPeering bandwidth values:[10, 20, 50, 100, 200, 500, 1000],unit Mbps") +} + func (self *SQcloudProviderFactory) GetSupportedDnsZoneTypes() []cloudprovider.TDnsZoneType { return []cloudprovider.TDnsZoneType{ cloudprovider.PublicZone, diff --git a/pkg/multicloud/qcloud/qcloud.go b/pkg/multicloud/qcloud/qcloud.go index 977d7ad851..cbe71db258 100644 --- a/pkg/multicloud/qcloud/qcloud.go +++ b/pkg/multicloud/qcloud/qcloud.go @@ -88,6 +88,7 @@ func (cfg *QcloudClientConfig) Debug(debug bool) *QcloudClientConfig { type SQcloudClient struct { *QcloudClientConfig + uin string ownerId string ownerName string @@ -234,11 +235,13 @@ func (r *phpJsonRequest) GetPath() string { // 2017vpc专用response type vpc2017JsonResponse struct { - Code int `json:"code"` - CodeDesc string `json:"codeDesc"` - Message string `json:"message"` - TotalCount int `json:"totalCount"` - Response *interface{} `json:"data"` + Code int `json:"code"` + CodeDesc string `json:"codeDesc"` + Message string `json:"message"` + TotalCount int `json:"totalCount"` + TaskId int64 `json:"taskId"` + PeeringConnectionId string `json:"peeringConnectionId"` + Response *interface{} `json:"data"` } func (r *vpc2017JsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { @@ -257,9 +260,11 @@ func (r *vpc2017JsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error func (r *vpc2017JsonResponse) GetResponse() *interface{} { if r.Response == nil { result, _ := jsonutils.Parse([]byte(`{"data":[],"totalCount":0}`)) + resultMap := result.(*jsonutils.JSONDict) + resultMap.Update(jsonutils.Marshal(r)) return func(resp interface{}) *interface{} { return &resp - }(result) + }(resultMap) } return func(resp interface{}) *interface{} { return &resp diff --git a/pkg/multicloud/qcloud/shell/vpc.go b/pkg/multicloud/qcloud/shell/vpc.go index 55363f5328..2b5442aae8 100644 --- a/pkg/multicloud/qcloud/shell/vpc.go +++ b/pkg/multicloud/qcloud/shell/vpc.go @@ -15,6 +15,7 @@ package shell import ( + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/multicloud/qcloud" "yunion.io/x/onecloud/pkg/util/shellutils" ) @@ -52,4 +53,119 @@ func init() { shellutils.R(&VpcDeleteOptions{}, "vpc-delete", "Delete vpc", func(cli *qcloud.SRegion, args *VpcDeleteOptions) error { return cli.DeleteVpc(args.ID) }) + + type VpcPCListOption struct { + VPCID string + Limit int `help:"page size"` + Offset int `help:"page offset"` + } + shellutils.R(&VpcPCListOption{}, "vpcPC-list", "List vpc peering connections", func(cli *qcloud.SRegion, args *VpcPCListOption) error { + vpcs, total, err := cli.DescribeVpcPeeringConnections(args.VPCID, "", args.Offset, args.Limit) + if err != nil { + return err + } + printList(vpcs, total, args.Offset, args.Limit, []string{}) + return nil + }) + + type VpcPCShowOption struct { + VPCPCID string + } + shellutils.R(&VpcPCShowOption{}, "vpcPC-show", "show vpc peering connection", func(cli *qcloud.SRegion, args *VpcPCShowOption) error { + vpcPC, err := cli.GetVpcPeeringConnectionbyId(args.VPCPCID) + if err != nil { + return err + } + printObject(vpcPC) + return nil + }) + + type VpcPCCreateOPtion struct { + NAME string + VPCID string + PEERVPCID string + PEERREGIONID string + PEEROWNERID string + Bandwidth int + } + shellutils.R(&VpcPCCreateOPtion{}, "vpcPC-create", "create vpc peering connection", func(cli *qcloud.SRegion, args *VpcPCCreateOPtion) error { + opts := cloudprovider.VpcPeeringConnectionCreateOptions{} + opts.Name = args.NAME + opts.PeerVpcId = args.PEERVPCID + opts.PeerAccountId = args.PEEROWNERID + opts.PeerRegionId = args.PEERREGIONID + vpcPCId, err := cli.CreateVpcPeeringConnection(args.VPCID, &opts) + if err != nil { + return err + } + printObject(vpcPCId) + return nil + }) + + shellutils.R(&VpcPCCreateOPtion{}, "vpcPC-createEx", "create Ex vpc peering connection", func(cli *qcloud.SRegion, args *VpcPCCreateOPtion) error { + opts := cloudprovider.VpcPeeringConnectionCreateOptions{} + opts.Name = args.NAME + opts.PeerVpcId = args.PEERVPCID + opts.PeerAccountId = args.PEEROWNERID + opts.PeerRegionId = args.PEERREGIONID + opts.Bandwidth = args.Bandwidth + taskId, err := cli.CreateVpcPeeringConnectionEx(args.VPCID, &opts) + if err != nil { + return err + } + printObject(taskId) + return nil + }) + + type VpcPeeringAcceptOPtion struct { + VPCPEERINGID string + } + shellutils.R(&VpcPeeringAcceptOPtion{}, "vpcPC-accept", "Accept vpcPeering", func(cli *qcloud.SRegion, args *VpcPeeringAcceptOPtion) error { + err := cli.AcceptVpcPeeringConnection(args.VPCPEERINGID) + if err != nil { + return err + } + return nil + }) + + shellutils.R(&VpcPeeringAcceptOPtion{}, "vpcPC-acceptEx", "Accept Ex vpcPeering", func(cli *qcloud.SRegion, args *VpcPeeringAcceptOPtion) error { + _, err := cli.AcceptVpcPeeringConnectionEx(args.VPCPEERINGID) + if err != nil { + return err + } + return nil + }) + + type VpcPeeringDeleteOPtion struct { + VPCPEERINGID string + } + shellutils.R(&VpcPeeringAcceptOPtion{}, "vpcPC-delete", "Delete vpcPeering", func(cli *qcloud.SRegion, args *VpcPeeringAcceptOPtion) error { + err := cli.DeleteVpcPeeringConnection(args.VPCPEERINGID) + if err != nil { + return err + } + return nil + }) + + shellutils.R(&VpcPeeringAcceptOPtion{}, "vpcPC-deleteEx", "Delete Ex vpcPeering", func(cli *qcloud.SRegion, args *VpcPeeringAcceptOPtion) error { + status, err := cli.DeleteVpcPeeringConnectionEx(args.VPCPEERINGID) + if err != nil { + return err + } + println(status) + return nil + }) + + type VpcTaskShowOption struct { + TASKID string + } + shellutils.R(&VpcTaskShowOption{}, "vpcTask-show", "show vpc task", func(cli *qcloud.SRegion, args *VpcTaskShowOption) error { + status, err := cli.DescribeVpcTaskResult(args.TASKID) + if err != nil { + return err + } + println(status) + return nil + }) + } diff --git a/pkg/multicloud/qcloud/vpc.go b/pkg/multicloud/qcloud/vpc.go index efb18fc6cf..20f1c505d3 100644 --- a/pkg/multicloud/qcloud/vpc.go +++ b/pkg/multicloud/qcloud/vpc.go @@ -15,9 +15,11 @@ package qcloud import ( + "fmt" "time" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -200,3 +202,157 @@ func (self *SVpc) addWire(wire *SWire) { } self.iwires = append(self.iwires, wire) } + +func (self *SVpc) GetSVpcPeeringConnections() ([]SVpcPC, error) { + svpcPCs, err := self.region.GetAllVpcPeeringConnections(self.GetId()) + if err != nil { + return nil, errors.Wrapf(err, "self.region.GetAllVpcPeeringConnections(%s)", self.GetId()) + } + for i := range svpcPCs { + svpcPCs[i].vpc = self + } + return svpcPCs, nil +} + +func (self *SVpc) GetSVpcPeeringConnectionById(id string) (*SVpcPC, error) { + svpcPC, err := self.region.GetVpcPeeringConnectionbyId(id) + if err != nil { + return nil, errors.Wrapf(err, "self.region.GetVpcPeeringConnectionbyId(%s)", id) + } + svpcPC.vpc = self + return svpcPC, nil +} + +func (self *SVpc) CreateSVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (*SVpcPC, error) { + vpcPCId, err := self.region.CreateVpcPeeringConnection(self.GetId(), opts) + if err != nil { + return nil, errors.Wrapf(err, "self.region.CreateVpcPeeringConnection(%s, %s)", self.GetId(), jsonutils.Marshal(opts).String()) + } + SvpcPC, err := self.GetSVpcPeeringConnectionById(vpcPCId) + if err != nil { + return nil, errors.Wrapf(err, "self.GetSVpcPeeringConnectionById(%s)", vpcPCId) + } + return SvpcPC, nil +} + +func (self *SVpc) CreateCrossRegionSVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (*SVpcPC, error) { + taskId, err := self.region.CreateVpcPeeringConnectionEx(self.GetId(), opts) + if err != nil { + return nil, errors.Wrapf(err, "self.region.CreateVpcPeeringConnectionEx(%s, %s)", self.GetId(), jsonutils.Marshal(opts).String()) + } + err = cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) { + status, err := self.region.DescribeVpcTaskResult(fmt.Sprintf("%d", taskId)) + if err != nil { + return false, errors.Wrap(err, "self.vpc.region.DescribeVpcTaskResult") + } + //任务的当前状态。0:成功,1:失败,2:进行中。 + if status == 1 { + return false, errors.Wrap(fmt.Errorf("taskfailed,taskId=%s", taskId), "client.DescribeVpcTaskResult(taskId)") + } + if status == 0 { + return true, nil + } + return false, nil + }) + if err != nil { + return nil, errors.Wrapf(err, " cloudprovider.Wait %s", taskId) + } + svpcPCs, err := self.GetSVpcPeeringConnections() + if err != nil { + return nil, errors.Wrap(err, "self.GetSVpcPeeringConnections()") + } + for i := range svpcPCs { + if svpcPCs[i].GetPeerVpcId() == opts.PeerVpcId { + return &svpcPCs[i], nil + } + } + return nil, errors.Wrap(cloudprovider.ErrNotFound, "vpcPeeringConnection not found after createEx") +} + +func (self *SVpc) AcceptSVpcPeeringConnection(id string) error { + err := self.region.AcceptVpcPeeringConnection(id) + if err != nil { + return errors.Wrapf(err, "self.region.AcceptVpcPeeringConnection(%s)", id) + } + return nil +} + +func (self *SVpc) AcceptCrossRegionSVpcPeeringConnection(id string) error { + taskId, err := self.region.AcceptVpcPeeringConnectionEx(id) + if err != nil { + return errors.Wrapf(err, "self.region.AcceptVpcPeeringConnectionEx(%s)", id) + } + err = cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) { + status, err := self.region.DescribeVpcTaskResult(fmt.Sprintf("%d", taskId)) + if err != nil { + return false, errors.Wrap(err, "self.vpc.region.DescribeVpcTaskResult") + } + //任务的当前状态。0:成功,1:失败,2:进行中。 + if status == 1 { + return false, errors.Wrap(fmt.Errorf("taskfailed,taskId=%s", taskId), "client.DescribeVpcTaskResult(taskId)") + } + if status == 0 { + return true, nil + } + return false, nil + }) + if err != nil { + return errors.Wrapf(err, " cloudprovider.Wait %s", taskId) + } + svpcPC, err := self.GetSVpcPeeringConnectionById(id) + if err != nil { + return errors.Wrapf(err, "self.GetSVpcPeeringConnectionById(%s)", id) + } + if svpcPC.GetStatus() != api.VPC_PEERING_CONNECTION_STATUS_ACTIVE { + return errors.Wrap(cloudprovider.ErrInvalidStatus, "invalid status after AcceptCrossRegionSVpcPeeringConnection") + } + return nil +} + +func (self *SVpc) GetICloudVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) { + result := []cloudprovider.ICloudVpcPeeringConnection{} + SVpcPCs, err := self.GetSVpcPeeringConnections() + if err != nil { + return nil, errors.Wrap(err, "self.GetSVpcPeeringConnections()") + } + for i := range SVpcPCs { + result = append(result, &SVpcPCs[i]) + } + return result, nil +} + +func (self *SVpc) GetICloudVpcPeeringConnectionById(id string) (cloudprovider.ICloudVpcPeeringConnection, error) { + svpcPC, err := self.GetSVpcPeeringConnectionById(id) + if err != nil { + return nil, errors.Wrapf(err, "self.GetSVpcPeeringConnectionById(%s)", id) + } + return svpcPC, nil +} + +func (self *SVpc) CreateICloudVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (cloudprovider.ICloudVpcPeeringConnection, error) { + if self.GetRegion().GetId() == opts.PeerRegionId { + return self.CreateSVpcPeeringConnection(opts) + } else { + return self.CreateCrossRegionSVpcPeeringConnection(opts) + } +} + +func (self *SVpc) AcceptICloudVpcPeeringConnection(id string) error { + svpcPC, err := self.GetSVpcPeeringConnectionById(id) + if err != nil { + return errors.Wrapf(err, "self.GetSVpcPeeringConnectionById(%s)", id) + } + if svpcPC.GetStatus() == api.VPC_PEERING_CONNECTION_STATUS_ACTIVE { + return nil + } + + if !svpcPC.IsCrossRegion() { + return self.AcceptSVpcPeeringConnection(id) + } else { + return self.AcceptCrossRegionSVpcPeeringConnection(id) + } +} + +func (self *SVpc) GetAuthorityOwnerId() string { + return self.region.client.ownerName +} diff --git a/pkg/multicloud/qcloud/vpc_peering_connection.go b/pkg/multicloud/qcloud/vpc_peering_connection.go new file mode 100644 index 0000000000..5ae897f13c --- /dev/null +++ b/pkg/multicloud/qcloud/vpc_peering_connection.go @@ -0,0 +1,282 @@ +// 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 qcloud + +import ( + "fmt" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SVpcPC struct { + multicloud.SResourceBase + vpc *SVpc + VpcID string `json:"vpcId"` + UnVpcID string `json:"unVpcId"` + PeerVpcID string `json:"peerVpcId"` + UnPeerVpcID string `json:"unPeerVpcId"` + AppID string `json:"appId"` + PeeringConnectionID string `json:"peeringConnectionId"` + PeeringConnectionName string `json:"peeringConnectionName"` + State int `json:"state"` + CreateTime string `json:"createTime"` + Uin string `json:"uin"` + PeerUin string `json:"peerUin"` + Region string `json:"region"` + PeerRegion string `json:"peerRegion"` +} + +func (region *SRegion) DescribeVpcPeeringConnections(vpcId string, peeringConnectionId string, offset int, limit int) ([]SVpcPC, int, error) { + if limit > 50 || limit <= 0 { + limit = 50 + } + params := make(map[string]string) + params["offset"] = fmt.Sprintf("%d", offset) + params["limit"] = fmt.Sprintf("%d", limit) + if len(vpcId) > 0 { + params["vpcId"] = vpcId + } + if len(peeringConnectionId) > 0 { + params["peeringConnectionId"] = peeringConnectionId + } + body, err := region.vpc2017Request("DescribeVpcPeeringConnections", params) + if err != nil { + return nil, 0, errors.Wrapf(err, `region.vpcRequest("DescribeVpcPeeringConnections", params)`, jsonutils.Marshal(params).String()) + } + + total, _ := body.Float("totalCount") + if total <= 0 { + return nil, int(total), nil + } + + vpcPCs := make([]SVpcPC, 0) + err = body.Unmarshal(&vpcPCs, "data") + if err != nil { + return nil, 0, errors.Wrapf(err, "body.Unmarshal(&vpcPCs,%s)", body.String()) + } + return vpcPCs, int(total), nil +} + +func (region *SRegion) GetAllVpcPeeringConnections(vpcId string) ([]SVpcPC, error) { + result := []SVpcPC{} + for { + vpcPCS, total, err := region.DescribeVpcPeeringConnections(vpcId, "", len(result), 50) + if err != nil { + return nil, errors.Wrapf(err, `client.DescribeVpcPeeringConnections(%s,"",%d,50)`, vpcId, len(result), len(result)) + } + result = append(result, vpcPCS...) + if total <= len(result) { + break + } + } + return result, nil +} + +func (region *SRegion) GetVpcPeeringConnectionbyId(vpcPCId string) (*SVpcPC, error) { + vpcPCs, _, err := region.DescribeVpcPeeringConnections("", vpcPCId, 0, 50) + if err != nil { + return nil, errors.Wrapf(err, `client.DescribeVpcPeeringConnections("", %s, 0, 50)`, vpcPCId) + } + if len(vpcPCs) < 1 { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetVpcPeeringConnectionbyId(%s)", vpcPCId) + } + if len(vpcPCs) > 1 { + return nil, errors.Wrapf(cloudprovider.ErrDuplicateId, "GetVpcPeeringConnectionbyId(%s)", vpcPCId) + } + return &vpcPCs[0], nil +} + +func (region *SRegion) CreateVpcPeeringConnection(vpcId string, opts *cloudprovider.VpcPeeringConnectionCreateOptions) (string, error) { + params := make(map[string]string) + params["vpcId"] = vpcId + params["peerVpcId"] = opts.PeerVpcId + params["peerUin"] = opts.PeerAccountId + params["peeringConnectionName"] = opts.Name + body, err := region.vpc2017Request("CreateVpcPeeringConnection", params) + if err != nil { + return "", errors.Wrapf(err, `client.vpc2017Request("CreateVpcPeeringConnection", %s)`, jsonutils.Marshal(params).String()) + } + peeringId, err := body.GetString("peeringConnectionId") + if err != nil { + return "", errors.Wrapf(err, `%s body.GetString("peeringConnectionId")`, body.String()) + } + return peeringId, nil +} + +func (region *SRegion) AcceptVpcPeeringConnection(peeringId string) error { + params := make(map[string]string) + params["peeringConnectionId"] = peeringId + _, err := region.vpc2017Request("AcceptVpcPeeringConnection", params) + if err != nil { + return errors.Wrapf(err, `client.vpc2017Request("AcceptVpcPeeringConnection",%s)`, jsonutils.Marshal(params).String()) + } + return nil +} + +func (region *SRegion) DeleteVpcPeeringConnection(peeringId string) error { + params := make(map[string]string) + params["peeringConnectionId"] = peeringId + _, err := region.vpc2017Request("DeleteVpcPeeringConnection", params) + if err != nil { + return errors.Wrapf(err, `client.vpc2017Request("DeleteVpcPeeringConnection",%s)`, jsonutils.Marshal(params).String()) + } + return nil +} + +func (region *SRegion) CreateVpcPeeringConnectionEx(vpcId string, opts *cloudprovider.VpcPeeringConnectionCreateOptions) (int64, error) { + params := make(map[string]string) + params["vpcId"] = vpcId + params["peerVpcId"] = opts.PeerVpcId + params["peerUin"] = opts.PeerAccountId + params["peeringConnectionName"] = opts.Name + params["peerRegion"] = opts.PeerRegionId + params["bandwidth"] = fmt.Sprintf("%d", opts.Bandwidth) + body, err := region.vpc2017Request("CreateVpcPeeringConnectionEx", params) + if err != nil { + return 0, errors.Wrapf(err, `client.vpc2017Request("CreateVpcPeeringConnectionEx", %s)`, jsonutils.Marshal(params).String()) + } + taskId, err := body.Int("taskId") + if err != nil { + return 0, errors.Wrapf(err, `%s body.Int("taskId")`, body.String()) + } + return taskId, nil +} + +func (region *SRegion) DescribeVpcTaskResult(taskId string) (int, error) { + params := make(map[string]string) + params["taskId"] = taskId + body, err := region.vpc2017Request("DescribeVpcTaskResult", params) + if err != nil { + return 0, errors.Wrapf(err, `client.vpc2017Request("DescribeVpcTaskResult",%s)`, taskId) + } + status, err := body.Float("data", "status") + if err != nil { + return 0, errors.Wrapf(err, `%s body.Int("data.status")`, body.String()) + } + return int(status), nil +} + +func (region *SRegion) AcceptVpcPeeringConnectionEx(peeringId string) (int64, error) { + params := make(map[string]string) + params["peeringConnectionId"] = peeringId + body, err := region.vpc2017Request("AcceptVpcPeeringConnectionEx", params) + if err != nil { + return 0, errors.Wrapf(err, `client.vpc2017Request("AcceptVpcPeeringConnectionEx", %s)`, jsonutils.Marshal(params).String()) + } + taskId, err := body.Int("taskId") + if err != nil { + return 0, errors.Wrapf(err, `%s body.Int("taskId")`, body.String()) + } + return taskId, nil +} + +func (region *SRegion) DeleteVpcPeeringConnectionEx(peeringId string) (int64, error) { + params := make(map[string]string) + params["peeringConnectionId"] = peeringId + body, err := region.vpc2017Request("DeleteVpcPeeringConnectionEx", params) + if err != nil { + return 0, errors.Wrapf(err, `client.vpc2017Request("DeleteVpcPeeringConnectionEx",%s)`, jsonutils.Marshal(params).String()) + } + taskId, err := body.Int("taskId") + if err != nil { + return 0, errors.Wrapf(err, `%s body.Int("taskId")`, body.String()) + } + return taskId, nil +} + +func (self *SVpcPC) GetId() string { + return self.PeeringConnectionID +} + +func (self *SVpcPC) GetName() string { + return self.PeeringConnectionName +} + +func (self *SVpcPC) GetGlobalId() string { + return self.GetId() +} + +func (self *SVpcPC) GetStatus() string { + switch self.State { + case 1: + return api.VPC_PEERING_CONNECTION_STATUS_ACTIVE + case 0: + return api.VPC_PEERING_CONNECTION_STATUS_PENDING_ACCEPT + default: + return api.VPC_PEERING_CONNECTION_STATUS_UNKNOWN + } +} + +func (self *SVpcPC) Refresh() error { + svpcPC, err := self.vpc.region.GetVpcPeeringConnectionbyId(self.GetId()) + if err != nil { + return errors.Wrapf(err, "self.vpc.region.GetVpcPeeringConnectionbyId(%s)", self.GetId()) + } + return jsonutils.Update(self, svpcPC) +} + +func (self *SVpcPC) GetPeerVpcId() string { + return self.UnPeerVpcID +} + +func (self *SVpcPC) GetPeerAccountId() string { + return self.PeerUin +} + +func (self *SVpcPC) GetEnabled() bool { + return true +} + +func (self *SVpcPC) Delete() error { + if !self.IsCrossRegion() { + return self.vpc.region.DeleteVpcPeeringConnection(self.GetId()) + } + taskId, err := self.vpc.region.DeleteVpcPeeringConnectionEx(self.GetId()) + if err != nil { + return errors.Wrapf(err, "self.vpc.region.DeleteVpcPeeringConnection(%s)", self.GetId()) + } + //err = self.vpc.region.WaitVpcTask(fmt.Sprintf("%d", taskId), time.Second*5, time.Minute*10) + err = cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) { + status, err := self.vpc.region.DescribeVpcTaskResult(fmt.Sprintf("%d", taskId)) + if err != nil { + return false, errors.Wrap(err, "self.vpc.region.DescribeVpcTaskResult") + } + //任务的当前状态。0:成功,1:失败,2:进行中。 + if status == 1 { + return false, errors.Wrap(fmt.Errorf("taskfailed,taskId=%s", taskId), "client.DescribeVpcTaskResult(taskId)") + } + if status == 0 { + return true, nil + } + return false, nil + }) + if err != nil { + return errors.Wrapf(err, "self.region.WaitTask(%s)", taskId) + } + return nil +} + +func (self *SVpcPC) IsCrossRegion() bool { + if len(self.Region) == 0 { + return false + } + return self.Region != self.PeerRegion +} diff --git a/pkg/multicloud/region_base.go b/pkg/multicloud/region_base.go index 83b09b9422..1b4ff4b7fe 100644 --- a/pkg/multicloud/region_base.go +++ b/pkg/multicloud/region_base.go @@ -15,130 +15,131 @@ package multicloud import ( - "fmt" "time" + "yunion.io/x/pkg/errors" + "yunion.io/x/onecloud/pkg/cloudprovider" ) type SRegion struct{} func (r *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) { - return nil, fmt.Errorf("Not Implement GetIDiskById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDiskById") } func (r *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - return nil, fmt.Errorf("Not Implement GetIHostById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIHostById") } func (r *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { - return nil, fmt.Errorf("Not Implement GetIHosts") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIHosts") } func (r *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) { - return nil, fmt.Errorf("Not Implement GetISnapshotById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetISnapshotById") } func (r *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { - return nil, fmt.Errorf("Not Implement GetISnapshots") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetISnapshots") } func (r *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return nil, fmt.Errorf("Not Implement GetIStorageById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIStorageById") } func (r *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - return nil, fmt.Errorf("Not Implement GetIStoragecacheById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIStoragecacheById") } func (r *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { - return nil, fmt.Errorf("Not Implement GetIStoragecaches") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIStoragecaches") } func (r *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { - return nil, fmt.Errorf("Not Implement GetIStorages") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIStorages") } func (r *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) { - return nil, fmt.Errorf("Not Implement GetIVMById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIVMById") } func (r *SRegion) CreateSnapshotPolicy(input *cloudprovider.SnapshotPolicyInput) (string, error) { - return "", fmt.Errorf("CreateSnapshotPolicy not implement") + return "", errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateSnapshotPolicy") } func (r *SRegion) UpdateSnapshotPolicy(input *cloudprovider.SnapshotPolicyInput, snapshotPolicyId string) error { - return fmt.Errorf("UpdateSnapshotPolicy not implement") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "UpdateSnapshotPolicy") } func (r *SRegion) GetISnapshotPolicyById(snapshotPolicyId string) (cloudprovider.ICloudSnapshotPolicy, error) { - return nil, fmt.Errorf("GetISnapshotPolicyById not implement") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetISnapshotPolicyById") } func (self *SRegion) GetISnapshotPolicies() ([]cloudprovider.ICloudSnapshotPolicy, error) { - return nil, fmt.Errorf("GetISnapshotPolicies not implement") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetISnapshotPolicies") } func (self *SRegion) DeleteSnapshotPolicy(string) error { - return fmt.Errorf("DeleteSnapshotPolicy not implement") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "DeleteSnapshotPolicy") } func (self *SRegion) ApplySnapshotPolicyToDisks(snapshotPolicyId string, diskId string) error { - return fmt.Errorf("ApplySnapshotPolicyToDisks not implement") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "ApplySnapshotPolicyToDisks") } func (self *SRegion) CancelSnapshotPolicyToDisks(snapshotPolicyId string, diskId string) error { - return fmt.Errorf("ApplySnapshotPolicyToDisks not implement") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "CancelSnapshotPolicyToDisks") } func (self *SRegion) GetISkus() ([]cloudprovider.ICloudSku, error) { - return nil, cloudprovider.ErrNotSupported + return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "GetISkus") } func (self *SRegion) CreateISku(name string, vCpu int, memoryMb int) error { - return fmt.Errorf("Not Implement CreateISku") + return errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateISku") } func (self *SRegion) GetINetworkInterfaces() ([]cloudprovider.ICloudNetworkInterface, error) { - return nil, fmt.Errorf("Not Implement GetINetworkInterfaces") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetINetworkInterfaces") } func (self *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) { - return nil, fmt.Errorf("GetIDBInstances not implement") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstances") } func (self *SRegion) GetIDBInstanceById(instanceId string) (cloudprovider.ICloudDBInstance, error) { - return nil, fmt.Errorf("GetIDBInstanceById not implement") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceById") } func (self *SRegion) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) { - return nil, fmt.Errorf("Not Implemented GetIDBInstanceBackups") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceBackups") } func (self *SRegion) GetIDBInstanceBackupById(backupId string) (cloudprovider.ICloudDBInstanceBackup, error) { - return nil, fmt.Errorf("Not Implemented GetIDBInstanceBackupById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIDBInstanceBackupById") } func (self *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) { - return nil, fmt.Errorf("Not Implemented GetIElasticcaches") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIElasticcaches") } func (self *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) { - return nil, fmt.Errorf("Not Implemented CreateIDBInstance") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateIDBInstance") } func (self *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) { - return nil, fmt.Errorf("Not Implemented CreateIElasticcaches") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateIElasticcaches") } func (self *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) { - return nil, fmt.Errorf("Not Implemented GetIElasticcacheById") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetIElasticcacheById") } func (self *SRegion) GetICloudEvents(start time.Time, end time.Time, withReadEvent bool) ([]cloudprovider.ICloudEvent, error) { - return nil, fmt.Errorf("Not Implemented GetICloudEvnets") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetICloudEvents") } func (self *SRegion) GetICloudQuotas() ([]cloudprovider.ICloudQuota, error) { - return nil, fmt.Errorf("Not Implemented GetICloudQuotas") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetICloudQuotas") } diff --git a/pkg/multicloud/vpc_base.go b/pkg/multicloud/vpc_base.go index 474a1464f1..5d5ded1e09 100644 --- a/pkg/multicloud/vpc_base.go +++ b/pkg/multicloud/vpc_base.go @@ -15,7 +15,7 @@ package multicloud import ( - "fmt" + "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/cloudprovider" ) @@ -25,5 +25,33 @@ type SVpc struct { } func (self *SVpc) GetINatGateways() ([]cloudprovider.ICloudNatGateway, error) { - return nil, fmt.Errorf("Not Implemented GetNatGateways") + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetINatGateways") +} + +func (self *SVpc) GetICloudVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) { + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetICloudVpcPeeringConnections") +} + +func (self *SVpc) GetICloudVpcPeeringConnectionById(id string) (cloudprovider.ICloudVpcPeeringConnection, error) { + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetICloudVpcPeeringConnectionById") +} + +func (self *SVpc) CreateICloudVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (cloudprovider.ICloudVpcPeeringConnection, error) { + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateICloudVpcPeeringConnection") +} + +func (self *SVpc) AcceptICloudVpcPeeringConnection(id string) error { + return errors.Wrapf(cloudprovider.ErrNotImplemented, "AcceptICloudVpcPeeringConnection", id) +} + +func (self *SVpc) GetAuthorityOwnerId() string { + return "" +} + +func (self *SVpc) CreateRouteToVpcPeeringConnection(cidrBlock, peerId string) error { + return errors.Wrapf(cloudprovider.ErrNotImplemented, "CreateRouteToVpcPeeringConnection") +} + +func (self *SVpc) DeleteVpcPeeringConnectionRoute(vpcPeeringConnectionId string) error { + return errors.Wrapf(cloudprovider.ErrNotImplemented, "DeleteVpcPeeringConnectionRoute") }