Files
cloudpods/pkg/multicloud/openstack/loadbalbacer.go
2021-07-16 12:20:52 +08:00

509 lines
16 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:// www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package openstack
import (
"context"
"fmt"
"net/url"
"time"
"github.com/coredns/coredns/plugin/pkg/log"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
var LB_ALGORITHM_MAP = map[string]string{
api.LB_SCHEDULER_RR: "ROUND_ROBIN",
api.LB_SCHEDULER_WRR: "ROUND_ROBIN",
api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS",
api.LB_SCHEDULER_SCH: "SOURCE_IP",
api.LB_SCHEDULER_TCH: "SOURCE_IP_PORT",
}
var LB_PROTOCOL_MAP = map[string]string{
api.LB_LISTENER_TYPE_HTTP: "HTTP",
api.LB_LISTENER_TYPE_HTTPS: "HTTPS",
api.LB_LISTENER_TYPE_TERMINATED_HTTPS: "TERMINATED_HTTPS",
api.LB_LISTENER_TYPE_UDP: "UDP",
api.LB_LISTENER_TYPE_TCP: "TCP",
}
var LB_STICKY_SESSION_MAP = map[string]string{
api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE",
api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE",
}
var LB_HEALTHCHECK_TYPE_MAP = map[string]string{
api.LB_HEALTH_CHECK_HTTP: "HTTP",
api.LB_HEALTH_CHECK_HTTPS: "HTTPS",
api.LB_HEALTH_CHECK_TCP: "TCP",
api.LB_HEALTH_CHECK_UDP: "UDP_CONNECT",
}
type SLoadbalancerCreateParams struct {
Description string `json:"description,omitempty"`
AdminStateUp bool `json:"admin_state_up,omitempty"`
ProjectID string `json:"project_id,omitempty"`
VipNetworkId string `json:"vip_network_id,omitempty"`
VipSubnetID string `json:"vip_subnet_id,omitempty"`
VipAddress string `json:"vip_address,omitempty"`
Provider string `json:"provider,omitempty"`
Name string `json:"name,omitempty"`
VipQosPolicyID string `json:"vip_qos_policy_id,omitempty"`
AvailabilityZone string `json:"availability_zone,omitempty"`
Tags []string `json:"tags,omitempty"`
}
type SLoadbalancerID struct {
ID string `json:"id"`
}
type SPoolID struct {
ID string `json:"id"`
}
type SMemberID struct {
ID string `json:"id"`
}
type SListenerID struct {
ID string `json:"id"`
}
type SL7PolicieID struct {
ID string `json:"id"`
}
type SL7RuleID struct {
ID string `json:"id"`
}
type SLoadbalancer struct {
multicloud.SLoadbalancerBase
multicloud.OpenStackTags
region *SRegion
Description string `json:"description"`
AdminStateUp bool `json:"admin_state_up"`
ProjectID string `json:"project_id"`
ProvisioningStatus string `json:"provisioning_status"`
FlavorID string `json:"flavor_id"`
VipSubnetID string `json:"vip_subnet_id"`
ListenerIds []SListenerID `json:"listeners"`
VipAddress string `json:"vip_address"`
VipNetworkID string `json:"vip_network_id"`
VipPortID string `json:"vip_port_id"`
Provider string `json:"provider"`
PoolIds []SPoolID `json:"pools"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ID string `json:"id"`
OperatingStatus string `json:"operating_status"`
Name string `json:"name"`
VipQosPolicyID string `json:"vip_qos_policy_id"`
AvailabilityZone string `json:"availability_zone"`
Tags []string `json:"tags"`
}
func waitLbResStatus(res cloudprovider.ICloudResource, interval time.Duration, timeout time.Duration) error {
err := cloudprovider.WaitMultiStatus(res, []string{api.LB_STATUS_ENABLED, api.LB_STATUS_UNKNOWN}, interval, timeout)
if err != nil {
return errors.Wrap(err, "waitLbResStatus(res, interval, timeout)")
}
if res.GetStatus() == api.LB_STATUS_UNKNOWN {
return errors.Wrap(fmt.Errorf("status error"), "check status")
}
return nil
}
func (lb *SLoadbalancer) GetName() string {
return lb.Name
}
func (lb *SLoadbalancer) GetId() string {
return lb.ID
}
func (lb *SLoadbalancer) GetGlobalId() string {
return lb.ID
}
func (lb *SLoadbalancer) GetStatus() string {
switch lb.ProvisioningStatus {
case "ACTIVE":
return api.LB_STATUS_ENABLED
case "PENDING_CREATE":
return api.LB_CREATING
case "PENDING_UPDATE":
return api.LB_SYNC_CONF
case "PENDING_DELETE":
return api.LB_STATUS_DELETING
case "DELETED":
return api.LB_STATUS_DELETED
default:
return api.LB_STATUS_UNKNOWN
}
}
func (lb *SLoadbalancer) GetAddress() string {
return lb.VipAddress
}
func (lb *SLoadbalancer) GetAddressType() string {
eip, err := lb.GetIEIP()
if err != nil {
return api.LB_ADDR_TYPE_INTRANET
}
if eip == nil {
return api.LB_ADDR_TYPE_INTRANET
}
return api.LB_ADDR_TYPE_INTERNET
}
func (lb *SLoadbalancer) GetNetworkType() string {
network, err := lb.region.GetVpc(lb.VipNetworkID)
if err != nil {
log.Error(errors.Wrapf(err, "lb.region.GetNetwork(%s)", lb.VipNetworkID))
}
if network.NetworkType == "flat" || network.NetworkType == "vlan" {
return api.LB_NETWORK_TYPE_CLASSIC
}
return api.LB_NETWORK_TYPE_VPC
}
func (lb *SLoadbalancer) GetNetworkIds() []string {
return []string{lb.VipSubnetID}
}
func (lb *SLoadbalancer) GetZoneId() string {
return lb.AvailabilityZone
}
func (self *SLoadbalancer) GetZone1Id() string {
return ""
}
func (lb *SLoadbalancer) IsEmulated() bool {
return false
}
func (lb *SLoadbalancer) GetVpcId() string {
return lb.VipNetworkID
}
func (lb *SLoadbalancer) Refresh() error {
loadbalancer, err := lb.region.GetLoadbalancerbyId(lb.ID)
if err != nil {
return err
}
return jsonutils.Update(lb, loadbalancer)
}
func (region *SRegion) GetLoadbalancers() ([]SLoadbalancer, error) {
loadbalancers := []SLoadbalancer{}
resource := "/v2/lbaas/loadbalancers"
query := url.Values{}
for {
resp, err := region.lbList(resource, query)
if err != nil {
return nil, errors.Wrap(err, "lbList")
}
part := struct {
Loadbalancers []SLoadbalancer
LoadbalancersLinks SNextLinks
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, errors.Wrap(err, "resp.Unmarshal")
}
loadbalancers = append(loadbalancers, part.Loadbalancers...)
marker := part.LoadbalancersLinks.GetNextMark()
if len(marker) == 0 {
break
}
query.Set("marker", marker)
}
for i := 0; i < len(loadbalancers); i++ {
loadbalancers[i].region = region
}
return loadbalancers, nil
}
func (region *SRegion) GetLoadbalancerbyId(loadbalancerId string) (*SLoadbalancer, error) {
// region.client.Debug(true)
body, err := region.lbGet(fmt.Sprintf("/v2/lbaas/loadbalancers/%s", loadbalancerId))
if err != nil {
return nil, errors.Wrapf(err, `region.lbGet(/v2/lbaas/loadbalancers/%s)`, loadbalancerId)
}
loadbalancer := SLoadbalancer{}
err = body.Unmarshal(&loadbalancer, "loadbalancer")
if err != nil {
return nil, errors.Wrap(err, "resp.Unmarshal(loadbalancer)")
}
loadbalancer.region = region
return &loadbalancer, nil
}
func (region *SRegion) CreateLoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (*SLoadbalancer, error) {
type CreateParams struct {
Loadbalancer SLoadbalancerCreateParams `json:"loadbalancer"`
}
params := CreateParams{}
params.Loadbalancer.AdminStateUp = true
params.Loadbalancer.AvailabilityZone = loadbalancer.ZoneID
params.Loadbalancer.Name = loadbalancer.Name
params.Loadbalancer.ProjectID = loadbalancer.ProjectId
params.Loadbalancer.VipSubnetID = loadbalancer.NetworkIDs[0]
params.Loadbalancer.VipAddress = loadbalancer.Address
body, err := region.lbPost("/v2/lbaas/loadbalancers", jsonutils.Marshal(params))
if err != nil {
return nil, errors.Wrap(err, `region.lbPost("/v2/lbaas/loadbalancers", jsonutils.Marshal(params))`)
}
sloadbalancer := SLoadbalancer{}
err = body.Unmarshal(&sloadbalancer, "loadbalancer")
if err != nil {
return nil, errors.Wrap(err, "body.Unmarshal(sloadbalancer, loadbalancer)")
}
sloadbalancer.region = region
if len(loadbalancer.EipID) > 0 {
err = region.AssociateEipWithPortId(sloadbalancer.VipPortID, loadbalancer.EipID)
if err != nil {
return nil, errors.Wrapf(err, "region.AssociateEipWithPortId(%s, %s)", sloadbalancer.VipPortID, loadbalancer.EipID)
}
}
return &sloadbalancer, nil
}
func (region *SRegion) DeleteLoadbalancer(loadbalancerId string) error {
_, err := region.lbDelete(fmt.Sprintf("/v2/lbaas/loadbalancers/%s?cascade=True", loadbalancerId))
if err != nil {
return errors.Wrapf(err, `region.lbDelete(/v2/lbaas/loadbalancers/%s?cascade=True)`, loadbalancerId)
}
return nil
}
func (lb *SLoadbalancer) Delete(ctx context.Context) error {
return lb.region.DeleteLoadbalancer(lb.ID)
}
func (lb *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
ibackendgroups := []cloudprovider.ICloudLoadbalancerBackendGroup{}
for i := 0; i < len(lb.PoolIds); i++ {
pool, err := lb.region.GetLoadbalancerPoolById(lb.PoolIds[i].ID)
if err != nil {
return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerPoolById(%s)", lb.PoolIds[i].ID)
}
ibackendgroups = append(ibackendgroups, pool)
}
return ibackendgroups, nil
}
func (lb *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
// ensure lb status
err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
if err != nil {
return nil, errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
}
// create pool
spool, err := lb.region.CreateLoadbalancerPool(group)
if err != nil {
return nil, errors.Wrap(err, "lb.region.CreateLoadbalancerPool")
}
// wait spool
err = waitLbResStatus(spool, 10*time.Second, 8*time.Minute)
if err != nil {
return nil, errors.Wrap(err, "waitLbResStatus(spool, 10*time.Second, 8*time.Minute)")
}
// create healthmonitor
if group.HealthCheck != nil {
healthmonitor, err := lb.region.CreateLoadbalancerHealthmonitor(spool.ID, group.HealthCheck)
if err != nil {
return nil, errors.Wrapf(err, "region.CreateLoadbalancerHealthmonitor(%s, group.HealthCheck)", spool.ID)
}
spool.healthmonitor = healthmonitor
}
// wait health monitor
if spool.healthmonitor != nil {
err = waitLbResStatus(spool.healthmonitor, 10*time.Second, 8*time.Minute)
if err != nil {
return nil, errors.Wrap(err, "waitLbResStatus(spool.healthmonitor, 10*time.Second, 8*time.Minute)")
}
}
return spool, nil
}
func (lb *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) {
// ensure lb status
err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
if err != nil {
return nil, errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
}
slistener, err := lb.region.CreateLoadbalancerListener(lb.ID, listener)
if err != nil {
return nil, errors.Wrapf(err, "lb.region.CreateLoadbalancerListener(%s, listener)", lb.ID)
}
return slistener, nil
}
func (lb *SLoadbalancer) GetLoadbalancerSpec() string {
return lb.Description
}
func (lb *SLoadbalancer) GetChargeType() string {
eip, err := lb.GetIEIP()
if err != nil {
log.Errorf("lb.GetIEIP(): %s", err)
}
if err != nil {
return eip.GetInternetChargeType()
}
return api.EIP_CHARGE_TYPE_BY_TRAFFIC
}
func (lb *SLoadbalancer) GetEgressMbps() int {
return 0
}
func (lb *SLoadbalancer) GetILoadBalancerBackendGroupById(poolId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
err := lb.Refresh()
if err != nil {
return nil, errors.Wrap(err, "lb.Refresh()")
}
index := -1
for i := 0; i < len(lb.PoolIds); i++ {
if poolId == lb.PoolIds[i].ID {
index = i
}
}
if index < 0 {
return nil, cloudprovider.ErrNotFound
}
spool, err := lb.region.GetLoadbalancerPoolById(poolId)
if err != nil {
return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerPoolById(%s)", poolId)
}
if spool.GetStatus() == api.LB_STATUS_DELETING {
return nil, cloudprovider.ErrNotFound
}
return spool, nil
}
func (lb *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) {
eips, err := lb.region.GetEips("")
if err != nil {
return nil, errors.Wrapf(err, "lb.region.GetEips()")
}
for _, eip := range eips {
if eip.PortId == lb.VipPortID {
return &eip, nil
}
}
return nil, nil
}
func (region *SRegion) UpdateLoadBalancerAdminStateUp(AdminStateUp bool, loadbalancerId string) error {
params := jsonutils.NewDict()
poolParam := jsonutils.NewDict()
poolParam.Add(jsonutils.NewBool(AdminStateUp), "admin_state_up")
params.Add(poolParam, "loadbalancer")
_, err := region.lbUpdate(fmt.Sprintf("/v2/lbaas/loadbalancers/%s", loadbalancerId), params)
if err != nil {
return errors.Wrapf(err, `region.lbUpdate(/v2/lbaas/loadbalancers/%s), params)`, loadbalancerId)
}
return nil
}
func (lb *SLoadbalancer) Start() error {
// ensure lb status
err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
if err != nil {
return errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
}
err = lb.region.UpdateLoadBalancerAdminStateUp(true, lb.ID)
if err != nil {
return errors.Wrapf(err, "lb.region.UpdateLoadBalancerAdminStateUp(true, %s)", lb.ID)
}
err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
if err != nil {
return errors.Wrap(err, "waitLbResStatus(lb, 10*time.Second, 8*time.Minute)")
}
return nil
}
func (lb *SLoadbalancer) Stop() error {
// ensure lb status
err := waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
if err != nil {
return errors.Wrap(err, "waitLbResStatus(lb, api.LB_STATUS_ENABLED, 10*time.Second, 8*time.Minute)")
}
err = lb.region.UpdateLoadBalancerAdminStateUp(false, lb.ID)
if err != nil {
return errors.Wrapf(err, "lb.region.UpdateLoadBalancerAdminStateUp(false,%s)", lb.ID)
}
err = waitLbResStatus(lb, 10*time.Second, 8*time.Minute)
if err != nil {
return errors.Wrap(err, "waitLbResStatus(lb, 10*time.Second, 8*time.Minute)")
}
return nil
}
func (lb *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
return lb.region.GetLoadbalancerListenerbyId(listenerId)
}
func (lb *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
ilisteners := []cloudprovider.ICloudLoadbalancerListener{}
for i := 0; i < len(lb.ListenerIds); i++ {
listener, err := lb.region.GetLoadbalancerListenerbyId(lb.ListenerIds[i].ID)
if err != nil {
return nil, errors.Wrapf(err, "lb.region.GetLoadbalancerListenerbyId(%s)", lb.ListenerIds[i].ID)
}
ilisteners = append(ilisteners, listener)
}
return ilisteners, nil
}
func (lb *SLoadbalancer) GetProjectId() string {
return lb.ProjectID
}
func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
return cloudprovider.ErrNotSupported
}