From aa55b51106d8ddac852b8860ea9fadfb658c155e Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 9 Jan 2020 16:29:36 +0800 Subject: [PATCH] aws sync routetable --- pkg/apis/compute/routetable_const.go | 23 ++++ pkg/compute/guestdrivers/aws.go | 15 +++ pkg/multicloud/aliyun/routetable.go | 2 + pkg/multicloud/aws/network.go | 19 ++- pkg/multicloud/aws/route.go | 63 +++++++++ pkg/multicloud/aws/routetable.go | 171 +++++++++++++++++++++++++ pkg/multicloud/aws/shell/routetable.go | 22 ++++ pkg/multicloud/aws/vpc.go | 15 ++- 8 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 pkg/apis/compute/routetable_const.go create mode 100644 pkg/multicloud/aws/route.go create mode 100644 pkg/multicloud/aws/routetable.go create mode 100644 pkg/multicloud/aws/shell/routetable.go diff --git a/pkg/apis/compute/routetable_const.go b/pkg/apis/compute/routetable_const.go new file mode 100644 index 0000000000..c16fdb7f4c --- /dev/null +++ b/pkg/apis/compute/routetable_const.go @@ -0,0 +1,23 @@ +package compute + +const ( + ROUTE_TABLE_TYPE_VPC = "VPC" // VPC路由器 + ROUTE_TABLE_TYPE_VBR = "VBR" // 边界路由器 +) + +const ( + ROUTE_ENTRY_TYPE_CUSTOM = "Custom" // 自定义路由 + ROUTE_ENTRY_TYPE_SYSTEM = "System" // 系统路由 +) + +const ( + Next_HOP_TYPE_INSTANCE = "Instance" // ECS实例。 + Next_HOP_TYPE_HAVIP = "HaVip" // 高可用虚拟IP。 + Next_HOP_TYPE_VPN = "VpnGateway" // VPN网关。 + Next_HOP_TYPE_NAT = "NatGateway" // NAT网关。 + Next_HOP_TYPE_NETWORK = "NetworkInterface" // 辅助弹性网卡。 + Next_HOP_TYPE_ROUTER = "RouterInterface" // 路由器接口。 + Next_HOP_TYPE_IPV6 = "IPv6Gateway" // IPv6网关。 + Next_HOP_TYPE_INTERNET = "InternetGateway" // Internet网关。 + Next_HOP_TYPE_EGRESS_INTERNET = "EgressInternetGateway" // egress only Internet网关。 +) diff --git a/pkg/compute/guestdrivers/aws.go b/pkg/compute/guestdrivers/aws.go index b7313450b7..e78073fd0e 100644 --- a/pkg/compute/guestdrivers/aws.go +++ b/pkg/compute/guestdrivers/aws.go @@ -30,6 +30,7 @@ import ( "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/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/billing" "yunion.io/x/onecloud/pkg/util/rbacutils" @@ -147,6 +148,20 @@ func (self *SAwsGuestDriver) GetDeployStatus() ([]string, error) { } func (self *SAwsGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) { + if len(input.Eip) > 0 || input.EipBw > 0 { + if len(input.Networks) > 0 { + inetwork, err := db.FetchByIdOrName(models.NetworkManager, userCred, input.Networks[0].Network) + if err != nil { + return nil, err + } + + support_eip := inetwork.(*models.SNetwork).GetMetadataJson("support_eip", nil) + if ok, _ := support_eip.Bool(); !ok { + return nil, httperrors.NewInputParameterError("network %s associated route table has no internet gateway attached.", inetwork.GetName()) + } + } + } + return self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input) } diff --git a/pkg/multicloud/aliyun/routetable.go b/pkg/multicloud/aliyun/routetable.go index 70fa0fe85c..466c500c52 100644 --- a/pkg/multicloud/aliyun/routetable.go +++ b/pkg/multicloud/aliyun/routetable.go @@ -42,6 +42,7 @@ type SRouteEntry struct { NextHops SNextHops } +// Custom:自定义路由。 System:系统路由。 func (route *SRouteEntry) GetType() string { return route.Type } @@ -118,6 +119,7 @@ func (self *SRouteTable) GetRegionId() string { return self.region.RegionId } +// VRouter:VPC路由器。 VBR:边界路由器。 func (self *SRouteTable) GetType() string { return self.RouteTableType } diff --git a/pkg/multicloud/aws/network.go b/pkg/multicloud/aws/network.go index d1c61d0a7d..b2be4a6774 100644 --- a/pkg/multicloud/aws/network.go +++ b/pkg/multicloud/aws/network.go @@ -83,7 +83,24 @@ func (self *SNetwork) IsEmulated() bool { } func (self *SNetwork) GetMetadata() *jsonutils.JSONDict { - return nil + meta := jsonutils.NewDict() + routes, _ := self.wire.vpc.region.GetRouteTablesByNetworkId(self.GetId()) + if len(routes) == 0 { + routes, _ = self.wire.vpc.region.GetRouteTables(self.VpcId, true) + } + + support_eip := false + if len(routes) >= 1 { + for i := range routes[0].Routes { + route := routes[0].Routes[i] + if route.GetNextHopType() == api.Next_HOP_TYPE_INTERNET { + support_eip = true + } + } + } + + meta.Set("support_eip", jsonutils.NewBool(support_eip)) + return meta } func (self *SNetwork) GetIWire() cloudprovider.ICloudWire { diff --git a/pkg/multicloud/aws/route.go b/pkg/multicloud/aws/route.go new file mode 100644 index 0000000000..cee5416f22 --- /dev/null +++ b/pkg/multicloud/aws/route.go @@ -0,0 +1,63 @@ +package aws + +import ( + "strings" + + api "yunion.io/x/onecloud/pkg/apis/compute" +) + +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"` +} + +func (self *SRoute) GetType() string { + if self.GetNextHop() == "local" { + return api.ROUTE_ENTRY_TYPE_SYSTEM + } + + return api.ROUTE_ENTRY_TYPE_CUSTOM +} + +func (self *SRoute) GetCidr() string { + return self.DestinationCIDRBlock +} + +func (self *SRoute) GetNextHopType() string { + segs := strings.Split(self.GetNextHop(), "-") + if len(segs) == 0 { + return "" + } + + switch segs[0] { + case "i": + return api.Next_HOP_TYPE_INSTANCE + case "vgw": + return api.Next_HOP_TYPE_VPN + case "pcx": + return api.Next_HOP_TYPE_ROUTER + case "eni": + return api.Next_HOP_TYPE_NETWORK + case "nat": + return api.Next_HOP_TYPE_NAT + case "igw": + return api.Next_HOP_TYPE_INTERNET + case "eigw": + return api.Next_HOP_TYPE_EGRESS_INTERNET + default: + return "" + } +} + +func (self *SRoute) GetNextHop() string { + if self.GatewayID == nil { + return "" + } + + return *self.GatewayID +} diff --git a/pkg/multicloud/aws/routetable.go b/pkg/multicloud/aws/routetable.go new file mode 100644 index 0000000000..94141a446c --- /dev/null +++ b/pkg/multicloud/aws/routetable.go @@ -0,0 +1,171 @@ +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" +) + +type SRouteTable struct { + region *SRegion + vpc *SVpc + + Associations []Association `json:"Associations"` + PropagatingVgws []string `json:"PropagatingVgws"` + RouteTableID string `json:"RouteTableId"` + Routes []SRoute `json:"Routes"` + VpcID string `json:"VpcId"` + OwnerID string `json:"OwnerId"` +} + +type Association struct { + Main bool `json:"Main"` + RouteTableAssociationID string `json:"RouteTableAssociationId"` + RouteTableID string `json:"RouteTableId"` + SubnetID *string `json:"SubnetId,omitempty"` +} + +func (self *SRouteTable) GetId() string { + return self.RouteTableID +} + +func (self *SRouteTable) GetName() string { + return "" +} + +func (self *SRouteTable) GetGlobalId() string { + return self.GetId() +} + +func (self *SRouteTable) GetStatus() string { + return "" +} + +func (self *SRouteTable) Refresh() error { + ret, err := self.region.GetRouteTable(self.GetId()) + if err != nil { + return errors.Wrap(err, "SRouteTable.Refresh.GetRouteTable") + } + + err = jsonutils.Update(self, ret) + if err != nil { + return errors.Wrap(err, "SRouteTable.Refresh.Update") + } + + return nil +} + +func (self *SRouteTable) IsEmulated() bool { + return false +} + +func (self *SRouteTable) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SRouteTable) GetDescription() string { + return "" +} + +func (self *SRouteTable) GetRegionId() string { + return self.region.GetId() +} + +func (self *SRouteTable) GetVpcId() string { + return self.VpcID +} + +func (self *SRouteTable) GetType() string { + return api.ROUTE_TABLE_TYPE_VPC +} + +func (self *SRouteTable) GetIRoutes() ([]cloudprovider.ICloudRoute, error) { + iroutes := make([]cloudprovider.ICloudRoute, len(self.Routes)) + for i := range self.Routes { + self.Routes[i].routetable = self + iroutes[i] = &self.Routes[i] + } + + return iroutes, nil +} + +func (self *SRegion) GetRouteTables(vpcId string, mainRouteOnly bool) ([]SRouteTable, error) { + input := &ec2.DescribeRouteTablesInput{} + filters := make([]*ec2.Filter, 0) + filters = AppendSingleValueFilter(filters, "vpc-id", vpcId) + if mainRouteOnly { + filters = AppendSingleValueFilter(filters, "association.main", "true") + } + + input.SetFilters(filters) + + ret, err := self.ec2Client.DescribeRouteTables(input) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables") + } + + routeTables := make([]SRouteTable, len(ret.RouteTables)) + err = unmarshalAwsOutput(ret, "RouteTables", routeTables) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetRouteTables.unmarshalAwsOutput") + } + + for i := range routeTables { + routeTables[i].region = self + } + + return routeTables, nil +} + +func (self *SRegion) GetRouteTablesByNetworkId(netId string) ([]SRouteTable, error) { + input := &ec2.DescribeRouteTablesInput{} + filter := &ec2.Filter{} + filter.SetName("association.subnet-id") + filter.SetValues([]*string{&netId}) + input.SetFilters([]*ec2.Filter{filter}) + + ret, err := self.ec2Client.DescribeRouteTables(input) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables") + } + + routeTables := make([]SRouteTable, len(ret.RouteTables)) + err = unmarshalAwsOutput(ret, "RouteTables", routeTables) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetRouteTables.unmarshalAwsOutput") + } + + for i := range routeTables { + routeTables[i].region = self + } + + return routeTables, nil +} + +func (self *SRegion) GetRouteTable(id string) (*SRouteTable, error) { + input := &ec2.DescribeRouteTablesInput{} + input.RouteTableIds = []*string{&id} + + ret, err := self.ec2Client.DescribeRouteTables(input) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables") + } + + routeTables := make([]SRouteTable, len(ret.RouteTables)) + err = unmarshalAwsOutput(ret, "RouteTables", routeTables) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetRouteTables.unmarshalAwsOutput") + } + + if len(routeTables) == 1 { + routeTables[0].region = self + return &routeTables[0], nil + } else if len(routeTables) == 0 { + return nil, errors.ErrNotFound + } else { + return nil, errors.ErrDuplicateId + } +} diff --git a/pkg/multicloud/aws/shell/routetable.go b/pkg/multicloud/aws/shell/routetable.go new file mode 100644 index 0000000000..30175faee0 --- /dev/null +++ b/pkg/multicloud/aws/shell/routetable.go @@ -0,0 +1,22 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/aws" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type RouteTableListOptions struct { + VpcId string `vpc id` + } + shellutils.R(&RouteTableListOptions{}, "routetable-list", "List route tables", func(cli *aws.SRegion, args *RouteTableListOptions) error { + routetables, err := cli.GetRouteTables(args.VpcId, false) + if err != nil { + printObject(err) + return nil + } + + printList(routetables, 0, 0, 0, nil) + return nil + }) +} diff --git a/pkg/multicloud/aws/vpc.go b/pkg/multicloud/aws/vpc.go index f0586bd47c..6257236aa7 100644 --- a/pkg/multicloud/aws/vpc.go +++ b/pkg/multicloud/aws/vpc.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/aws/aws-sdk-go/service/ec2" + "yunion.io/x/pkg/errors" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -131,8 +132,18 @@ func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, err } func (self *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) { - rts := []cloudprovider.ICloudRouteTable{} - return rts, nil + 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) Delete() error {