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