routetable.go 9.6 KB

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