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 }