yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/routetable.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package aws
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/aws/aws-sdk-go/service/ec2"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/pkg/errors"
    25  
    26  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  	"yunion.io/x/cloudmux/pkg/multicloud"
    29  )
    30  
    31  type SRouteTable struct {
    32  	multicloud.SResourceBase
    33  	AwsTags
    34  	region *SRegion
    35  	vpc    *SVpc
    36  
    37  	Associations    []Association `json:"Associations"`
    38  	PropagatingVgws []string      `json:"PropagatingVgws"`
    39  	RouteTableID    string        `json:"RouteTableId"`
    40  	Routes          []SRoute      `json:"Routes"`
    41  	VpcID           string        `json:"VpcId"`
    42  	OwnerID         string        `json:"OwnerId"`
    43  }
    44  
    45  type Association struct {
    46  	Main                    bool    `json:"Main"`
    47  	RouteTableAssociationID string  `json:"RouteTableAssociationId"`
    48  	RouteTableID            string  `json:"RouteTableId"`
    49  	GatewayID               *string `json:"GatewayId,omitempty"`
    50  	SubnetID                *string `json:"SubnetId,omitempty"`
    51  }
    52  
    53  func (self *SRouteTable) GetId() string {
    54  	return self.RouteTableID
    55  }
    56  
    57  func (self *SRouteTable) GetName() string {
    58  	return ""
    59  }
    60  
    61  func (self *SRouteTable) GetGlobalId() string {
    62  	return self.GetId()
    63  }
    64  
    65  func (self *SRouteTable) GetStatus() string {
    66  	return api.ROUTE_TABLE_AVAILABLE
    67  }
    68  
    69  func (self *SRouteTable) Refresh() error {
    70  	ret, err := self.region.GetRouteTable(self.GetId())
    71  	if err != nil {
    72  		return errors.Wrap(err, "SRouteTable.Refresh.GetRouteTable")
    73  	}
    74  
    75  	err = jsonutils.Update(self, ret)
    76  	if err != nil {
    77  		return errors.Wrap(err, "SRouteTable.Refresh.Update")
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func (self *SRouteTable) IsEmulated() bool {
    84  	return false
    85  }
    86  
    87  func (self *SRouteTable) GetDescription() string {
    88  	return ""
    89  }
    90  
    91  func (self *SRouteTable) GetRegionId() string {
    92  	return self.region.GetId()
    93  }
    94  
    95  func (self *SRouteTable) GetVpcId() string {
    96  	return self.VpcID
    97  }
    98  
    99  func (self *SRouteTable) GetType() cloudprovider.RouteTableType {
   100  	for i := range self.Associations {
   101  		if self.Associations[i].Main {
   102  			return cloudprovider.RouteTableTypeSystem
   103  		}
   104  	}
   105  	return cloudprovider.RouteTableTypeCustom
   106  }
   107  
   108  func (self *SRouteTable) GetAssociations() []cloudprovider.RouteTableAssociation {
   109  	result := []cloudprovider.RouteTableAssociation{}
   110  	for i := range self.Associations {
   111  		if self.Associations[i].GatewayID != nil {
   112  			association := cloudprovider.RouteTableAssociation{
   113  				AssociationId:        self.Associations[i].RouteTableAssociationID,
   114  				AssociationType:      cloudprovider.RouteTableAssociaToRouter,
   115  				AssociatedResourceId: *self.Associations[i].GatewayID,
   116  			}
   117  			result = append(result, association)
   118  		}
   119  		if self.Associations[i].SubnetID != nil {
   120  			association := cloudprovider.RouteTableAssociation{
   121  				AssociationId:        self.Associations[i].RouteTableAssociationID,
   122  				AssociationType:      cloudprovider.RouteTableAssociaToSubnet,
   123  				AssociatedResourceId: *self.Associations[i].SubnetID,
   124  			}
   125  			result = append(result, association)
   126  		}
   127  	}
   128  	return result
   129  }
   130  
   131  func (self *SRouteTable) CreateRoute(route cloudprovider.RouteSet) error {
   132  	err := self.region.CreateRoute(self.RouteTableID, route.Destination, route.NextHop)
   133  	if err != nil {
   134  		return errors.Wrapf(err, "self.region.CreateRoute(%s,%s,%s)", self.RouteTableID, route.Destination, route.NextHop)
   135  	}
   136  	return nil
   137  }
   138  
   139  func (self *SRouteTable) UpdateRoute(route cloudprovider.RouteSet) error {
   140  	routeInfo := strings.Split(route.RouteId, ":")
   141  	if len(routeInfo) != 2 {
   142  		return errors.Wrap(cloudprovider.ErrNotSupported, "invalid route info")
   143  	}
   144  	err := self.region.RemoveRoute(self.RouteTableID, routeInfo[0])
   145  	if err != nil {
   146  		return errors.Wrapf(err, "self.region.RemoveRoute(%s,%s)", self.RouteTableID, route.Destination)
   147  	}
   148  
   149  	err = self.CreateRoute(route)
   150  	if err != nil {
   151  		return errors.Wrapf(err, "self.CreateRoute(%s)", jsonutils.Marshal(route).String())
   152  	}
   153  	return nil
   154  }
   155  
   156  func (self *SRouteTable) RemoveRoute(route cloudprovider.RouteSet) error {
   157  	err := self.region.RemoveRoute(self.RouteTableID, route.Destination)
   158  	if err != nil {
   159  		return errors.Wrapf(err, "self.region.RemoveRoute(%s,%s)", self.RouteTableID, route.Destination)
   160  	}
   161  	return nil
   162  }
   163  
   164  func (self *SRouteTable) GetIRoutes() ([]cloudprovider.ICloudRoute, error) {
   165  	iroutes := make([]cloudprovider.ICloudRoute, len(self.Routes))
   166  	for i := range self.Routes {
   167  		self.Routes[i].routetable = self
   168  		iroutes[i] = &self.Routes[i]
   169  	}
   170  
   171  	return iroutes, nil
   172  }
   173  
   174  func (self *SRegion) GetRouteTables(vpcId string, mainRouteOnly bool) ([]SRouteTable, error) {
   175  	ec2Client, err := self.getEc2Client()
   176  	if err != nil {
   177  		return nil, errors.Wrap(err, "getEc2Client")
   178  	}
   179  
   180  	input := &ec2.DescribeRouteTablesInput{}
   181  	filters := make([]*ec2.Filter, 0)
   182  	filters = AppendSingleValueFilter(filters, "vpc-id", vpcId)
   183  	if mainRouteOnly {
   184  		filters = AppendSingleValueFilter(filters, "association.main", "true")
   185  	}
   186  
   187  	input.SetFilters(filters)
   188  
   189  	ret, err := ec2Client.DescribeRouteTables(input)
   190  	if err != nil {
   191  		return nil, errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables")
   192  	}
   193  
   194  	routeTables := make([]SRouteTable, len(ret.RouteTables))
   195  	err = unmarshalAwsOutput(ret, "RouteTables", routeTables)
   196  	if err != nil {
   197  		return nil, errors.Wrap(err, "SRegion.GetRouteTables.unmarshalAwsOutput")
   198  	}
   199  
   200  	for i := range routeTables {
   201  		routeTables[i].region = self
   202  	}
   203  
   204  	return routeTables, nil
   205  }
   206  
   207  func (self *SRegion) CreateRoute(routeTableId string, DestinationCIDRBlock string, targetId string) error {
   208  	input := &ec2.CreateRouteInput{}
   209  	input.RouteTableId = &routeTableId
   210  	input.DestinationCidrBlock = &DestinationCIDRBlock
   211  	segs := strings.Split(targetId, "-")
   212  	if len(segs) == 0 {
   213  		return fmt.Errorf("invalid aws vpc targetid:%s", targetId)
   214  	}
   215  	switch segs[0] {
   216  	case "i":
   217  		input.InstanceId = &targetId
   218  	case "igw", "vgw":
   219  		input.GatewayId = &targetId
   220  	case "pcx":
   221  		input.VpcPeeringConnectionId = &targetId
   222  	case "eni":
   223  		input.NetworkInterfaceId = &targetId
   224  	case "nat":
   225  		input.NatGatewayId = &targetId
   226  	case "eigw":
   227  		input.EgressOnlyInternetGatewayId = &targetId
   228  	default:
   229  		return fmt.Errorf("invalid aws vpc targetid:%s", targetId)
   230  	}
   231  	ec2Client, err := self.getEc2Client()
   232  	if err != nil {
   233  		return errors.Wrap(err, "getEc2Client")
   234  	}
   235  	_, err = ec2Client.CreateRoute(input)
   236  	if err != nil {
   237  		return errors.Wrapf(err, "self.ec2Client.CreateRoute(%s)", jsonutils.Marshal(input).String())
   238  	}
   239  	return nil
   240  }
   241  
   242  func (self *SRegion) ReplaceRoute(routeTableId string, DestinationCIDRBlock string, targetId string) error {
   243  	input := &ec2.ReplaceRouteInput{}
   244  	input.RouteTableId = &routeTableId
   245  	input.DestinationCidrBlock = &DestinationCIDRBlock
   246  	segs := strings.Split(targetId, "-")
   247  	if len(segs) == 0 {
   248  		return fmt.Errorf("invalid aws vpc targetid:%s", targetId)
   249  	}
   250  	switch segs[0] {
   251  	case "i":
   252  		input.InstanceId = &targetId
   253  	case "igw", "vgw":
   254  		input.GatewayId = &targetId
   255  	case "pcx":
   256  		input.VpcPeeringConnectionId = &targetId
   257  	case "eni":
   258  		input.NetworkInterfaceId = &targetId
   259  	case "nat":
   260  		input.NatGatewayId = &targetId
   261  	case "eigw":
   262  		input.EgressOnlyInternetGatewayId = &targetId
   263  	default:
   264  		return fmt.Errorf("invalid aws vpc targetid:%s", targetId)
   265  	}
   266  	ec2Client, err := self.getEc2Client()
   267  	if err != nil {
   268  		return errors.Wrap(err, "getEc2Client")
   269  	}
   270  	_, err = ec2Client.ReplaceRoute(input)
   271  	if err != nil {
   272  		return errors.Wrapf(err, "self.ec2Client.ReplaceRouteInput(%s)", jsonutils.Marshal(input).String())
   273  	}
   274  	return nil
   275  }
   276  
   277  func (self *SRegion) RemoveRoute(routeTableId string, DestinationCIDRBlock string) error {
   278  	ec2Client, err := self.getEc2Client()
   279  	if err != nil {
   280  		return errors.Wrap(err, "getEc2Client")
   281  	}
   282  	input := &ec2.DeleteRouteInput{}
   283  	input.RouteTableId = &routeTableId
   284  	input.DestinationCidrBlock = &DestinationCIDRBlock
   285  	_, err = ec2Client.DeleteRoute(input)
   286  	if err != nil {
   287  		return errors.Wrapf(err, "self.ec2Client.DeleteRoute(%s)", jsonutils.Marshal(input).String())
   288  	}
   289  	return nil
   290  }
   291  
   292  func (self *SRegion) GetRouteTablesByNetworkId(netId string) ([]SRouteTable, error) {
   293  	ec2Client, err := self.getEc2Client()
   294  	if err != nil {
   295  		return nil, errors.Wrap(err, "getEc2Client")
   296  	}
   297  
   298  	input := &ec2.DescribeRouteTablesInput{}
   299  	filter := &ec2.Filter{}
   300  	filter.SetName("association.subnet-id")
   301  	filter.SetValues([]*string{&netId})
   302  	input.SetFilters([]*ec2.Filter{filter})
   303  
   304  	ret, err := ec2Client.DescribeRouteTables(input)
   305  	if err != nil {
   306  		return nil, errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables")
   307  	}
   308  
   309  	routeTables := make([]SRouteTable, len(ret.RouteTables))
   310  	err = unmarshalAwsOutput(ret, "RouteTables", routeTables)
   311  	if err != nil {
   312  		return nil, errors.Wrap(err, "SRegion.GetRouteTables.unmarshalAwsOutput")
   313  	}
   314  
   315  	for i := range routeTables {
   316  		routeTables[i].region = self
   317  	}
   318  
   319  	return routeTables, nil
   320  }
   321  
   322  func (self *SRegion) GetRouteTable(id string) (*SRouteTable, error) {
   323  	ec2Client, err := self.getEc2Client()
   324  	if err != nil {
   325  		return nil, errors.Wrap(err, "getEc2Client")
   326  	}
   327  
   328  	input := &ec2.DescribeRouteTablesInput{}
   329  	input.RouteTableIds = []*string{&id}
   330  	ret, err := ec2Client.DescribeRouteTables(input)
   331  	if err != nil {
   332  		return nil, errors.Wrap(err, "SRegion.GetRouteTables.DescribeRouteTables")
   333  	}
   334  
   335  	routeTables := make([]SRouteTable, len(ret.RouteTables))
   336  	err = unmarshalAwsOutput(ret, "RouteTables", routeTables)
   337  	if err != nil {
   338  		return nil, errors.Wrap(err, "SRegion.GetRouteTables.unmarshalAwsOutput")
   339  	}
   340  
   341  	if len(routeTables) == 1 {
   342  		routeTables[0].region = self
   343  		return &routeTables[0], nil
   344  	} else if len(routeTables) == 0 {
   345  		return nil, errors.ErrNotFound
   346  	} else {
   347  		return nil, errors.ErrDuplicateId
   348  	}
   349  }
   350  
   351  func (self *SRegion) DeleteRouteTable(rid string) error {
   352  	input := &ec2.DeleteRouteTableInput{}
   353  	input.SetRouteTableId(rid)
   354  
   355  	ec2Client, err := self.getEc2Client()
   356  	if err != nil {
   357  		return errors.Wrap(err, "getEc2Client")
   358  	}
   359  	_, err = ec2Client.DeleteRouteTable(input)
   360  	if err != nil {
   361  		return errors.Wrap(err, "DeleteRouteTable")
   362  	}
   363  
   364  	return nil
   365  }