loadbalancer.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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 huawei
  15. import (
  16. "context"
  17. "fmt"
  18. "net/url"
  19. "strings"
  20. "time"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. api "yunion.io/x/cloudmux/pkg/apis/compute"
  25. "yunion.io/x/cloudmux/pkg/cloudprovider"
  26. "yunion.io/x/cloudmux/pkg/multicloud"
  27. )
  28. var LB_ALGORITHM_MAP = map[string]string{
  29. api.LB_SCHEDULER_WRR: "ROUND_ROBIN",
  30. api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS",
  31. api.LB_SCHEDULER_SCH: "SOURCE_IP",
  32. }
  33. var LBBG_PROTOCOL_MAP = map[string]string{
  34. api.LB_LISTENER_TYPE_HTTP: "HTTP",
  35. api.LB_LISTENER_TYPE_HTTPS: "HTTP",
  36. api.LB_LISTENER_TYPE_UDP: "UDP",
  37. api.LB_LISTENER_TYPE_TCP: "TCP",
  38. }
  39. var LB_STICKY_SESSION_MAP = map[string]string{
  40. api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE",
  41. api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE",
  42. }
  43. var LB_HEALTHCHECK_TYPE_MAP = map[string]string{
  44. api.LB_HEALTH_CHECK_HTTP: "HTTP",
  45. api.LB_HEALTH_CHECK_TCP: "TCP",
  46. api.LB_HEALTH_CHECK_UDP: "UDP_CONNECT",
  47. }
  48. type SLoadbalancer struct {
  49. multicloud.SLoadbalancerBase
  50. HuaweiTags
  51. region *SRegion
  52. subnet *SNetwork
  53. Description string `json:"description"`
  54. ProvisioningStatus string `json:"provisioning_status"`
  55. TenantId string `json:"tenant_id"`
  56. ProjectId string `json:"project_id"`
  57. EnterpriseProjectId string `json:"enterprise_project_id"`
  58. AdminStateUp bool `json:"admin_state_up"`
  59. Provider string `json:"provider"`
  60. Pools []Pool `json:"pools"`
  61. Listeners []Listener `json:"listeners"`
  62. VipPortId string `json:"vip_port_id"`
  63. OperatingStatus string `json:"operating_status"`
  64. VipAddress string `json:"vip_address"`
  65. VipSubnetId string `json:"vip_subnet_id"`
  66. Id string `json:"id"`
  67. Name string `json:"name"`
  68. VpcId string
  69. CreatedAt time.Time `json:"created_at"`
  70. UpdatedAt time.Time `json:"updated_at"`
  71. Publicips []struct {
  72. PublicipId string
  73. PublicipAddress string
  74. IpVersion string
  75. }
  76. }
  77. type Listener struct {
  78. Id string `json:"id"`
  79. }
  80. type Pool struct {
  81. Id string `json:"id"`
  82. }
  83. func (self *SLoadbalancer) GetIEIPs() ([]cloudprovider.ICloudEIP, error) {
  84. ret := []cloudprovider.ICloudEIP{}
  85. for _, ip := range self.Publicips {
  86. if ip.IpVersion != "4" {
  87. continue
  88. }
  89. eip, err := self.region.GetEip(ip.PublicipId)
  90. if err != nil {
  91. return nil, err
  92. }
  93. ret = append(ret, eip)
  94. }
  95. return ret, nil
  96. }
  97. func (self *SLoadbalancer) GetId() string {
  98. return self.Id
  99. }
  100. func (self *SLoadbalancer) GetName() string {
  101. return self.Name
  102. }
  103. func (self *SLoadbalancer) GetGlobalId() string {
  104. return self.Id
  105. }
  106. func (self *SLoadbalancer) GetStatus() string {
  107. return api.LB_STATUS_ENABLED
  108. }
  109. func (self *SLoadbalancer) Refresh() error {
  110. lb, err := self.region.GetLoadbalancer(self.GetId())
  111. if err != nil {
  112. return err
  113. }
  114. return jsonutils.Update(self, lb)
  115. }
  116. func (self *SLoadbalancer) GetProjectId() string {
  117. return self.EnterpriseProjectId
  118. }
  119. func (self *SLoadbalancer) GetAddress() string {
  120. return self.VipAddress
  121. }
  122. // todo: api.LB_ADDR_TYPE_INTERNET?
  123. func (self *SLoadbalancer) GetAddressType() string {
  124. return api.LB_ADDR_TYPE_INTRANET
  125. }
  126. func (self *SLoadbalancer) GetNetworkType() string {
  127. return api.LB_NETWORK_TYPE_VPC
  128. }
  129. func (self *SLoadbalancer) GetNetworkIds() []string {
  130. net := self.GetNetwork()
  131. if net != nil {
  132. return []string{net.GetId()}
  133. }
  134. return []string{}
  135. }
  136. func (self *SLoadbalancer) GetNetwork() *SNetwork {
  137. if self.subnet == nil {
  138. port, err := self.region.GetPort(self.VipPortId)
  139. if err == nil {
  140. net, err := self.region.GetNetwork(port.NetworkID)
  141. if err == nil {
  142. self.subnet = net
  143. } else {
  144. log.Debugf("huawei.SLoadbalancer.getNetwork %s", err)
  145. }
  146. } else {
  147. log.Debugf("huawei.SLoadbalancer.GetPort %s", err)
  148. }
  149. }
  150. return self.subnet
  151. }
  152. func (self *SLoadbalancer) GetVpcId() string {
  153. return self.VpcId
  154. }
  155. func (self *SLoadbalancer) GetZoneId() string {
  156. net := self.GetNetwork()
  157. if net != nil {
  158. z, err := self.region.getZoneById(net.AvailabilityZone)
  159. if err != nil {
  160. log.Infof("getZoneById %s %s", net.AvailabilityZone, err)
  161. return ""
  162. }
  163. return z.GetGlobalId()
  164. }
  165. return ""
  166. }
  167. func (self *SLoadbalancer) GetZone1Id() string {
  168. return ""
  169. }
  170. func (self *SLoadbalancer) GetLoadbalancerSpec() string {
  171. return ""
  172. }
  173. func (self *SLoadbalancer) GetChargeType() string {
  174. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  175. }
  176. func (self *SLoadbalancer) GetEgressMbps() int {
  177. return 0
  178. }
  179. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=DeleteLoadBalancer
  180. func (self *SLoadbalancer) Delete(ctx context.Context) error {
  181. for _, res := range self.Pools {
  182. backends, err := self.region.getLoadBalancerBackends(res.Id)
  183. if err != nil {
  184. return errors.Wrapf(err, "get backend group %s backends", res.Id)
  185. }
  186. for _, backend := range backends {
  187. err := self.region.RemoveLoadBalancerBackend(res.Id, backend.ID)
  188. if err != nil {
  189. return errors.Wrapf(err, "RemoveLoadBalancerBackend")
  190. }
  191. }
  192. pool, err := self.region.GetLoadBalancerBackendGroup(res.Id)
  193. if err != nil {
  194. return errors.Wrapf(err, "GetLoadBalancerBackendGroup")
  195. }
  196. if len(pool.HealthMonitorID) > 0 {
  197. err = self.region.DeleteLoadbalancerHealthCheck(pool.HealthMonitorID)
  198. if err != nil {
  199. return errors.Wrapf(err, "delete health check")
  200. }
  201. }
  202. err = self.region.DeleteLoadBalancerBackendGroup(res.Id)
  203. if err != nil {
  204. return errors.Wrapf(err, "delete backend group %s", res.Id)
  205. }
  206. }
  207. for _, lis := range self.Listeners {
  208. err := self.region.DeleteElbListener(lis.Id)
  209. if err != nil {
  210. return errors.Wrapf(err, "delete listener %s", lis.Id)
  211. }
  212. }
  213. return self.region.DeleteLoadBalancer(self.GetId())
  214. }
  215. func (self *SLoadbalancer) Start() error {
  216. return nil
  217. }
  218. func (self *SLoadbalancer) Stop() error {
  219. return cloudprovider.ErrNotSupported
  220. }
  221. func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
  222. ret, err := self.region.GetLoadBalancerListeners(self.GetId())
  223. if err != nil {
  224. return nil, err
  225. }
  226. iret := make([]cloudprovider.ICloudLoadbalancerListener, 0)
  227. for i := range ret {
  228. listener := ret[i]
  229. listener.lb = self
  230. iret = append(iret, &listener)
  231. }
  232. return iret, nil
  233. }
  234. func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  235. ret, err := self.region.GetLoadBalancerBackendGroups(self.GetId())
  236. if err != nil {
  237. return nil, err
  238. }
  239. iret := make([]cloudprovider.ICloudLoadbalancerBackendGroup, 0)
  240. for i := range ret {
  241. bg := ret[i]
  242. bg.lb = self
  243. bg.region = self.region
  244. iret = append(iret, &bg)
  245. }
  246. return iret, nil
  247. }
  248. func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(opts *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  249. ret, err := self.region.CreateLoadBalancerBackendGroup(self.Id, opts)
  250. if err != nil {
  251. return nil, errors.Wrapf(err, "CreateLoadBalancerBackendGroup")
  252. }
  253. ret.lb = self
  254. return ret, err
  255. }
  256. func (self *SLoadbalancer) CreateHealthCheck(backendGroupId string, healthcheck *cloudprovider.SLoadbalancerHealthCheck) error {
  257. _, err := self.region.CreateLoadBalancerHealthCheck(backendGroupId, healthcheck)
  258. return err
  259. }
  260. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ShowPool
  261. func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  262. ret := &SElbBackendGroup{lb: self, region: self.region}
  263. resp, err := self.region.list(SERVICE_ELB, "elb/pools/"+groupId, nil)
  264. if err != nil {
  265. return nil, err
  266. }
  267. return ret, resp.Unmarshal(ret, "pool")
  268. }
  269. func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, opts *cloudprovider.SLoadbalancerListenerCreateOptions) (cloudprovider.ICloudLoadbalancerListener, error) {
  270. ret, err := self.region.CreateLoadBalancerListener(opts, self.Id)
  271. if err != nil {
  272. return nil, err
  273. }
  274. ret.lb = self
  275. return ret, nil
  276. }
  277. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ShowListener
  278. func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
  279. ret := &SElbListener{lb: self}
  280. resp, err := self.region.list(SERVICE_ELB, "elb/listeners/"+listenerId, nil)
  281. if err != nil {
  282. return nil, err
  283. }
  284. return ret, resp.Unmarshal(ret, "listener")
  285. }
  286. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ShowLoadBalancer
  287. func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) {
  288. resp, err := self.list(SERVICE_ELB, "elb/loadbalancers/"+id, nil)
  289. if err != nil {
  290. return nil, err
  291. }
  292. ret := &SLoadbalancer{region: self}
  293. return ret, resp.Unmarshal(ret, "loadbalancer")
  294. }
  295. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=DeleteLoadBalancer
  296. func (self *SRegion) DeleteLoadBalancer(elbId string) error {
  297. resource := fmt.Sprintf("elb/loadbalancers/%s", elbId)
  298. _, err := self.delete(SERVICE_ELB, resource)
  299. return err
  300. }
  301. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ListListeners
  302. func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, error) {
  303. ret := []SElbListener{}
  304. params := url.Values{}
  305. if len(lbId) > 0 {
  306. params.Set("loadbalancer_id", lbId)
  307. }
  308. return ret, self.lbListAll("elb/listeners", params, "listeners", &ret)
  309. }
  310. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreateLoadBalancer
  311. func (self *SRegion) CreateLoadBalancerListener(listener *cloudprovider.SLoadbalancerListenerCreateOptions, lbId string) (*SElbListener, error) {
  312. params := map[string]interface{}{
  313. "name": listener.Name,
  314. "description": listener.Description,
  315. "protocol_port": listener.ListenerPort,
  316. "loadbalancer_id": lbId,
  317. "http2_enable": listener.EnableHTTP2,
  318. }
  319. switch listener.ListenerType {
  320. case api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP, api.LB_LISTENER_TYPE_HTTP:
  321. params["protocol"] = strings.ToUpper(listener.ListenerType)
  322. case api.LB_LISTENER_TYPE_HTTPS:
  323. params["protocol"] = "TERMINATED_HTTPS"
  324. default:
  325. return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "protocol %s", listener.ListenerType)
  326. }
  327. if len(listener.BackendGroupId) > 0 {
  328. params["default_pool_id"] = listener.BackendGroupId
  329. }
  330. if listener.ListenerType == api.LB_LISTENER_TYPE_HTTPS {
  331. params["default_tls_container_ref"] = listener.CertificateId
  332. }
  333. if listener.XForwardedFor {
  334. params["insert_headers"] = map[string]interface{}{
  335. "X-Forwarded-ELB-IP": listener.XForwardedFor,
  336. }
  337. }
  338. ret := &SElbListener{}
  339. resp, err := self.post(SERVICE_ELB, "elb/listeners", map[string]interface{}{"listener": params})
  340. if err != nil {
  341. return nil, err
  342. }
  343. return ret, resp.Unmarshal(&ret, "listener")
  344. }
  345. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ListPools
  346. func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGroup, error) {
  347. query := url.Values{}
  348. if len(elbId) > 0 {
  349. query.Set("loadbalancer_id", elbId)
  350. }
  351. ret := []SElbBackendGroup{}
  352. return ret, self.lbListAll("elb/pools", query, "pools", &ret)
  353. }
  354. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreatePool
  355. func (self *SRegion) CreateLoadBalancerBackendGroup(lbId string, opts *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) {
  356. params := map[string]interface{}{
  357. "name": opts.Name,
  358. "loadbalancer_id": lbId,
  359. }
  360. switch opts.Scheduler {
  361. case api.LB_SCHEDULER_WRR:
  362. params["lb_algorithm"] = "ROUND_ROBIN"
  363. case api.LB_SCHEDULER_WLC:
  364. params["lb_algorithm"] = "LEAST_CONNECTIONS"
  365. case api.LB_SCHEDULER_SCH:
  366. params["lb_algorithm"] = "SOURCE_IP"
  367. default:
  368. return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "invalid scheduler %s", opts.Scheduler)
  369. }
  370. switch opts.Protocol {
  371. case api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP:
  372. params["protocol"] = strings.ToUpper(opts.Protocol)
  373. case api.LB_LISTENER_TYPE_HTTP, api.LB_LISTENER_TYPE_HTTPS:
  374. params["protocol"] = "HTTP"
  375. default:
  376. return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "invalid protocol %s", opts.Protocol)
  377. }
  378. resp, err := self.post(SERVICE_ELB, "elb/pools", map[string]interface{}{"pool": params})
  379. if err != nil {
  380. return nil, err
  381. }
  382. ret := &SElbBackendGroup{region: self}
  383. err = resp.Unmarshal(ret, "pool")
  384. if err != nil {
  385. return nil, err
  386. }
  387. return ret, nil
  388. }
  389. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreateHealthMonitor
  390. func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
  391. params := map[string]interface{}{
  392. "delay": healthCheck.HealthCheckInterval,
  393. "max_retries": healthCheck.HealthCheckRise,
  394. "pool_id": backendGroupId,
  395. "timeout": healthCheck.HealthCheckTimeout,
  396. "type": LB_HEALTHCHECK_TYPE_MAP[healthCheck.HealthCheckType],
  397. }
  398. if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP {
  399. if len(healthCheck.HealthCheckDomain) > 0 {
  400. params["domain_name"] = healthCheck.HealthCheckDomain
  401. }
  402. if len(healthCheck.HealthCheckURI) > 0 {
  403. params["url_path"] = healthCheck.HealthCheckURI
  404. }
  405. if len(healthCheck.HealthCheckHttpCode) > 0 {
  406. params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode)
  407. }
  408. }
  409. ret := SElbHealthCheck{region: self}
  410. resp, err := self.post(SERVICE_ELB, "elb/healthmonitors", map[string]interface{}{"healthmonitor": params})
  411. if err != nil {
  412. return ret, err
  413. }
  414. return ret, resp.Unmarshal(&ret, "healthmonitor")
  415. }
  416. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=UpdateHealthMonitor
  417. func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
  418. params := map[string]interface{}{
  419. "delay": healthCheck.HealthCheckInterval,
  420. "max_retries": healthCheck.HealthCheckRise,
  421. "timeout": healthCheck.HealthCheckTimeout,
  422. }
  423. if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP {
  424. if len(healthCheck.HealthCheckDomain) > 0 {
  425. params["domain_name"] = healthCheck.HealthCheckDomain
  426. }
  427. if len(healthCheck.HealthCheckURI) > 0 {
  428. params["url_path"] = healthCheck.HealthCheckURI
  429. }
  430. if len(healthCheck.HealthCheckHttpCode) > 0 {
  431. params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode)
  432. }
  433. }
  434. ret := SElbHealthCheck{region: self}
  435. resp, err := self.put(SERVICE_ELB, "elb/healthmonitors/"+healthCheckId, map[string]interface{}{"healthmonitor": params})
  436. if err != nil {
  437. return ret, err
  438. }
  439. return ret, resp.Unmarshal(&ret, "healthmonitor")
  440. }
  441. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=DeleteHealthMonitor
  442. func (self *SRegion) DeleteLoadbalancerHealthCheck(healthCheckId string) error {
  443. _, err := self.delete(SERVICE_ELB, "elb/healthmonitors/"+healthCheckId)
  444. return err
  445. }
  446. func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
  447. return cloudprovider.ErrNotSupported
  448. }
  449. func (self *SRegion) lbListAll(resource string, query url.Values, respKey string, retVal interface{}) error {
  450. ret := jsonutils.NewArray()
  451. for {
  452. resp, err := self.list(SERVICE_ELB, resource, query)
  453. if err != nil {
  454. return err
  455. }
  456. arr, err := resp.GetArray(respKey)
  457. if err != nil {
  458. return errors.Wrapf(err, "get %s", respKey)
  459. }
  460. ret.Add(arr...)
  461. marker, _ := resp.GetString("page_info", "next_marker")
  462. if len(marker) == 0 {
  463. break
  464. }
  465. query.Set("marker", marker)
  466. }
  467. return ret.Unmarshal(retVal)
  468. }
  469. // https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreateLoadBalancer
  470. func (self *SRegion) CreateLoadBalancer(opts *cloudprovider.SLoadbalancerCreateOptions) (*SLoadbalancer, error) {
  471. subnet, err := self.GetNetwork(opts.NetworkIds[0])
  472. if err != nil {
  473. return nil, errors.Wrap(err, "getNetwork")
  474. }
  475. projectId := ""
  476. project, ok := self.client.projects[self.Id]
  477. if ok {
  478. projectId = project.Id
  479. }
  480. params := map[string]interface{}{
  481. "name": opts.Name,
  482. "description": opts.Desc,
  483. "vip_subnet_cidr_id": subnet.NeutronSubnetID,
  484. "provider": "vlb",
  485. "admin_state_up": true,
  486. "guaranteed": true,
  487. "project_id": projectId,
  488. "charge_mode": "lcu",
  489. }
  490. if len(opts.ProjectId) > 0 {
  491. params["enterprise_project_id"] = opts.ProjectId
  492. }
  493. if len(opts.Address) > 0 {
  494. params["vip_address"] = opts.Address
  495. }
  496. tags := []map[string]string{}
  497. for k, v := range opts.Tags {
  498. tags = append(tags, map[string]string{
  499. "key": k,
  500. "value": v,
  501. })
  502. }
  503. if len(tags) > 0 {
  504. params["tags"] = tags
  505. }
  506. if len(opts.EipId) > 0 {
  507. params["publicip_ids"] = []string{opts.EipId}
  508. }
  509. zones, err := self.GetZones()
  510. if err != nil {
  511. return nil, errors.Wrapf(err, "GetZones")
  512. }
  513. zoneIds := []string{}
  514. for i := range zones {
  515. zone := zones[i]
  516. zoneIds = append(zoneIds, zone.ZoneName)
  517. }
  518. params["availability_zone_list"] = zoneIds
  519. resp, err := self.post(SERVICE_ELB, "elb/loadbalancers", map[string]interface{}{"loadbalancer": params})
  520. if err != nil {
  521. return nil, err
  522. }
  523. ret := &SLoadbalancer{region: self}
  524. err = resp.Unmarshal(ret, "loadbalancer")
  525. if err != nil {
  526. return nil, errors.Wrapf(err, "resp.Unmarshal")
  527. }
  528. return ret, nil
  529. }