yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/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 hcso
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"yunion.io/x/jsonutils"
    21  	"yunion.io/x/pkg/errors"
    22  
    23  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    24  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    25  	"yunion.io/x/cloudmux/pkg/multicloud"
    26  	"yunion.io/x/cloudmux/pkg/multicloud/huawei"
    27  )
    28  
    29  // date: 2019.07.15
    30  // In Huawei cloud, there are only two routing tables in a vpc, which are
    31  // self-defined routing tables and peer-to-peer routing tables.
    32  // The routing in these two tables is different, one's NextHop is a IP address and
    33  // the other one's NextHop address is a instance ID of peer-to-peer connection.
    34  // The former has no id and it's Type is ROUTE_TYPR_IP, and the latter's Type is ROUTE_TYPE_PEER.
    35  
    36  const (
    37  	ROUTE_TYPR_IP   = "IP"
    38  	ROUTE_TYPE_PEER = "peering"
    39  )
    40  
    41  type SRouteEntry struct {
    42  	multicloud.SResourceBase
    43  	huawei.HuaweiTags
    44  	routeTable *SRouteTable
    45  
    46  	ID          string // route ID
    47  	Type        string // route type
    48  	Destination string // route destination
    49  	NextHop     string // route next hop (ip or id)
    50  }
    51  
    52  func (route *SRouteEntry) GetId() string {
    53  	if len(route.ID) == 0 {
    54  		return route.Destination + ":" + route.NextHop
    55  	}
    56  	return route.ID
    57  }
    58  
    59  func (route *SRouteEntry) GetName() string {
    60  	return ""
    61  }
    62  
    63  func (route *SRouteEntry) GetGlobalId() string {
    64  	return route.GetId()
    65  }
    66  
    67  func (route *SRouteEntry) GetStatus() string {
    68  	return api.ROUTE_ENTRY_STATUS_AVAILIABLE
    69  }
    70  
    71  func (route *SRouteEntry) Refresh() error {
    72  	return nil
    73  }
    74  
    75  func (route *SRouteEntry) IsEmulated() bool {
    76  	return false
    77  }
    78  
    79  func (route *SRouteEntry) GetType() string {
    80  	if route.Type == ROUTE_TYPE_PEER {
    81  		return api.ROUTE_ENTRY_TYPE_CUSTOM
    82  	}
    83  	return api.ROUTE_ENTRY_TYPE_SYSTEM
    84  }
    85  
    86  func (route *SRouteEntry) GetCidr() string {
    87  	return route.Destination
    88  }
    89  
    90  func (route *SRouteEntry) GetNextHopType() string {
    91  	// In Huawei Cloud, NextHopType is same with itself
    92  	switch route.Type {
    93  	case ROUTE_TYPE_PEER:
    94  		return api.NEXT_HOP_TYPE_VPCPEERING
    95  	default:
    96  		return ""
    97  	}
    98  }
    99  
   100  func (route *SRouteEntry) GetNextHop() string {
   101  	return route.NextHop
   102  }
   103  
   104  // SRouteTable has no ID and Name because there is no id or name of route table in huawei cloud.
   105  // And some method such as GetId and GetName of ICloudRouteTable has no practical meaning
   106  type SRouteTable struct {
   107  	multicloud.SResourceBase
   108  	huawei.HuaweiTags
   109  	region *SRegion
   110  	vpc    *SVpc
   111  
   112  	VpcId       string
   113  	Description string
   114  	Type        string
   115  	Routes      []*SRouteEntry
   116  }
   117  
   118  func NewSRouteTable(vpc *SVpc, Type string) SRouteTable {
   119  	return SRouteTable{
   120  		region: vpc.region,
   121  		vpc:    vpc,
   122  		Type:   Type,
   123  		VpcId:  vpc.GetId(),
   124  	}
   125  
   126  }
   127  
   128  func (self *SRouteTable) GetId() string {
   129  	return self.GetGlobalId()
   130  }
   131  
   132  func (self *SRouteTable) GetName() string {
   133  	return ""
   134  }
   135  
   136  func (self *SRouteTable) GetGlobalId() string {
   137  	return fmt.Sprintf("%s-%s", self.GetVpcId(), self.GetType())
   138  }
   139  
   140  func (self *SRouteTable) GetStatus() string {
   141  	return api.ROUTE_TABLE_AVAILABLE
   142  }
   143  
   144  func (self *SRouteTable) Refresh() error {
   145  	return nil
   146  }
   147  
   148  func (self *SRouteTable) IsEmulated() bool {
   149  	return false
   150  }
   151  
   152  func (self *SRouteTable) GetDescription() string {
   153  	return self.Description
   154  }
   155  
   156  func (self *SRouteTable) GetRegionId() string {
   157  	return self.region.GetId()
   158  }
   159  
   160  func (self *SRouteTable) GetVpcId() string {
   161  	return self.VpcId
   162  }
   163  
   164  func (self *SRouteTable) GetType() cloudprovider.RouteTableType {
   165  	return cloudprovider.RouteTableTypeSystem
   166  }
   167  
   168  func (self *SRouteTable) GetIRoutes() ([]cloudprovider.ICloudRoute, error) {
   169  	if self.Routes == nil {
   170  		err := self.fetchRoutes()
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  	}
   175  	ret := []cloudprovider.ICloudRoute{}
   176  	for i := range self.Routes {
   177  		ret = append(ret, self.Routes[i])
   178  	}
   179  	return ret, nil
   180  }
   181  
   182  // fetchRoutes fetch Routes
   183  func (self *SRouteTable) fetchRoutes() error {
   184  	if self.Type == ROUTE_TYPR_IP {
   185  		return self.fetchRoutesForIP()
   186  	}
   187  	return self.fetchRoutesForPeer()
   188  }
   189  
   190  // fetchRoutesForIP fetch the Routes which Type is ROUTE_TYPR_IP through vpc's get api
   191  func (self *SRouteTable) fetchRoutesForIP() error {
   192  	ret, err := self.region.ecsClient.Vpcs.Get(self.GetVpcId(), map[string]string{})
   193  	if err != nil {
   194  		return errors.Wrap(err, "get vpc info error")
   195  	}
   196  	routeArray, err := ret.GetArray("routes")
   197  	routes := make([]*SRouteEntry, 0, len(routeArray))
   198  	for i := range routeArray {
   199  		destination, err := routeArray[i].GetString("destination")
   200  		if err != nil {
   201  			return errors.Wrap(err, "get destination of route error")
   202  		}
   203  		nextHop, err := routeArray[i].GetString("nexthop")
   204  		if err != nil {
   205  			return errors.Wrap(err, "get nexthop of route error")
   206  		}
   207  		routes = append(routes, &SRouteEntry{
   208  			routeTable:  self,
   209  			ID:          "",
   210  			Type:        ROUTE_TYPR_IP,
   211  			Destination: destination,
   212  			NextHop:     nextHop,
   213  		})
   214  	}
   215  	self.Routes = routes
   216  	return nil
   217  }
   218  
   219  // fetchRoutesForPeer fetch the routes which Type is ROUTE_TYPE_PEER through vpcRoute's list api
   220  func (self *SRouteTable) fetchRoutesForPeer() error {
   221  	retPeer, err := self.region.ecsClient.VpcRoutes.List(map[string]string{"vpc_id": self.GetVpcId()})
   222  	if err != nil {
   223  		return errors.Wrap(err, "get peer route error")
   224  	}
   225  	routesPeer := make([]*SRouteEntry, 0, retPeer.Total)
   226  	for i := range retPeer.Data {
   227  		route := retPeer.Data[i]
   228  		id, err := route.GetString("id")
   229  		if err != nil {
   230  			return errors.Wrap(err, "get id of peer route error")
   231  		}
   232  		destination, err := route.GetString("destination")
   233  		if err != nil {
   234  			return errors.Wrap(err, "get destination of peer route error")
   235  		}
   236  		nextHop, err := route.GetString("nexthop")
   237  		if err != nil {
   238  			return errors.Wrap(err, "get nexthop of peer route error")
   239  		}
   240  		routesPeer = append(routesPeer, &SRouteEntry{
   241  			routeTable:  self,
   242  			ID:          id,
   243  			Type:        ROUTE_TYPE_PEER,
   244  			Destination: destination,
   245  			NextHop:     nextHop,
   246  		})
   247  	}
   248  	self.Routes = routesPeer
   249  	return nil
   250  }
   251  
   252  func (self *SRouteTable) GetAssociations() []cloudprovider.RouteTableAssociation {
   253  	result := []cloudprovider.RouteTableAssociation{}
   254  	return result
   255  }
   256  
   257  func (self *SRouteTable) CreateRoute(route cloudprovider.RouteSet) error {
   258  	if route.NextHopType != api.NEXT_HOP_TYPE_VPCPEERING {
   259  		return cloudprovider.ErrNotSupported
   260  	}
   261  	err := self.region.CreatePeeringRoute(self.vpc.GetId(), route.Destination, route.NextHop)
   262  	if err != nil {
   263  		return errors.Wrapf(err, " self.region.CreatePeeringRoute(%s,%s,%s)", self.vpc.GetId(), route.Destination, route.NextHop)
   264  	}
   265  	return nil
   266  }
   267  
   268  func (self *SRouteTable) UpdateRoute(route cloudprovider.RouteSet) error {
   269  	err := self.RemoveRoute(route)
   270  	if err != nil {
   271  		return errors.Wrap(err, "self.RemoveRoute(route)")
   272  	}
   273  	err = self.CreateRoute(route)
   274  	if err != nil {
   275  		return errors.Wrap(err, "self.CreateRoute(route)")
   276  	}
   277  	return nil
   278  }
   279  
   280  func (self *SRouteTable) RemoveRoute(route cloudprovider.RouteSet) error {
   281  	err := self.region.DeletePeeringRoute(route.RouteId)
   282  	if err != nil {
   283  		return errors.Wrapf(err, "self.region.DeletePeeringRoute(%s)", route.RouteId)
   284  	}
   285  	return nil
   286  }
   287  
   288  // GetRouteTables return []SRouteTable of self
   289  func (self *SVpc) getRouteTables() ([]SRouteTable, error) {
   290  	// every Vpc has two route table in Huawei Cloud
   291  	routeTableIp := NewSRouteTable(self, ROUTE_TYPR_IP)
   292  	routeTablePeer := NewSRouteTable(self, ROUTE_TYPE_PEER)
   293  	if err := routeTableIp.fetchRoutesForIP(); err != nil {
   294  		return nil, errors.Wrap(err, `get route table whilc type is "ip" error`)
   295  	}
   296  	if err := routeTablePeer.fetchRoutesForPeer(); err != nil {
   297  		return nil, errors.Wrap(err, `get route table whilc type is "peering" error`)
   298  	}
   299  	ret := make([]SRouteTable, 0, 2)
   300  	if len(routeTableIp.Routes) != 0 {
   301  		ret = append(ret, routeTableIp)
   302  	}
   303  	if len(routeTablePeer.Routes) != 0 {
   304  		ret = append(ret, routeTablePeer)
   305  	}
   306  	return ret, nil
   307  }
   308  
   309  // GetRouteTables return []SRouteTable of vpc which id is vpcId if vpcId is no-nil,
   310  // otherwise return []SRouteTable of all vpc in this SRegion
   311  func (self *SRegion) GetRouteTables(vpcId string) ([]SRouteTable, error) {
   312  	vpcs, err := self.GetVpcs()
   313  	if err != nil {
   314  		return nil, errors.Wrap(err, "Get Vpcs error")
   315  	}
   316  	if vpcId != "" {
   317  		for i := range vpcs {
   318  			if vpcs[i].GetId() == vpcId {
   319  				vpcs = vpcs[i : i+1]
   320  				break
   321  			}
   322  		}
   323  	}
   324  	ret := make([]SRouteTable, 0, 2*len(vpcs))
   325  	for _, vpc := range vpcs {
   326  		routetables, err := vpc.getRouteTables()
   327  		if err != nil {
   328  			return nil, errors.Wrapf(err, "get vpc's route tables whilch id is %s error", vpc.GetId())
   329  		}
   330  		ret = append(ret, routetables...)
   331  
   332  	}
   333  	return ret, nil
   334  }
   335  
   336  func (self *SRegion) CreatePeeringRoute(vpcId, destinationCidr, target string) error {
   337  	params := jsonutils.NewDict()
   338  	routeObj := jsonutils.NewDict()
   339  	routeObj.Set("type", jsonutils.NewString("peering"))
   340  	routeObj.Set("nexthop", jsonutils.NewString(target))
   341  	routeObj.Set("destination", jsonutils.NewString(destinationCidr))
   342  	routeObj.Set("vpc_id", jsonutils.NewString(vpcId))
   343  	params.Set("route", routeObj)
   344  	err := DoCreate(self.ecsClient.VpcRoutes.Create, params, nil)
   345  	if err != nil {
   346  		return errors.Wrapf(err, "DoCreate(self.ecsClient.VpcRoutes.Create, %s, &ret)", jsonutils.Marshal(params).String())
   347  	}
   348  	return nil
   349  }
   350  
   351  func (self *SRegion) DeletePeeringRoute(routeId string) error {
   352  	err := DoDelete(self.ecsClient.VpcRoutes.Delete, routeId, nil, nil)
   353  	if err != nil {
   354  		return errors.Wrapf(err, "DoDelete(self.ecsClient.VpcRoutes.Delete,%s,nil)", routeId)
   355  	}
   356  	return nil
   357  }