loadbalabcerpool.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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. "strings"
  20. "time"
  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. type SLoadbalancerPoolCreateParams struct {
  28. LbAlgorithm string `json:"lb_algorithm,omitempty"`
  29. Protocol string `json:"protocol,omitempty"`
  30. Description string `json:"description,omitempty"`
  31. AdminStateUp bool `json:"admin_state_up,omitempty"`
  32. SessionPersistence *SSessionPersistence `json:"session_persistence"`
  33. LoadbalancerID string `json:"loadbalancer_id,omitempty"`
  34. ListenerID string `json:"listener_id,omitempty"`
  35. Name string `json:"name,omitempty"`
  36. Tags []string `json:"tags,omitempty"`
  37. TLSContainerRef string `json:"tls_container_ref,omitempty"`
  38. CaTLSContainerRef string `json:"ca_tls_container_ref,omitempty"`
  39. CrlContainerRef string `json:"crl_container_ref,omitempty"`
  40. TLSEnabled *bool `json:"tls_enabled,omitempty"`
  41. TLSCiphers string `json:"tls_ciphers,omitempty"`
  42. TLSVersions []string `json:"tls_versions,omitempty"`
  43. }
  44. type SLoadbalancerPoolUpdateParams struct {
  45. LbAlgorithm string `json:"lb_algorithm,omitempty"`
  46. Description string `json:"description,omitempty"`
  47. AdminStateUp bool `json:"admin_state_up,omitempty"`
  48. SessionPersistence *SSessionPersistence `json:"session_persistence"`
  49. Name string `json:"name,omitempty"`
  50. Tags []string `json:"tags,omitempty"`
  51. TLSContainerRef string `json:"tls_container_ref,omitempty"`
  52. CaTLSContainerRef string `json:"ca_tls_container_ref,omitempty"`
  53. CrlContainerRef string `json:"crl_container_ref,omitempty"`
  54. TLSEnabled *bool `json:"tls_enabled,omitempty"`
  55. TLSCiphers string `json:"tls_ciphers,omitempty"`
  56. TLSVersions []string `json:"tls_versions,omitempty"`
  57. }
  58. type SSessionPersistence struct {
  59. CookieName string `json:"cookie_name,omitempty"`
  60. Type string `json:"type,omitempty"`
  61. }
  62. type SLoadbalancerPool struct {
  63. multicloud.SLoadbalancerBackendGroupBase
  64. OpenStackTags
  65. region *SRegion
  66. members []SLoadbalancerMember
  67. healthmonitor *SLoadbalancerHealthmonitor
  68. LbAlgorithm string `json:"lb_algorithm"`
  69. Protocol string `json:"protocol"`
  70. Description string `json:"description"`
  71. AdminStateUp bool `json:"admin_state_up"`
  72. LoadbalancerIds []SLoadbalancerID `json:"loadbalancers"`
  73. CreatedAt string `json:"created_at"`
  74. ProvisioningStatus string `json:"provisioning_status"`
  75. UpdatedAt string `json:"updated_at"`
  76. SessionPersistence SSessionPersistence `json:"session_persistence"`
  77. ListenerIds []SListenerID `json:"listeners"`
  78. MemberIds []SMemberID `json:"members"`
  79. HealthmonitorID string `json:"healthmonitor_id"`
  80. ProjectID string `json:"project_id"`
  81. ID string `json:"id"`
  82. OperatingStatus string `json:"operating_status"`
  83. Name string `json:"name"`
  84. Tags []string `json:"tags"`
  85. TLSContainerRef string `json:"tls_container_ref"`
  86. CaTLSContainerRef string `json:"ca_tls_container_ref"`
  87. CrlContainerRef string `json:"crl_container_ref"`
  88. TLSEnabled bool `json:"tls_enabled"`
  89. TLSCiphers string `json:"tls_ciphers"`
  90. TLSVersions []string `json:"tls_versions"`
  91. }
  92. func ToOpenstackHealthCheckHttpCode(c string) string {
  93. c = strings.TrimSpace(c)
  94. segs := strings.Split(c, ",")
  95. ret := []string{}
  96. for _, seg := range segs {
  97. seg = strings.TrimLeft(seg, "http_")
  98. seg = strings.TrimSpace(seg)
  99. seg = strings.Replace(seg, "xx", "00", -1)
  100. ret = append(ret, seg)
  101. }
  102. return strings.Join(ret, ",")
  103. }
  104. func ToOnecloudHealthCheckHttpCode(c string) string {
  105. c = strings.TrimSpace(c)
  106. segs := strings.Split(c, ",")
  107. ret := []string{}
  108. for _, seg := range segs {
  109. seg = strings.TrimSpace(seg)
  110. seg = strings.Replace(seg, "00", "xx", -1)
  111. seg = "http_" + seg
  112. ret = append(ret, seg)
  113. }
  114. return strings.Join(ret, ",")
  115. }
  116. func (pool *SLoadbalancerPool) GetILoadbalancer() cloudprovider.ICloudLoadbalancer {
  117. if len(pool.LoadbalancerIds) != 1 {
  118. return nil
  119. }
  120. loadbalancer, err := pool.region.GetLoadbalancerbyId(pool.LoadbalancerIds[0].ID)
  121. if err != nil {
  122. return nil
  123. }
  124. return loadbalancer
  125. }
  126. func (pool *SLoadbalancerPool) GetLoadbalancerId() string {
  127. if len(pool.LoadbalancerIds) != 1 {
  128. return ""
  129. }
  130. return pool.LoadbalancerIds[0].ID
  131. }
  132. func (pool *SLoadbalancerPool) GetProtocolType() string {
  133. switch pool.Protocol {
  134. case "TCP":
  135. return api.LB_LISTENER_TYPE_TCP
  136. case "UDP":
  137. return api.LB_LISTENER_TYPE_UDP
  138. case "HTTP":
  139. return api.LB_LISTENER_TYPE_HTTP
  140. default:
  141. return ""
  142. }
  143. }
  144. func (pool *SLoadbalancerPool) GetScheduler() string {
  145. switch pool.LbAlgorithm {
  146. case "LEAST_CONNECTIONS":
  147. return api.LB_SCHEDULER_WLC
  148. case "ROUND_ROBIN":
  149. return api.LB_SCHEDULER_WRR
  150. case "SOURCE_IP":
  151. return api.LB_SCHEDULER_SCH
  152. case "SOURCE_IP_PORT":
  153. return api.LB_SCHEDULER_TCH
  154. default:
  155. return ""
  156. }
  157. }
  158. func (pool *SLoadbalancerPool) GetHealthCheck() (*cloudprovider.SLoadbalancerHealthCheck, error) {
  159. healthCheck := cloudprovider.SLoadbalancerHealthCheck{}
  160. healthCheck.HealthCheckDomain = pool.healthmonitor.DomainName
  161. healthCheck.HealthCheckHttpCode = ToOnecloudHealthCheckHttpCode(pool.healthmonitor.ExpectedCodes)
  162. healthCheck.HealthCheckInterval = pool.healthmonitor.Delay
  163. healthCheck.HealthCheckRise = pool.healthmonitor.MaxRetries
  164. healthCheck.HealthCheckFail = pool.healthmonitor.MaxRetriesDown
  165. healthCheck.HealthCheckTimeout = pool.healthmonitor.Timeout
  166. switch pool.healthmonitor.Type {
  167. case "HTTP":
  168. healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_HTTP
  169. case "HTTPS":
  170. healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_HTTPS
  171. case "TCP":
  172. healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_TCP
  173. case "UDP-CONNECT":
  174. healthCheck.HealthCheckType = api.LB_HEALTH_CHECK_UDP
  175. default:
  176. healthCheck.HealthCheckType = ""
  177. }
  178. healthCheck.HealthCheckURI = pool.healthmonitor.URLPath
  179. return &healthCheck, nil
  180. }
  181. func (pool *SLoadbalancerPool) GetStickySession() (*cloudprovider.SLoadbalancerStickySession, error) {
  182. if len(pool.SessionPersistence.Type) == 0 {
  183. return nil, nil
  184. }
  185. var stickySessionType string
  186. switch pool.SessionPersistence.Type {
  187. case "SOURCE_IP":
  188. stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT
  189. case "HTTP_COOKIE":
  190. stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT
  191. case "APP_COOKIE":
  192. stickySessionType = api.LB_STICKY_SESSION_TYPE_SERVER
  193. }
  194. ret := cloudprovider.SLoadbalancerStickySession{
  195. StickySession: api.LB_BOOL_ON,
  196. StickySessionCookie: pool.SessionPersistence.CookieName,
  197. StickySessionType: stickySessionType,
  198. StickySessionCookieTimeout: 0,
  199. }
  200. return &ret, nil
  201. }
  202. func (pool *SLoadbalancerPool) GetName() string {
  203. return pool.Name
  204. }
  205. func (pool *SLoadbalancerPool) GetId() string {
  206. return pool.ID
  207. }
  208. func (pool *SLoadbalancerPool) GetGlobalId() string {
  209. return pool.ID
  210. }
  211. func (pool *SLoadbalancerPool) GetStatus() string {
  212. switch pool.ProvisioningStatus {
  213. case "ACTIVE":
  214. return api.LB_STATUS_ENABLED
  215. case "PENDING_CREATE":
  216. return api.LB_CREATING
  217. case "PENDING_UPDATE":
  218. return api.LB_SYNC_CONF
  219. case "PENDING_DELETE":
  220. return api.LB_STATUS_DELETING
  221. case "DELETED":
  222. return api.LB_STATUS_DELETED
  223. default:
  224. return api.LB_STATUS_UNKNOWN
  225. }
  226. }
  227. func (pool *SLoadbalancerPool) IsDefault() bool {
  228. return false
  229. }
  230. func (pool *SLoadbalancerPool) GetType() string {
  231. return api.LB_BACKENDGROUP_TYPE_NORMAL
  232. }
  233. func (pool *SLoadbalancerPool) IsEmulated() bool {
  234. return false
  235. }
  236. func (region *SRegion) GetLoadbalancerPools() ([]SLoadbalancerPool, error) {
  237. pools := []SLoadbalancerPool{}
  238. resource := "/v2/lbaas/pools"
  239. query := url.Values{}
  240. for {
  241. resp, err := region.lbList(resource, query)
  242. if err != nil {
  243. return nil, errors.Wrap(err, "lbList")
  244. }
  245. part := struct {
  246. Pools []SLoadbalancerPool
  247. PoolsLinks SNextLinks
  248. }{}
  249. err = resp.Unmarshal(&part)
  250. if err != nil {
  251. return nil, errors.Wrap(err, "resp.Unmarshal")
  252. }
  253. pools = append(pools, part.Pools...)
  254. marker := part.PoolsLinks.GetNextMark()
  255. if len(marker) == 0 {
  256. break
  257. }
  258. query.Set("marker", marker)
  259. }
  260. for i := 0; i < len(pools); i++ {
  261. pools[i].region = region
  262. err := pools[i].fetchLoadbalancerHealthmonitor()
  263. if err != nil {
  264. return nil, errors.Wrapf(err, "pools[%d].fetchLoadbalancerHealthmonitor()", i)
  265. }
  266. }
  267. return pools, nil
  268. }
  269. func (region *SRegion) GetLoadbalancerPoolById(poolId string) (*SLoadbalancerPool, error) {
  270. body, err := region.lbGet(fmt.Sprintf("/v2/lbaas/pools/%s", poolId))
  271. if err != nil {
  272. return nil, errors.Wrapf(err, "region.lbGet(fmt.Sprintf(/v2/lbaas/pools/%s)", poolId)
  273. }
  274. pool := SLoadbalancerPool{}
  275. err = body.Unmarshal(&pool, "pool")
  276. if err != nil {
  277. return nil, errors.Wrap(err, "body.Unmarshal")
  278. }
  279. pool.region = region
  280. err = pool.fetchLoadbalancermembers()
  281. if err != nil {
  282. return nil, errors.Wrap(err, "pool.fetchLoadbalancermembers()")
  283. }
  284. err = pool.fetchLoadbalancerHealthmonitor()
  285. if err != nil {
  286. return nil, errors.Wrap(err, "pool.fetchLoadbalancerHealthmonitor()")
  287. }
  288. return &pool, nil
  289. }
  290. func (region *SRegion) CreateLoadbalancerPool(lbId string, opts *cloudprovider.SLoadbalancerBackendGroup) (*SLoadbalancerPool, error) {
  291. type CreateParams struct {
  292. Pool SLoadbalancerPoolCreateParams `json:"pool"`
  293. }
  294. params := CreateParams{}
  295. params.Pool.AdminStateUp = true
  296. params.Pool.LbAlgorithm = opts.Scheduler
  297. params.Pool.Name = opts.Name
  298. params.Pool.LoadbalancerID = lbId
  299. // 绑定规则时不能指定listener
  300. params.Pool.Protocol = opts.Protocol
  301. params.Pool.SessionPersistence = nil
  302. body, err := region.lbPost("/v2/lbaas/pools", jsonutils.Marshal(params))
  303. if err != nil {
  304. return nil, errors.Wrap(err, "region.lbPost(/v2/lbaas/pools)")
  305. }
  306. spool := SLoadbalancerPool{}
  307. spool.region = region
  308. err = body.Unmarshal(&spool, "pool")
  309. if err != nil {
  310. return nil, errors.Wrap(err, "body.Unmarshal(&spool, pool)")
  311. }
  312. return &spool, nil
  313. }
  314. func (pool *SLoadbalancerPool) Refresh() error {
  315. newPool, err := pool.region.GetLoadbalancerPoolById(pool.ID)
  316. if err != nil {
  317. return err
  318. }
  319. return jsonutils.Update(pool, newPool)
  320. }
  321. func (pool *SLoadbalancerPool) fetchLoadbalancermembers() error {
  322. if len(pool.MemberIds) < 1 {
  323. return nil
  324. }
  325. members, err := pool.region.GetLoadbalancerMenbers(pool.ID)
  326. if err != nil {
  327. return err
  328. }
  329. pool.members = members
  330. return nil
  331. }
  332. func (pool *SLoadbalancerPool) fetchLoadbalancerHealthmonitor() error {
  333. if len(pool.HealthmonitorID) < 1 {
  334. return nil
  335. }
  336. healthmonitor, err := pool.region.GetLoadbalancerHealthmonitorById(pool.HealthmonitorID)
  337. if err != nil {
  338. return errors.Wrap(err, "pool.region.GetLoadbalancerHealthmonitorById")
  339. }
  340. pool.healthmonitor = healthmonitor
  341. return nil
  342. }
  343. func (pool *SLoadbalancerPool) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) {
  344. ibackends := []cloudprovider.ICloudLoadbalancerBackend{}
  345. for i := 0; i < len(pool.members); i++ {
  346. ibackends = append(ibackends, &pool.members[i])
  347. }
  348. return ibackends, nil
  349. }
  350. func (pool *SLoadbalancerPool) GetILoadbalancerBackendById(memberId string) (cloudprovider.ICloudLoadbalancerBackend, error) {
  351. for i := 0; i < len(pool.members); i++ {
  352. if pool.members[i].ID == memberId {
  353. return &pool.members[i], nil
  354. }
  355. }
  356. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetILoadbalancerBackendById(%s)", memberId)
  357. }
  358. func (region *SRegion) DeleteLoadBalancerPool(poolId string) error {
  359. _, err := region.lbDelete(fmt.Sprintf("/v2/lbaas/pools/%s", poolId))
  360. if err != nil {
  361. return errors.Wrapf(err, "lbDelete(/v2/lbaas/pools/%s)", poolId)
  362. }
  363. return nil
  364. }
  365. func (pool *SLoadbalancerPool) Delete(ctx context.Context) error {
  366. lb, err := pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())
  367. if err != nil {
  368. return errors.Wrap(err, "pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())")
  369. }
  370. err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  371. if err != nil {
  372. return errors.Wrap(err, `waitLbResStatus(lb, 10*time.Second, 8*time.Minute)`)
  373. }
  374. return pool.region.DeleteLoadBalancerPool(pool.ID)
  375. }
  376. func (pool *SLoadbalancerPool) AddBackendServer(opts *cloudprovider.SLoadbalancerBackend) (cloudprovider.ICloudLoadbalancerBackend, error) {
  377. // ensure lb status
  378. lb, err := pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())
  379. if err != nil {
  380. return nil, errors.Wrap(err, "pool.region.GetLoadbalancerbyId(pool.GetLoadbalancerId())")
  381. }
  382. err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
  383. if err != nil {
  384. return nil, errors.Wrap(err, `waitLbResStatus(lb, 10*time.Second, 8*time.Minute)`)
  385. }
  386. smemeber, err := pool.region.CreateLoadbalancerMember(pool.ID, opts.ExternalId, opts.Weight, opts.Port)
  387. if err != nil {
  388. return nil, errors.Wrapf(err, `CreateLoadbalancerMember(%s,%s,%d,%d)`, pool.ID, opts.ExternalId, opts.Weight, opts.Port)
  389. }
  390. err = waitLbResStatus(smemeber, 10*time.Second, 8*time.Minute)
  391. if err != nil {
  392. return nil, errors.Wrap(err, `waitLbResStatus(smemeber, 10*time.Second, 8*time.Minute)`)
  393. }
  394. smemeber.region = pool.region
  395. smemeber.poolID = pool.ID
  396. pool.members = append(pool.members, *smemeber)
  397. return smemeber, nil
  398. }
  399. // 不是serverId,是memberId
  400. func (pool *SLoadbalancerPool) RemoveBackendServer(opts *cloudprovider.SLoadbalancerBackend) error {
  401. return pool.region.DeleteLoadbalancerMember(pool.ID, opts.ExternalId)
  402. }
  403. func (pool *SLoadbalancerPool) GetProjectId() string {
  404. return pool.ProjectID
  405. }