Files
cloudpods/pkg/multicloud/aws/vpc.go
2021-12-13 16:43:06 +08:00

724 lines
20 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SUserCIDRs struct {
UserCidr []string
}
type SVpc struct {
multicloud.SVpc
multicloud.AwsTags
region *SRegion
iwires []cloudprovider.ICloudWire
secgroups []cloudprovider.ICloudSecurityGroup
RegionId string
VpcId string
VpcName string
CidrBlock string
CidrBlockAssociationSet []string
IsDefault bool
Status string
InstanceTenancy string
}
func (self *SVpc) addWire(wire *SWire) {
if self.iwires == nil {
self.iwires = make([]cloudprovider.ICloudWire, 0)
}
self.iwires = append(self.iwires, wire)
}
func (self *SVpc) GetId() string {
return self.VpcId
}
func (self *SVpc) GetName() string {
if len(self.VpcName) > 0 {
return self.VpcName
}
return self.VpcId
}
func (self *SVpc) GetGlobalId() string {
return self.VpcId
}
func (self *SVpc) GetStatus() string {
// 目前不支持专用主机
if self.InstanceTenancy == "dedicated" {
return api.VPC_STATUS_UNAVAILABLE
}
return strings.ToLower(self.Status)
}
func (self *SVpc) Refresh() error {
new, err := self.region.getVpc(self.VpcId)
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SVpc) IsEmulated() bool {
return false
}
func (self *SVpc) GetRegion() cloudprovider.ICloudRegion {
return self.region
}
func (self *SVpc) GetIsDefault() bool {
return self.IsDefault
}
func (self *SVpc) GetCidrBlock() string {
return strings.Join(self.CidrBlockAssociationSet, ",")
}
func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) {
if self.iwires == nil {
err := self.fetchNetworks()
if err != nil {
return nil, errors.Wrap(err, "fetchNetworks")
}
}
return self.iwires, nil
}
func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
if self.secgroups == nil {
err := self.fetchSecurityGroups()
if err != nil {
return nil, errors.Wrap(err, "fetchSecurityGroups")
}
}
return self.secgroups, nil
}
func (self *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) {
tables, err := self.region.GetRouteTables(self.GetId(), false)
if err != nil {
return nil, errors.Wrap(err, "SVpc.GetIRouteTables")
}
itables := make([]cloudprovider.ICloudRouteTable, len(tables))
for i := range tables {
tables[i].vpc = self
itables[i] = &tables[i]
}
return itables, nil
}
func (self *SVpc) GetIRouteTableById(routeTableId string) (cloudprovider.ICloudRouteTable, error) {
routeTable, err := self.region.GetRouteTable(routeTableId)
if err != nil {
return nil, errors.Wrapf(err, "self.region.GetRouteTable(routeTableId:%s)", routeTableId)
}
routeTable.vpc = self
return routeTable, nil
}
/*
Deletes the specified VPC. You must detach or delete all gateways and resources that are associated with
the VPC before you can delete it. For example, you must terminate all instances running in the VPC,
delete all security groups associated with the VPC (except the default one),
delete all route tables associated with the VPC (except the default one), and so on.
*/
func (self *SVpc) Delete() error {
err := self.DeleteInternetGateways()
if err != nil {
return errors.Wrap(err, "DeleteInternetGateways")
}
// 删除路由表. todo: 3.7版本路由表开放之后,需要同步状态到平台
rts, err := self.GetIRouteTables()
if err != nil {
return errors.Wrap(err, "GetIRouteTables")
}
for i := range rts {
// 主路由表不允许删除
rt := rts[i].(*SRouteTable)
if len(rt.Associations) > 0 {
if rt.Associations[0].Main {
log.Debugf("Delete.RouteTable skipped main route table %s(%s)", rt.GetName(), rt.GetId())
continue
}
}
err = self.region.DeleteRouteTable(rts[i].GetId())
if err != nil {
return errors.Wrap(err, "DeleteRouteTable")
}
}
return self.region.DeleteVpc(self.VpcId)
}
func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) {
if self.iwires == nil {
err := self.fetchNetworks()
if err != nil {
return nil, errors.Wrap(err, "fetchNetworks")
}
}
for i := 0; i < len(self.iwires); i += 1 {
if self.iwires[i].GetGlobalId() == wireId {
return self.iwires[i], nil
}
}
return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetIWireById")
}
func (self *SVpc) getWireByZoneId(zoneId string) *SWire {
for i := 0; i < len(self.iwires); i += 1 {
wire := self.iwires[i].(*SWire)
if wire.zone.ZoneId == zoneId {
return wire
}
}
zone, err := self.region.getZoneById(zoneId)
if err != nil {
return nil
}
return &SWire{
zone: zone,
vpc: self,
}
}
func (self *SVpc) fetchNetworks() error {
networks, err := self.region.GetNetwroks(nil, self.VpcId)
if err != nil {
return errors.Wrapf(err, "GetNetwroks(%s)", self.VpcId)
}
for i := 0; i < len(networks); i += 1 {
wire := self.getWireByZoneId(networks[i].ZoneId)
networks[i].wire = wire
wire.addNetwork(&networks[i])
}
return nil
}
func (self *SVpc) revokeSecurityGroup(secgroupId string, instanceId string, keep bool) error {
return self.region.revokeSecurityGroup(secgroupId, instanceId, keep)
}
func (self *SVpc) assignSecurityGroup(secgroupId string, instanceId string) error {
return self.region.assignSecurityGroup(secgroupId, instanceId)
}
func (self *SVpc) fetchSecurityGroups() error {
if len(self.VpcId) == 0 {
return fmt.Errorf("fetchSecurityGroups vpc id is empty")
}
secgroups, _, err := self.region.GetSecurityGroups(self.VpcId, "", "", 0, 0)
if err != nil {
return errors.Wrap(err, "GetSecurityGroups")
}
self.secgroups = make([]cloudprovider.ICloudSecurityGroup, len(secgroups))
for i := 0; i < len(secgroups); i++ {
secgroups[i].vpc = self
self.secgroups[i] = &secgroups[i]
}
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) GetICloudAccepterVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) {
svpcPCs, err := self.getAccepterVpcPeeringConnections()
if err != nil {
return nil, errors.Wrap(err, "self.getAccepterVpcPeeringConnections()")
}
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.DescribeRequesterVpcPeeringConnections()")
}
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) getAccepterVpcPeeringConnections() ([]*SVpcPeeringConnection, error) {
vpcPCs, err := self.region.DescribeAccepterVpcPeeringConnections(self.VpcId)
if err != nil {
return nil, errors.Wrap(err, "self.region.DescribeAccepterVpcPeeringConnections()")
}
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 *SVpc) IsSupportSetExternalAccess() bool {
return true
}
func (self *SVpc) GetExternalAccessMode() string {
igws, err := self.region.GetInternetGateways(self.GetId())
if err != nil {
log.Errorf("GetExternalAccessMode.GetInternetGateways %s", err)
}
if len(igws) > 0 {
return api.VPC_EXTERNAL_ACCESS_MODE_EIP
}
return api.VPC_EXTERNAL_ACCESS_MODE_NONE
}
func (self *SVpc) AttachInternetGateway(igwId string) error {
ec2Client, err := self.region.getEc2Client()
if err != nil {
return errors.Wrap(err, "getEc2Client")
}
input := ec2.AttachInternetGatewayInput{}
input.SetInternetGatewayId(igwId)
input.SetVpcId(self.GetId())
_, err = ec2Client.AttachInternetGateway(&input)
if err != nil {
return errors.Wrap(err, "AttachInternetGateway")
}
err = self.AddDefaultInternetGatewayRoute(igwId)
if err != nil {
return errors.Wrap(err, "AddDefaultInternetGatewayRoute")
}
return nil
}
func (self *SVpc) AddDefaultInternetGatewayRoute(igwId string) error {
rt, err := self.GetMainRouteTable()
if err != nil {
return errors.Wrap(err, "GetMainRouteTable")
}
defaultRoute := cloudprovider.RouteSet{}
defaultRoute.NextHop = igwId
defaultRoute.Destination = "0.0.0.0/0"
err = rt.CreateRoute(defaultRoute)
if err != nil {
return errors.Wrap(err, "CreateRoute")
}
return nil
}
func (self *SVpc) GetMainRouteTable() (*SRouteTable, error) {
rt, err := self.region.GetRouteTables(self.GetId(), true)
if err != nil {
return nil, errors.Wrap(err, "GetRouteTables")
}
if len(rt) == 0 {
return nil, errors.Wrap(cloudprovider.ErrNotSupported, "GetMainRouteTable")
}
return &rt[0], nil
}
func (self *SVpc) DetachInternetGateways() error {
igws, err := self.region.GetInternetGateways(self.GetId())
if err != nil {
return errors.Wrap(err, "GetInternetGateways")
}
if len(igws) > 0 {
for i := range igws {
err = self.DetachInternetGateway(igws[i].GetId())
if err != nil {
return errors.Wrap(err, "DetachInternetGateway")
}
}
}
return nil
}
func (self *SVpc) DetachInternetGateway(igwId string) error {
ec2Client, err := self.region.getEc2Client()
if err != nil {
return errors.Wrap(err, "getEc2Client")
}
input := ec2.DetachInternetGatewayInput{}
input.SetInternetGatewayId(igwId)
input.SetVpcId(self.GetId())
_, err = ec2Client.DetachInternetGateway(&input)
if err != nil {
return errors.Wrap(err, "DetachInternetGateway")
}
return nil
}
func (self *SVpc) DeleteInternetGateway(igwId string) error {
ec2Client, err := self.region.getEc2Client()
if err != nil {
return errors.Wrap(err, "getEc2Client")
}
input := ec2.DeleteInternetGatewayInput{}
input.SetInternetGatewayId(igwId)
_, err = ec2Client.DeleteInternetGateway(&input)
if err != nil {
return errors.Wrap(err, "DeleteInternetGateway")
}
return nil
}
func (self *SVpc) DeleteInternetGateways() error {
igws, err := self.region.GetInternetGateways(self.GetId())
if err != nil {
return errors.Wrap(err, "GetInternetGateways")
}
if len(igws) > 0 {
for i := range igws {
err = self.DetachInternetGateway(igws[i].GetId())
if err != nil {
return errors.Wrap(err, "DetachInternetGateway")
}
err = self.DeleteInternetGateway(igws[i].GetId())
if err != nil {
return errors.Wrap(err, "DeleteInternetGateway")
}
}
}
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.")
}
vpcs, err := self.GetVpcs([]string{vpcId})
if err != nil {
return nil, errors.Wrap(err, "GetVpcs")
}
if len(vpcs) != 1 {
return nil, errors.Wrap(cloudprovider.ErrNotFound, "getVpc")
}
vpcs[0].region = self
return &vpcs[0], nil
}
func (self *SRegion) revokeSecurityGroup(secgroupId, instanceId string, keep bool) error {
// todo : keep ? 直接使用assignSecurityGroup 即可?
return nil
}
func (self *SRegion) assignSecurityGroup(secgroupId, instanceId string) error {
return self.assignSecurityGroups([]*string{&secgroupId}, instanceId)
}
func (self *SRegion) assignSecurityGroups(secgroupIds []*string, instanceId string) error {
instance, err := self.GetInstance(instanceId)
if err != nil {
return errors.Wrap(err, "GetInstance")
}
ec2Client, err := self.getEc2Client()
if err != nil {
return errors.Wrap(err, "getEc2Client")
}
for _, eth := range instance.NetworkInterfaces {
params := &ec2.ModifyNetworkInterfaceAttributeInput{}
params.SetNetworkInterfaceId(eth.NetworkInterfaceId)
params.SetGroups(secgroupIds)
_, err := ec2Client.ModifyNetworkInterfaceAttribute(params)
if err != nil {
return err
}
}
return nil
}
func (self *SRegion) DeleteSecurityGroup(secGrpId string) error {
ec2Client, err := self.getEc2Client()
if err != nil {
return errors.Wrap(err, "getEc2Client")
}
params := &ec2.DeleteSecurityGroupInput{}
params.SetGroupId(secGrpId)
_, err = ec2Client.DeleteSecurityGroup(params)
return errors.Wrap(err, "DeleteSecurityGroup")
}
func (self *SRegion) DeleteVpc(vpcId string) error {
ec2Client, err := self.getEc2Client()
if err != nil {
return errors.Wrap(err, "getEc2Client")
}
params := &ec2.DeleteVpcInput{}
params.SetVpcId(vpcId)
_, err = ec2Client.DeleteVpc(params)
return errors.Wrap(err, "DeleteVpc")
}
func (self *SRegion) GetVpcs(vpcId []string) ([]SVpc, error) {
ec2Client, err := self.getEc2Client()
if err != nil {
return nil, errors.Wrap(err, "getEc2Client")
}
params := &ec2.DescribeVpcsInput{}
if len(vpcId) > 0 {
params.SetVpcIds(ConvertedList(vpcId))
}
ret, err := ec2Client.DescribeVpcs(params)
err = parseNotFoundError(err)
if err != nil {
return nil, err
}
vpcs := []SVpc{}
for _, item := range ret.Vpcs {
if err := FillZero(item); err != nil {
return nil, err
}
cidrBlockAssociationSet := []string{}
for i := range item.CidrBlockAssociationSet {
cidr := item.CidrBlockAssociationSet[i]
if cidr.CidrBlockState.State != nil && *cidr.CidrBlockState.State == "associated" {
cidrBlockAssociationSet = append(cidrBlockAssociationSet, *cidr.CidrBlock)
}
}
tagspec := TagSpec{ResourceType: "vpc"}
tagspec.LoadingEc2Tags(item.Tags)
vpc := SVpc{
region: self,
RegionId: self.RegionId,
VpcId: *item.VpcId,
VpcName: tagspec.GetNameTag(),
CidrBlock: *item.CidrBlock,
CidrBlockAssociationSet: cidrBlockAssociationSet,
IsDefault: *item.IsDefault,
Status: *item.State,
InstanceTenancy: *item.InstanceTenancy,
}
jsonutils.Update(&vpc.AwsTags.TagSet, item.Tags)
vpcs = append(vpcs, vpc)
}
return vpcs, nil
}
func (self *SRegion) GetInternetGateways(vpcId string) ([]SInternetGateway, error) {
ec2Client, err := self.getEc2Client()
if err != nil {
return nil, errors.Wrap(err, "getEc2Client")
}
input := ec2.DescribeInternetGatewaysInput{}
filters := make([]*ec2.Filter, 0)
if len(vpcId) > 0 {
filters = AppendSingleValueFilter(filters, "attachment.vpc-id", vpcId)
}
if len(filters) > 0 {
input.SetFilters(filters)
}
output, err := ec2Client.DescribeInternetGateways(&input)
if err != nil {
return nil, errors.Wrap(err, "DescribeInternetGateways")
}
igws := make([]SInternetGateway, len(output.InternetGateways))
err = unmarshalAwsOutput(output, "InternetGateways", &igws)
if err != nil {
return nil, errors.Wrap(err, "unmarshalAwsOutput")
}
for i := range igws {
igws[i].region = self
}
return igws, nil
}