loadbalbacer.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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 openstack
  15. import (
  16. "context"
  17. "fmt"
  18. "net/url"
  19. "time"
  20. "github.com/coredns/coredns/plugin/pkg/log"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/pkg/errors"
  23. api "yunion.io/x/cloudmux/pkg/apis/compute"
  24. "yunion.io/x/cloudmux/pkg/cloudprovider"
  25. "yunion.io/x/cloudmux/pkg/multicloud"
  26. )
  27. var LB_ALGORITHM_MAP = map[string]string{
  28. api.LB_SCHEDULER_RR: "ROUND_ROBIN",
  29. api.LB_SCHEDULER_WRR: "ROUND_ROBIN",
  30. api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS",
  31. api.LB_SCHEDULER_SCH: "SOURCE_IP",
  32. api.LB_SCHEDULER_TCH: "SOURCE_IP_PORT",
  33. }
  34. var LB_PROTOCOL_MAP = map[string]string{
  35. api.LB_LISTENER_TYPE_HTTP: "HTTP",
  36. api.LB_LISTENER_TYPE_HTTPS: "HTTPS",
  37. api.LB_LISTENER_TYPE_TERMINATED_HTTPS: "TERMINATED_HTTPS",
  38. api.LB_LISTENER_TYPE_UDP: "UDP",
  39. api.LB_LISTENER_TYPE_TCP: "TCP",
  40. }
  41. var LB_STICKY_SESSION_MAP = map[string]string{
  42. api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE",
  43. api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE",
  44. }
  45. var LB_HEALTHCHECK_TYPE_MAP = map[string]string{
  46. api.LB_HEALTH_CHECK_HTTP: "HTTP",
  47. api.LB_HEALTH_CHECK_HTTPS: "HTTPS",
  48. api.LB_HEALTH_CHECK_TCP: "TCP",
  49. api.LB_HEALTH_CHECK_UDP: "UDP_CONNECT",
  50. }
  51. type SLoadbalancerCreateParams struct {
  52. Description string `json:"description,omitempty"`
  53. AdminStateUp bool `json:"admin_state_up,omitempty"`
  54. ProjectID string `json:"project_id,omitempty"`
  55. VipNetworkId string `json:"vip_network_id,omitempty"`
  56. VipSubnetID string `json:"vip_subnet_id,omitempty"`
  57. VipAddress string `json:"vip_address,omitempty"`
  58. Provider string `json:"provider,omitempty"`
  59. Name string `json:"name,omitempty"`
  60. VipQosPolicyID string `json:"vip_qos_policy_id,omitempty"`
  61. AvailabilityZone string `json:"availability_zone,omitempty"`
  62. Tags []string `json:"tags,omitempty"`
  63. }
  64. type SLoadbalancerID struct {
  65. ID string `json:"id"`
  66. }
  67. type SPoolID struct {
  68. ID string `json:"id"`
  69. }
  70. type SMemberID struct {
  71. ID string `json:"id"`
  72. }
  73. type SListenerID struct {
  74. ID string `json:"id"`
  75. }
  76. type SL7PolicieID struct {
  77. ID string `json:"id"`
  78. }
  79. type SL7RuleID struct {
  80. ID string `json:"id"`
  81. }
  82. type SLoadbalancer struct {
  83. multicloud.SLoadbalancerBase
  84. OpenStackTags
  85. region *SRegion
  86. Description string `json:"description"`
  87. AdminStateUp bool `json:"admin_state_up"`
  88. ProjectID string `json:"project_id"`
  89. ProvisioningStatus string `json:"provisioning_status"`
  90. FlavorID string `json:"flavor_id"`
  91. VipSubnetID string `json:"vip_subnet_id"`
  92. ListenerIds []SListenerID `json:"listeners"`
  93. VipAddress string `json:"vip_address"`
  94. VipNetworkID string `json:"vip_network_id"`
  95. VipPortID string `json:"vip_port_id"`
  96. Provider string `json:"provider"`
  97. PoolIds []SPoolID `json:"pools"`
  98. CreatedAt string `json:"created_at"`
  99. UpdatedAt string `json:"updated_at"`
  100. ID string `json:"id"`
  101. OperatingStatus string `json:"operating_status"`
  102. Name string `json:"name"`
  103. VipQosPolicyID string `json:"vip_qos_policy_id"`
  104. AvailabilityZone string `json:"availability_zone"`
  105. Tags []string `json:"tags"`
  106. }
  107. func waitLbResStatus(res cloudprovider.ICloudResource, interval time.Duration, timeout time.Duration) error {
  108. err := cloudprovider.WaitMultiStatus(res, []string{api.LB_STATUS_ENABLED, api.LB_STATUS_UNKNOWN}, interval, timeout)
  109. if err != nil {
  110. return errors.Wrap(err, "waitLbResStatus(res, interval, timeout)")
  111. }
  112. if res.GetStatus() == api.LB_STATUS_UNKNOWN {
  113. return errors.Wrap(fmt.Errorf("status error"), "check status")
  114. }
  115. return nil
  116. }
  117. func (lb *SLoadbalancer) GetName() string {
  118. return lb.Name
  119. }
  120. func (lb *SLoadbalancer) GetId() string {
  121. return lb.ID
  122. }
  123. func (lb *SLoadbalancer) GetGlobalId() string {
  124. return lb.ID
  125. }
  126. func (lb *SLoadbalancer) GetStatus() string {
  127. switch lb.ProvisioningStatus {
  128. case "ACTIVE":
  129. return api.LB_STATUS_ENABLED
  130. case "PENDING_CREATE":
  131. return api.LB_CREATING
  132. case "PENDING_UPDATE":
  133. return api.LB_SYNC_CONF
  134. case "PENDING_DELETE":
  135. return api.LB_STATUS_DELETING
  136. case "DELETED":
  137. return api.LB_STATUS_DELETED
  138. default:
  139. return api.LB_STATUS_UNKNOWN
  140. }
  141. }
  142. func (lb *SLoadbalancer) GetAddress() string {
  143. return lb.VipAddress
  144. }
  145. func (lb *SLoadbalancer) GetAddressType() string {
  146. eip, err := lb.GetIEIPs()
  147. if err != nil {
  148. return api.LB_ADDR_TYPE_INTRANET
  149. }
  150. if eip == nil {
  151. return api.LB_ADDR_TYPE_INTRANET
  152. }
  153. return api.LB_ADDR_TYPE_INTERNET
  154. }
  155. func (lb *SLoadbalancer) GetNetworkType() string {
  156. network, err := lb.region.GetVpc(lb.VipNetworkID)
  157. if err != nil {
  158. log.Error(errors.Wrapf(err, "lb.region.GetNetwork(%s)", lb.VipNetworkID))
  159. }
  160. if network.NetworkType == "flat" || network.NetworkType == "vlan" {
  161. return api.LB_NETWORK_TYPE_CLASSIC
  162. }
  163. return api.LB_NETWORK_TYPE_VPC
  164. }
  165. func (lb *SLoadbalancer) GetNetworkIds() []string {
  166. return []string{lb.VipSubnetID}
  167. }
  168. func (lb *SLoadbalancer) GetZoneId() string {
  169. return lb.AvailabilityZone
  170. }
  171. func (self *SLoadbalancer) GetZone1Id() string {
  172. return ""
  173. }
  174. func (lb *SLoadbalancer) IsEmulated() bool {
  175. return false
  176. }
  177. func (lb *SLoadbalancer) GetVpcId() string {
  178. return lb.VipNetworkID
  179. }
  180. func (lb *SLoadbalancer) Refresh() error {
  181. loadbalancer, err := lb.region.GetLoadbalancerbyId(lb.ID)
  182. if err != nil {
  183. return err
  184. }
  185. return jsonutils.Update(lb, loadbalancer)
  186. }
  187. func (region *SRegion) GetLoadbalancers() ([]SLoadbalancer, error) {
  188. loadbalancers := []SLoadbalancer{}
  189. resource := "/v2/lbaas/loadbalancers"
  190. query := url.Values{}
  191. for {
  192. resp, err := region.lbList(resource, query)
  193. if err != nil {
  194. return nil, errors.Wrap(err, "lbList")
  195. }
  196. part := struct {
  197. Loadbalancers []SLoadbalancer
  198. LoadbalancersLinks SNextLinks
  199. }{}
  200. err = resp.Unmarshal(&part)
  201. if err != nil {
  202. return nil, errors.Wrap(err, "resp.Unmarshal")
  203. }
  204. loadbalancers = append(loadbalancers, part.Loadbalancers...)
  205. marker := part.LoadbalancersLinks.GetNextMark()
  206. if len(marker) == 0 {
  207. break
  208. }
  209. query.Set("marker", marker)
  210. }
  211. for i := 0; i < len(loadbalancers); i++ {
  212. loadbalancers[i].region = region
  213. }
  214. return loadbalancers, nil
  215. }
  216. func (region *SRegion) GetLoadbalancerbyId(loadbalancerId string) (*SLoadbalancer, error) {
  217. // region.client.Debug(true)
  218. body, err := region.lbGet(fmt.Sprintf("/v2/lbaas/loadbalancers/%s", loadbalancerId))
  219. if err != nil {
  220. return nil, errors.Wrapf(err, `region.lbGet(/v2/lbaas/loadbalancers/%s)`, loadbalancerId)
  221. }
  222. loadbalancer := SLoadbalancer{}
  223. err = body.Unmarshal(&loadbalancer, "loadbalancer")
  224. if err != nil {
  225. return nil, errors.Wrap(err, "resp.Unmarshal(loadbalancer)")
  226. }
  227. loadbalancer.region = region
  228. return &loadbalancer, nil
  229. }
  230. func (region *SRegion) CreateLoadBalancer(loadbalancer *cloudprovider.SLoadbalancerCreateOptions) (*SLoadbalancer, error) {
  231. type CreateParams struct {
  232. Loadbalancer SLoadbalancerCreateParams `json:"loadbalancer"`
  233. }
  234. params := CreateParams{}
  235. params.Loadbalancer.AdminStateUp = true
  236. params.Loadbalancer.AvailabilityZone = loadbalancer.ZoneId
  237. params.Loadbalancer.Name = loadbalancer.Name
  238. params.Loadbalancer.ProjectID = loadbalancer.ProjectId
  239. params.Loadbalancer.VipSubnetID = loadbalancer.NetworkIds[0]
  240. params.Loadbalancer.VipAddress = loadbalancer.Address
  241. body, err := region.lbPost("/v2/lbaas/loadbalancers", jsonutils.Marshal(params))
  242. if err != nil {
  243. return nil, errors.Wrap(err, `region.lbPost("/v2/lbaas/loadbalancers", jsonutils.Marshal(params))`)
  244. }
  245. sloadbalancer := SLoadbalancer{}
  246. err = body.Unmarshal(&sloadbalancer, "loadbalancer")
  247. if err != nil {
  248. return nil, errors.Wrap(err, "body.Unmarshal(sloadbalancer, loadbalancer)")
  249. }
  250. sloadbalancer.region = region
  251. if len(loadbalancer.EipId) > 0 {
  252. err = region.AssociateEipWithPortId(sloadbalancer.VipPortID, loadbalancer.EipId)
  253. if err != nil {
  254. return nil, errors.Wrapf(err, "region.AssociateEipWithPortId(%s, %s)", sloadbalancer.VipPortID, loadbalancer.EipId)
  255. }
  256. }
  257. return &sloadbalancer, nil
  258. }
  259. func (region *SRegion) DeleteLoadbalancer(loadbalancerId string) error {
  260. _, err := region.lbDelete(fmt.Sprintf("/v2/lbaas/loadbalancers/%s?cascade=True", loadbalancerId))
  261. if err != nil {
  262. return errors.Wrapf(err, `region.lbDelete(/v2/lbaas/loadbalancers/%s?cascade=True)`, loadbalancerId)
  263. }
  264. return nil
  265. }
  266. func (lb *SLoadbalancer) Delete(ctx context.Context) error {
  267. return lb.region.DeleteLoadbalancer(lb.ID)
  268. }
  269. func (lb *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  270. ibackendgroups := []cloudprovider.ICloudLoadbalancerBackendGroup{}
  271. for i := 0; i < len(lb.PoolIds); i++ {
  272. pool, err := lb.region.GetLoadbalancerPoolById(lb.PoolIds[i].ID)
  273. if err != nil {
  274. return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerPoolById(%s)", lb.PoolIds[i].ID)
  275. }
  276. ibackendgroups = append(ibackendgroups, pool)
  277. }
  278. return ibackendgroups, nil
  279. }
  280. func (lb *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  281. // ensure lb status
  282. err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  283. if err != nil {
  284. return nil, errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
  285. }
  286. // create pool
  287. spool, err := lb.region.CreateLoadbalancerPool(lb.ID, group)
  288. if err != nil {
  289. return nil, errors.Wrap(err, "lb.region.CreateLoadbalancerPool")
  290. }
  291. // wait spool
  292. err = waitLbResStatus(spool, 10*time.Second, 8*time.Minute)
  293. if err != nil {
  294. return nil, errors.Wrap(err, "waitLbResStatus(spool, 10*time.Second, 8*time.Minute)")
  295. }
  296. return spool, nil
  297. }
  298. func (lb *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListenerCreateOptions) (cloudprovider.ICloudLoadbalancerListener, error) {
  299. // ensure lb status
  300. err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  301. if err != nil {
  302. return nil, errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
  303. }
  304. slistener, err := lb.region.CreateLoadbalancerListener(lb.ID, listener)
  305. if err != nil {
  306. return nil, errors.Wrapf(err, "lb.region.CreateLoadbalancerListener(%s, listener)", lb.ID)
  307. }
  308. return slistener, nil
  309. }
  310. func (lb *SLoadbalancer) GetLoadbalancerSpec() string {
  311. return lb.Description
  312. }
  313. func (lb *SLoadbalancer) GetChargeType() string {
  314. _, err := lb.GetIEIPs()
  315. if err != nil {
  316. log.Errorf("lb.GetIEIP(): %s", err)
  317. }
  318. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  319. }
  320. func (lb *SLoadbalancer) GetEgressMbps() int {
  321. return 0
  322. }
  323. func (lb *SLoadbalancer) GetILoadBalancerBackendGroupById(poolId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  324. err := lb.Refresh()
  325. if err != nil {
  326. return nil, errors.Wrap(err, "lb.Refresh()")
  327. }
  328. index := -1
  329. for i := 0; i < len(lb.PoolIds); i++ {
  330. if poolId == lb.PoolIds[i].ID {
  331. index = i
  332. }
  333. }
  334. if index < 0 {
  335. return nil, cloudprovider.ErrNotFound
  336. }
  337. spool, err := lb.region.GetLoadbalancerPoolById(poolId)
  338. if err != nil {
  339. return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerPoolById(%s)", poolId)
  340. }
  341. if spool.GetStatus() == api.LB_STATUS_DELETING {
  342. return nil, cloudprovider.ErrNotFound
  343. }
  344. return spool, nil
  345. }
  346. func (lb *SLoadbalancer) GetIEIPs() ([]cloudprovider.ICloudEIP, error) {
  347. eips, err := lb.region.GetEips("")
  348. if err != nil {
  349. return nil, errors.Wrapf(err, "lb.region.GetEips()")
  350. }
  351. for _, eip := range eips {
  352. if eip.PortId == lb.VipPortID {
  353. return []cloudprovider.ICloudEIP{&eip}, nil
  354. }
  355. }
  356. return nil, nil
  357. }
  358. func (region *SRegion) UpdateLoadBalancerAdminStateUp(AdminStateUp bool, loadbalancerId string) error {
  359. params := jsonutils.NewDict()
  360. poolParam := jsonutils.NewDict()
  361. poolParam.Add(jsonutils.NewBool(AdminStateUp), "admin_state_up")
  362. params.Add(poolParam, "loadbalancer")
  363. _, err := region.lbUpdate(fmt.Sprintf("/v2/lbaas/loadbalancers/%s", loadbalancerId), params)
  364. if err != nil {
  365. return errors.Wrapf(err, `region.lbUpdate(/v2/lbaas/loadbalancers/%s), params)`, loadbalancerId)
  366. }
  367. return nil
  368. }
  369. func (lb *SLoadbalancer) Start() error {
  370. // ensure lb status
  371. err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  372. if err != nil {
  373. return errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
  374. }
  375. err = lb.region.UpdateLoadBalancerAdminStateUp(true, lb.ID)
  376. if err != nil {
  377. return errors.Wrapf(err, "lb.region.UpdateLoadBalancerAdminStateUp(true, %s)", lb.ID)
  378. }
  379. err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  380. if err != nil {
  381. return errors.Wrap(err, "waitLbResStatus(lb, 10*time.Second, 8*time.Minute)")
  382. }
  383. return nil
  384. }
  385. func (lb *SLoadbalancer) Stop() error {
  386. // ensure lb status
  387. err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  388. if err != nil {
  389. return errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
  390. }
  391. err = lb.region.UpdateLoadBalancerAdminStateUp(false, lb.ID)
  392. if err != nil {
  393. return errors.Wrapf(err, "lb.region.UpdateLoadBalancerAdminStateUp(false,%s)", lb.ID)
  394. }
  395. err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  396. if err != nil {
  397. return errors.Wrap(err, "waitLbResStatus(lb, 10*time.Second, 8*time.Minute)")
  398. }
  399. return nil
  400. }
  401. func (lb *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
  402. return lb.region.GetLoadbalancerListenerbyId(listenerId)
  403. }
  404. func (lb *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
  405. ilisteners := []cloudprovider.ICloudLoadbalancerListener{}
  406. for i := 0; i < len(lb.ListenerIds); i++ {
  407. listener, err := lb.region.GetLoadbalancerListenerbyId(lb.ListenerIds[i].ID)
  408. if err != nil {
  409. return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerListenerbyId(%s)", lb.ListenerIds[i].ID)
  410. }
  411. ilisteners = append(ilisteners, listener)
  412. }
  413. return ilisteners, nil
  414. }
  415. func (lb *SLoadbalancer) GetProjectId() string {
  416. return lb.ProjectID
  417. }
  418. func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
  419. return cloudprovider.ErrNotSupported
  420. }