yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/loadbalancerlistener.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 package azure 16 17 import ( 18 "context" 19 "net/url" 20 "regexp" 21 "strconv" 22 "strings" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 26 "yunion.io/x/pkg/errors" 27 28 api "yunion.io/x/cloudmux/pkg/apis/compute" 29 "yunion.io/x/cloudmux/pkg/cloudprovider" 30 "yunion.io/x/cloudmux/pkg/multicloud" 31 ) 32 33 // todo: 目前不支持 入站 NAT 规则 34 // todo: HTTP 设置(端口+协议,其余信息丢弃) + 后端池 = onecloud 路径的路由 的 onecloud 后端服务器组 35 /* 36 应用型LB: urlPathMaps(defaultBackendAddressPool+defaultBackendHttpSettings+requestRoutingRules+httpListeners)= Onecloud监听器 37 4层LB: loadBalancingRules(前端) = Onecloud监听器 38 */ 39 type SLoadBalancerListener struct { 40 multicloud.SResourceBase 41 multicloud.SLoadbalancerRedirectBase 42 43 lb *SLoadbalancer 44 lbrs []cloudprovider.ICloudLoadbalancerListenerRule 45 46 /* 47 IPVersion string 48 FrontendIP string // 前端IP 49 FrontendIPId string // 前端IP ID 50 */ 51 fp *FrontendIPConfiguration 52 53 /* 54 Protocol string // 监听协议 55 HostName string // 监听器 监听域名 56 FrontendPort int // 前端端口 57 */ 58 listener *HTTPListener 59 60 /* 61 Protocol string // 后端协议 62 BackendPort int // 后端端口 63 IdleTimeoutInMinutes int // 空闲超时(分钟) 64 LoadDistribution string // 会话保持方法SourceIP|SourceIPProtocol 65 CookieBasedAffinity string // cookies 关联 66 CookieName string // cookie 名称 67 EnabledConnectionDraining bool // 排出超时这应用于通过 API 调用从后端池明确删除的后端实例,以及运行状况探测报告的运行不正常的实例。 68 ConnectionDrainingSec int // 排出超时时长(秒) 69 RequestTimeout int // 请求超时时间(秒) 70 probe // 健康检查 71 */ 72 backendSetting *BackendHTTPSettingsCollection 73 74 // 75 backendGroup *BackendAddressPool 76 77 /* 78 redirectType string // 重定向类型 79 targetListener // 重定向到监听 80 */ 81 redirect *RedirectConfiguration 82 83 /* 84 "protocol": "Http", 85 "host": "baidu.com", 86 "path": "/test", 87 "interval": 30, 88 "timeout": 30, 89 "unhealthyThreshold": 3, 90 "pickHostNameFromBackendHttpSettings": false, 91 "minServers": 0, 92 "match": { 93 "body": "500", 94 "statusCodes": [ 95 "200-399" 96 ] 97 }, 98 */ 99 healthcheck *Probe 100 101 Name string 102 ID string 103 ProvisioningState string 104 IPVersion string 105 Protocol string // 监听协议 106 LoadDistribution string // 调度算法 107 FrontendPort int // 前端端口 108 BackendPort int // 后端端口 109 ClientIdleTimeout int // 客户端连接超时 110 EnableFloatingIP bool // 浮动 IP 111 EnableTcpReset bool 112 113 rules []PathRule 114 } 115 116 func (self *SLoadBalancerListener) GetId() string { 117 return self.ID 118 } 119 120 func (self *SLoadBalancerListener) GetName() string { 121 return self.Name 122 } 123 124 func (self *SLoadBalancerListener) GetGlobalId() string { 125 return strings.ToLower(self.GetId()) 126 } 127 128 func (self *SLoadBalancerListener) GetStatus() string { 129 switch self.ProvisioningState { 130 case "Succeeded", "Updating", "Deleting": 131 return api.LB_STATUS_ENABLED 132 case "Failed": 133 return api.LB_STATUS_DISABLED 134 default: 135 return api.LB_STATUS_UNKNOWN 136 } 137 } 138 139 func (self *SLoadBalancerListener) Refresh() error { 140 lbl, err := self.lb.GetILoadBalancerListenerById(self.GetId()) 141 if err != nil { 142 return errors.Wrap(err, "GetILoadBalancerListenerById") 143 } 144 145 err = jsonutils.Update(self, lbl) 146 if err != nil { 147 return errors.Wrap(err, "refresh.Update") 148 } 149 150 self.lbrs = nil 151 return nil 152 } 153 154 func (self *SLoadBalancerListener) IsEmulated() bool { 155 return false 156 } 157 158 func (self *SLoadBalancerListener) GetSysTags() map[string]string { 159 return nil 160 } 161 162 func (self *SLoadBalancerListener) GetTags() (map[string]string, error) { 163 if self.fp != nil { 164 if self.fp.Properties.PublicIPAddress != nil && len(self.fp.Properties.PublicIPAddress.ID) > 0 { 165 eip, _ := self.lb.GetIEIPById(self.fp.Properties.PublicIPAddress.ID) 166 if eip != nil { 167 return map[string]string{"FrontendIP": eip.GetIpAddr()}, nil 168 } 169 } 170 171 if len(self.fp.Properties.PrivateIPAddress) > 0 { 172 return map[string]string{"FrontendIP": self.fp.Properties.PrivateIPAddress}, nil 173 } 174 } 175 176 return map[string]string{}, nil 177 } 178 179 func (self *SLoadBalancerListener) SetTags(tags map[string]string, replace bool) error { 180 return errors.Wrap(cloudprovider.ErrNotImplemented, "SetTags") 181 } 182 183 func (self *SLoadBalancerListener) GetProjectId() string { 184 return getResourceGroup(self.GetId()) 185 } 186 187 func (self *SLoadBalancerListener) GetListenerType() string { 188 switch strings.ToLower(self.Protocol) { 189 case "tcp": 190 return api.LB_LISTENER_TYPE_TCP 191 case "udp": 192 return api.LB_LISTENER_TYPE_UDP 193 case "http": 194 return api.LB_LISTENER_TYPE_HTTP 195 case "https": 196 return api.LB_LISTENER_TYPE_HTTPS 197 default: 198 return "" 199 } 200 } 201 202 func (self *SLoadBalancerListener) GetListenerPort() int { 203 return int(self.FrontendPort) 204 } 205 206 func (self *SLoadBalancerListener) GetScheduler() string { 207 switch self.LoadDistribution { 208 case "SourceIPProtocol", "SourceIP": 209 return api.LB_SCHEDULER_SCH 210 default: 211 return "" 212 } 213 } 214 215 func (self *SLoadBalancerListener) GetAclStatus() string { 216 return api.LB_BOOL_OFF 217 } 218 219 func (self *SLoadBalancerListener) GetAclType() string { 220 return "" 221 } 222 223 func (self *SLoadBalancerListener) GetAclId() string { 224 return "" 225 } 226 227 func (self *SLoadBalancerListener) GetEgressMbps() int { 228 return 0 229 } 230 231 func (self *SLoadBalancerListener) GetHealthCheck() string { 232 // if self.probe != nil 233 if self.healthcheck != nil { 234 return api.LB_BOOL_ON 235 } 236 237 return api.LB_BOOL_OFF 238 } 239 240 func (self *SLoadBalancerListener) GetHealthCheckType() string { 241 if self.healthcheck == nil { 242 return "" 243 } 244 switch strings.ToLower(self.healthcheck.Properties.Protocol) { 245 case "tcp": 246 return api.LB_HEALTH_CHECK_TCP 247 case "udp": 248 return api.LB_HEALTH_CHECK_UDP 249 case "http": 250 return api.LB_HEALTH_CHECK_HTTP 251 case "https": 252 return api.LB_HEALTH_CHECK_HTTPS 253 default: 254 return "" 255 } 256 } 257 258 func (self *SLoadBalancerListener) GetHealthCheckTimeout() int { 259 if self.healthcheck == nil { 260 return 0 261 } 262 switch self.GetHealthCheckType() { 263 case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS: 264 return self.healthcheck.Properties.Timeout 265 } 266 267 return self.healthcheck.Properties.IntervalInSeconds 268 } 269 270 func (self *SLoadBalancerListener) GetHealthCheckInterval() int { 271 if self.healthcheck == nil { 272 return 0 273 } 274 switch self.GetHealthCheckType() { 275 case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS: 276 if self.healthcheck.Properties.Interval > 0 { 277 return self.healthcheck.Properties.Interval 278 } 279 } 280 281 return self.healthcheck.Properties.IntervalInSeconds 282 } 283 284 func (self *SLoadBalancerListener) GetHealthCheckRise() int { 285 return 0 286 } 287 288 func (self *SLoadBalancerListener) GetHealthCheckFail() int { 289 if self.healthcheck == nil { 290 return 0 291 } 292 switch self.GetHealthCheckType() { 293 case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS: 294 if self.healthcheck.Properties.UnhealthyThreshold > 0 { 295 return self.healthcheck.Properties.UnhealthyThreshold 296 } 297 } 298 299 return self.healthcheck.Properties.NumberOfProbes 300 } 301 302 func (self *SLoadBalancerListener) GetHealthCheckReq() string { 303 return "" 304 } 305 306 func (self *SLoadBalancerListener) GetHealthCheckExp() string { 307 return "" 308 } 309 310 func (self *SLoadBalancerListener) GetBackendGroupId() string { 311 if self.backendGroup != nil { 312 return self.backendGroup.ID + "::" + strconv.Itoa(self.BackendPort) 313 } 314 315 return "" 316 } 317 318 func (self *SLoadBalancerListener) GetBackendServerPort() int { 319 return self.BackendPort 320 } 321 322 func (self *SLoadBalancerListener) GetHealthCheckDomain() string { 323 if self.healthcheck == nil { 324 return "" 325 } 326 switch self.GetHealthCheckType() { 327 case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS: 328 return self.healthcheck.Properties.Host 329 } 330 331 return "" 332 } 333 334 func (self *SLoadBalancerListener) GetHealthCheckURI() string { 335 if self.healthcheck == nil { 336 return "" 337 } 338 switch self.GetHealthCheckType() { 339 case api.LB_HEALTH_CHECK_HTTP, api.LB_HEALTH_CHECK_HTTPS: 340 return self.healthcheck.Properties.Path 341 } 342 343 return "" 344 } 345 346 /* 347 todo: 和onecloud code不兼容? 348 与此处输入的 HTTP 状态代码或代码范围相匹配的响应将被视为成功。请输入逗号分隔的代码列表(例如 200, 201)或者输入一个代码范围(例如 220-226) 349 */ 350 func (self *SLoadBalancerListener) GetHealthCheckCode() string { 351 return "" 352 } 353 354 func (self *SLoadBalancerListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { 355 return nil, errors.Wrap(cloudprovider.ErrNotImplemented, "CreateILoadBalancerListenerRule") 356 } 357 358 func (self *SLoadBalancerListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) { 359 lbrs, err := self.GetILoadbalancerListenerRules() 360 if err != nil { 361 return nil, errors.Wrap(err, "GetILoadbalancerListenerRules") 362 } 363 364 for i := range lbrs { 365 if lbrs[i].GetId() == ruleId { 366 return lbrs[i], nil 367 } 368 } 369 370 return nil, errors.Wrap(err, "GetILoadBalancerListenerRuleById") 371 } 372 373 func (self *SLoadBalancerListener) GetILoadbalancerListenerRules() ([]cloudprovider.ICloudLoadbalancerListenerRule, error) { 374 if self.lbrs != nil { 375 return self.lbrs, nil 376 } 377 378 irules := []cloudprovider.ICloudLoadbalancerListenerRule{} 379 for i := range self.rules { 380 r := self.rules[i] 381 var redirect *RedirectConfiguration 382 var lbbg *SLoadbalancerBackendGroup 383 if r.Properties.RedirectConfiguration != nil { 384 redirect = self.lb.getRedirectConfiguration(r.Properties.RedirectConfiguration.ID) 385 } else { 386 if r.Properties.BackendAddressPool == nil || r.Properties.BackendHTTPSettings == nil { 387 continue 388 } 389 390 pool := self.lb.getBackendAddressPool(r.Properties.BackendAddressPool.ID) 391 if pool == nil { 392 log.Debugf("getBackendAddressPool %s not found", r.Properties.BackendAddressPool.ID) 393 continue 394 } 395 backsetting := self.lb.getBackendHTTPSettingsCollection(r.Properties.BackendHTTPSettings.ID) 396 if backsetting == nil { 397 log.Debugf("getBackendHTTPSettingsCollection %s not found", r.Properties.BackendHTTPSettings.ID) 398 continue 399 } 400 401 lbbg = &SLoadbalancerBackendGroup{ 402 lb: self.lb, 403 Pool: *pool, 404 DefaultPort: backsetting.Properties.Port, 405 HttpSettings: backsetting, 406 BackendIps: pool.Properties.BackendIPConfigurations, 407 } 408 } 409 410 domain := "" 411 if self.listener != nil { 412 domain = self.listener.Properties.HostName 413 } 414 415 rule := SLoadbalancerListenerRule{ 416 SResourceBase: multicloud.SResourceBase{}, 417 listener: self, 418 lbbg: lbbg, 419 redirect: redirect, 420 Name: r.Name, 421 ID: r.ID, 422 Domain: domain, 423 Properties: r.Properties, 424 } 425 irules = append(irules, &rule) 426 } 427 428 return irules, nil 429 } 430 431 func (self *SLoadBalancerListener) GetStickySession() string { 432 if self.backendSetting == nil { 433 return api.LB_BOOL_OFF 434 } 435 436 if self.backendSetting.Properties.CookieBasedAffinity == "Enabled" { 437 return api.LB_BOOL_ON 438 } else { 439 return api.LB_BOOL_OFF 440 } 441 } 442 443 func (self *SLoadBalancerListener) GetStickySessionType() string { 444 if self.GetStickySession() == api.LB_BOOL_ON { 445 return api.LB_STICKY_SESSION_TYPE_INSERT 446 } 447 return "" 448 } 449 450 func (self *SLoadBalancerListener) GetStickySessionCookie() string { 451 if self.backendSetting == nil { 452 return "" 453 } 454 455 return self.backendSetting.Properties.AffinityCookieName 456 } 457 458 func (self *SLoadBalancerListener) GetStickySessionCookieTimeout() int { 459 if self.backendSetting == nil { 460 return 0 461 } 462 463 if self.backendSetting.Properties.ConnectionDraining.Enabled { 464 _sec := strings.TrimSpace(self.backendSetting.Properties.ConnectionDraining.DrainTimeoutInSEC) 465 sec, _ := strconv.ParseInt(_sec, 10, 64) 466 return int(sec) 467 } 468 469 return 0 470 } 471 472 func (self *SLoadBalancerListener) XForwardedForEnabled() bool { 473 return false 474 } 475 476 func (self *SLoadBalancerListener) GzipEnabled() bool { 477 return false 478 } 479 480 func (self *SLoadBalancerListener) GetCertificateId() string { 481 if self.listener != nil { 482 return self.listener.Properties.SSLCertificate.ID 483 } 484 485 return "" 486 } 487 488 func (self *SLoadBalancerListener) GetTLSCipherPolicy() string { 489 return "" 490 } 491 492 func (self *SLoadBalancerListener) HTTP2Enabled() bool { 493 return self.lb.Properties.EnableHttp2 494 } 495 496 func (self *SLoadBalancerListener) Start() error { 497 return errors.Wrap(cloudprovider.ErrNotImplemented, "Start") 498 } 499 500 func (self *SLoadBalancerListener) Stop() error { 501 return errors.Wrap(cloudprovider.ErrNotImplemented, "Stop") 502 } 503 504 func (self *SLoadBalancerListener) Sync(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) error { 505 return errors.Wrap(cloudprovider.ErrNotImplemented, "Sync") 506 } 507 508 func (self *SLoadBalancerListener) Delete(ctx context.Context) error { 509 return errors.Wrap(cloudprovider.ErrNotImplemented, "Delete") 510 } 511 512 func (self *SLoadBalancerListener) GetRedirect() string { 513 if self.redirect != nil { 514 return api.LB_REDIRECT_RAW 515 } 516 517 return api.LB_REDIRECT_OFF 518 } 519 520 func (self *SLoadBalancerListener) GetRedirectCode() int64 { 521 if self.redirect == nil { 522 return 0 523 } 524 525 switch self.redirect.Properties.RedirectType { 526 case "Permanent": 527 return api.LB_REDIRECT_CODE_301 528 case "Found": 529 return api.LB_REDIRECT_CODE_302 530 case "Temporary", "SeeOther": 531 return api.LB_REDIRECT_CODE_307 532 default: 533 return 0 534 } 535 } 536 537 func (self *SLoadBalancerListener) getRedirectUrl() *url.URL { 538 if self.redirect == nil { 539 return nil 540 } 541 542 if len(self.redirect.Properties.TargetUrl) == 0 { 543 return nil 544 } 545 546 _url := self.redirect.Properties.TargetUrl 547 if matched, _ := regexp.MatchString("^\\w{0,5}://", _url); !matched { 548 _url = "http://" + _url 549 } 550 551 u, err := url.Parse(_url) 552 if err != nil { 553 log.Debugf("url Parse %s : %s", self.redirect.Properties.TargetUrl, err) 554 return nil 555 } 556 557 return u 558 } 559 func (self *SLoadBalancerListener) GetRedirectScheme() string { 560 u := self.getRedirectUrl() 561 if u == nil { 562 return "" 563 } 564 565 return strings.ToLower(u.Scheme) 566 } 567 568 func (self *SLoadBalancerListener) GetRedirectHost() string { 569 u := self.getRedirectUrl() 570 if u == nil { 571 if self.redirect != nil && len(self.redirect.Properties.TargetListener.ID) > 0 { 572 segs := strings.Split(self.redirect.Properties.TargetListener.ID, "/") 573 return segs[len(segs)-1] 574 } 575 return "" 576 } 577 578 return u.Host 579 } 580 581 func (self *SLoadBalancerListener) GetRedirectPath() string { 582 u := self.getRedirectUrl() 583 if u == nil { 584 return "" 585 } 586 587 return u.Path 588 } 589 590 func (self *SLoadBalancerListener) GetClientIdleTimeout() int { 591 return self.ClientIdleTimeout 592 } 593 594 func (self *SLoadBalancerListener) GetBackendConnectTimeout() int { 595 if self.backendSetting == nil { 596 return 0 597 } 598 599 return self.backendSetting.Properties.RequestTimeout 600 }