Source code for aliyun.slb.connection

# -*- coding:utf-8 -*-
# Copyright 2014, Quixey Inc.
#
# 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.

import collections
import logging

from aliyun import connection
from aliyun.slb.model import (
    BackendServer,
    BackendServerStatus,
    HTTPListener,
    LoadBalancer,
    LoadBalancerStatus,
    ListenerStatus,
    Region,
    TCPListener
)

logger = logging.getLogger(__name__)

[docs]class Error(Exception): """Base Exception class for this module."""
[docs]class SlbConnection(connection.Connection): """A connection to Aliyun SLB service.""" def __init__(self, region_id, access_key_id=None, secret_access_key=None): """Constructor. If the access and secret key are not provided the credentials are looked for in $HOME/.aliyun.cfg or /etc/aliyun.cfg. Args: region_id (str): The id of the region to connect to. access_key_id (str): The access key id. secret_access_key (str): The secret access key. """ super(SlbConnection, self).__init__( region_id, 'slb', access_key_id=access_key_id, secret_access_key=secret_access_key)
[docs] def get_all_regions(self): """Get all regions. Return: list[slb.Region] """ resp = self.get({'Action': 'DescribeRegions'}) regions = [] for region in resp['Regions']['Region']: regions.append(Region(region['RegionId'])) return regions
[docs] def get_all_region_ids(self): return [r.region_id for r in self.get_all_regions()]
[docs] def get_all_load_balancer_status(self, instance_id=None): """Get all LoadBalancerStatus in the region. Args: instance_id (str, optional): Restrict results to LBs with this instance. Return: List of LoadBalancerStatus. """ lb_status = [] params = {'Action': 'DescribeLoadBalancers'} if instance_id: params['ServerId'] = instance_id resp = self.get(params) for lb in resp['LoadBalancers']['LoadBalancer']: new_lb_status = LoadBalancerStatus(lb['LoadBalancerId'], lb['LoadBalancerName'], lb['LoadBalancerStatus']) lb_status.append(new_lb_status) return lb_status
[docs] def get_all_load_balancer_ids(self): """Get all the load balancer IDs in the region.""" return ( [x.load_balancer_id for x in self.get_all_load_balancer_status()] )
[docs] def delete_load_balancer(self, load_balancer_id): """Delete a LoadBalancer by ID Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to delete. """ params = { 'Action': 'DeleteLoadBalancer', 'LoadBalancerId': load_balancer_id } return self.get(params)
[docs] def get_load_balancer(self, load_balancer_id): """Get a LoadBalancer by ID. Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to retrieve. Returns: LoadBalancer with given ID. """ resp = self.get({ 'Action': 'DescribeLoadBalancerAttribute', 'LoadBalancerId': load_balancer_id }) backend_servers = [] for bs in resp['BackendServers']['BackendServer']: backend_servers.append(BackendServer(bs['ServerId'], bs['Weight'])) return LoadBalancer(resp['LoadBalancerId'], resp['RegionId'], resp['LoadBalancerName'], resp['LoadBalancerStatus'], resp['Address'], resp['AddressType'], [port for port in resp['ListenerPorts'] ['ListenerPort']], backend_servers)
[docs] def create_load_balancer(self, region_id, address_type=None, internet_charge_type=None, bandwidth=None, load_balancer_name=None): """Create a load balancer. This does not configure listeners nor backend servers. Args: region_id (str): An id from get_all_region_ids() addres_type (str): IP the SLB on the public network ('internet', default) or the private network ('intranet') internet_charge_type (str): 'paybytraffic' (default) vs 'paybybandwidth' bandwidth (int): peak burst speed of 'paybybandwidth' type slbs. Listener must be set first before this will take effect. default: 1 (unit Mbps) load_balancer_name (str): Name of the SLB. 80 char max. Optional. Returns: load_balancer_id of the created LB. Address and Name are not given. """ params = { 'Action': 'CreateLoadBalancer', 'RegionId': region_id } if load_balancer_name is not None: params['LoadBalancerName'] = load_balancer_name if address_type is not None: params['AddressType'] = address_type.lower() if internet_charge_type is not None: params['InternetChargeType'] = internet_charge_type.lower() if bandwidth is not None: params['Bandwidth'] = bandwidth resp = self.get(params) logger.debug("Created a load balancer: %(LoadBalancerId)s named %(LoadBalancerName)s at %(Address)s", resp) return resp['LoadBalancerId']
[docs] def set_load_balancer_status(self, load_balancer_id, status): """Set the Status of an SLB Args: load_balancer_id (str): SLB ID status (str): One of 'inactive' or 'active' """ params = { 'Action': 'SetLoadBalancerStatus', 'LoadBalancerId': load_balancer_id, 'LoadBalancerStatus': status } return self.get(params)
[docs] def set_load_balancer_name(self, load_balancer_id, name): """Set the Name of an SLB Args: load_balancer_id (str): SLB ID name (str): Alias for the SLB. Up to 64 characters. """ params = { 'Action': 'SetLoadBalancerName', 'LoadBalancerId': load_balancer_id, 'LoadBalancerName': name } return self.get(params)
[docs] def delete_listener(self, load_balancer_id, listener_port): """Delete the SLB Listner on specified port Args: load_balancer_id (str): SLB ID listener_port (int): SLB Listener port. Between 1 and 65535. """ params = { 'Action': 'DeleteLoadBalancerListener', 'LoadBalancerId': load_balancer_id, 'ListenerPort': listener_port } return self.get(params)
[docs] def set_listener_status(self, load_balancer_id, listener_port, status): """Set the status of an SLB Listener. Turn them on or off. Args: load_balancer_id (str): SLB ID listener_port (int): SLB Listener port. Between 1 and 65535. status (str): 'inactive' for off and 'active' for on. """ params = { 'Action': 'SetLoadBalancerListenerStatus', 'LoadBalancerId': load_balancer_id, 'ListenerPort': listener_port, 'ListenerStatus': status } return self.get(params)
[docs] def get_tcp_listener(self, load_balancer_id, listener_port): """Get the TCP Listener from an SLB ID and port Args: load_balancer_id (str): SLB ID listener_port (int): SLB Listener port. Between 1 and 65535. Returns: TCPListener """ params = { 'Action': 'DescribeLoadBalancerTCPListenerAttribute', 'LoadBalancerId': load_balancer_id, 'ListenerPort': listener_port } resp = self.get(params) if 'ConnectPort' not in resp: resp['ConnectPort'] = resp['BackendServerPort'] return TCPListener(load_balancer_id, int(resp['ListenerPort']), int(resp['BackendServerPort']), listener_status=resp['Status'], scheduler=resp['Scheduler'] or None, health_check=resp['HealthCheck'] == 'on', connect_port=int(resp['ConnectPort']) or None, persistence_timeout=int(resp['PersistenceTimeout']))
[docs] def get_http_listener(self, load_balancer_id, listener_port): """Get the HTTP Listener from an SLB ID and port Args: load_balancer_id (str): SLB ID listener_port (int): SLB Listener port. Between 1 and 65535. Returns: HTTPListener """ params = { 'Action': 'DescribeLoadBalancerHTTPListenerAttribute', 'LoadBalancerId': load_balancer_id, 'ListenerPort': listener_port } resp = self.get(params) return HTTPListener(load_balancer_id, int(resp['ListenerPort']), int(resp['BackendServerPort']), listener_status=resp['Status'] or None, scheduler=resp['Scheduler'] or None, health_check=resp['HealthCheck'] == 'on', x_forwarded_for=resp['XForwardedFor'] == 'on', sticky_session=resp['StickySession'] == 'on', sticky_session_type=resp['StickySessionapiType'] or None, cookie=resp['Cookie'] or None, domain=resp['Domain'] or None, uri=resp['URI'])
[docs] def create_tcp_listener(self, load_balancer_id, listener_port, backend_server_port, healthy_threshold=3, unhealthy_threshold=3, listener_status=None, scheduler=None, health_check=None, connect_timeout=None, interval=None, connect_port=None, persistence_timeout=None): """Create a TCP SLB Listener Args: load_balancer_id (str): LoadBalancerId unique identifier of the SLB. listener_port (int): Port for the SLB to listen on backend_server_port (int): Port to send traffic to on the back-end healthy_threshold (int): Number of successful healthchecks before considering the listener healthy. Default 3. unhealthy_threshold (int): Number of failed healthchecks before considering the listener unhealthy. Default 3. TCPListener arguments: listener_status (str): 'active' (default) or 'stopped'. scheduler (str): wrr or wlc. Round Robin (default) or Least Connections. health_check (bool): True for 'on' and False for 'off' (default) connect_timeout (int): number of seconds to timeout and fail a health check interval (int): number of seconds between health checks connect_port (int): defaults to backend_server_port persistence_timeout (int): number of seconds to hold TCP connection open """ params = {'Action': 'CreateLoadBalancerTCPListener', 'LoadBalancerId': load_balancer_id, 'ListenerPort': int(listener_port), 'BackendServerPort': int(backend_server_port), } if healthy_threshold is not None: params['HealthyThreshold'] = healthy_threshold if unhealthy_threshold is not None: params['UnhealthyThreshold'] = unhealthy_threshold if listener_status: params['ListenerStatus'] = listener_status if scheduler: params['Scheduler'] = scheduler if health_check is not None: params['HealthCheck'] = 'on' if health_check else 'off' if connect_timeout is not None: params['ConnectTimeout'] = connect_timeout if interval is not None: params['Interval'] = interval if connect_port is not None: params['ConnectPort'] = connect_port if persistence_timeout is not None: params['PersistenceTimeout'] = int(persistence_timeout) self.get(params)
[docs] def create_http_listener(self, load_balancer_id, listener_port, backend_server_port, bandwidth, sticky_session, health_check, healthy_threshold=3, unhealthy_threshold=3, scheduler=None, connect_timeout=None, interval=None, x_forwarded_for=None, sticky_session_type=None, cookie_timeout=None, cookie=None, domain=None, uri=None): """Create an HTTP SLB Listener Args: load_balancer_id (str): LoadBalancerId unique identifier of the SLB. listener_port (int): Port for the SLB to listen on backend_server_port (int): Port to send traffic to on the back-end bandwidth (int): The peak burst speed of the network. Optional values: - 1|1 - 1000Mbps For the paybybandwidth intances, the peak burst speed of all Listeners should not exceed the Bandwidth value in SLB instance creation, and the Bandwidth value must not be set to - 1. For paybytraffic instances, this value can be set to - 1, meaning there is no restriction on bandwidth peak speed. sticky_session (str): on or off healthy_threshold (int): Number of successful healthchecks before considering the listener healthy. Default 3. unhealthy_threshold (int): Number of failed healthchecks before considering the listener unhealthy. Default 3. health_check (str): 'on' and 'off' (default) HTTPListener arguments: scheduler (str): wrr or wlc. Round Robin (default) or Least Connections. connect_timeout (int): number of seconds to timeout and fail a health check interval (int): number of seconds between health checks x_forwarded_for (bool): wether or not to append ips to x-fordwarded-for http header sticky_session_type (str): 'insert' to have the SLB add a cookie to requests 'server' to have the SLB look for a server-injected cookie sticky_session must be 'on' cookie_timeout (int [0-86400]): Lifetime of cookie in seconds. Max 1 day. sticky_session must be 'on' cookie (str): The Cookie key to use as sticky_session indicator. sticky_session_type must be 'server' domain (str): the Host header to use for the health check uri (str): URL path for healthcheck. E.g. /health """ params = {'Action': 'CreateLoadBalancerHTTPListener', 'LoadBalancerId': load_balancer_id, 'ListenerPort': int(listener_port), 'BackendServerPort': int(backend_server_port), 'Bandwidth': int(bandwidth), 'StickySession': sticky_session, 'HealthCheck': health_check, } if healthy_threshold is not None: params['HealthyThreshold'] = healthy_threshold if unhealthy_threshold is not None: params['UnhealthyThreshold'] = unhealthy_threshold if scheduler: params['Scheduler'] = scheduler if connect_timeout is not None: params['ConnectTimeout'] = connect_timeout if interval is not None: params['Interval'] = interval if x_forwarded_for is not None: params['XForwardedFor'] = 'on' if x_forwarded_for else 'off' if sticky_session_type is not None: params['StickySessionapiType'] = sticky_session_type if cookie_timeout is not None: params['CookieTimeout'] = cookie_timeout if cookie is not None: params['Cookie'] = cookie if domain is not None: params['Domain'] = domain if uri is not None: params['URI'] = uri self.get(params)
[docs] def update_tcp_listener(self, load_balancer_id, listener_port, healthy_threshold=None, unhealthy_threshold=None, scheduler=None, health_check=None, connect_timeout=None, interval=None, connect_port=None, persistence_timeout=None): """Update an existing TCP SLB Listener Args: load_balancer_id (str): LoadBalancerId unique identifier of the SLB. listener_port (int): Port for the SLB to listen on healthy_threshold (int): Number of successful healthchecks before considering the listener healthy. Default 3. unhealthy_threshold (int): Number of failed healthchecks before considering the listener unhealthy. Default 3. scheduler (str): wrr or wlc. Round Robin (default) or Least Connections. health_check (bool): True for 'on' and False for 'off' (default) connect_timeout (int): number of seconds to timeout and fail a health check interval (int): number of seconds between health checks connect_port (int): defaults to backend_server_port persistence_timeout (int): number of seconds to hold TCP connection open """ params = {'Action': 'SetLoadBalancerTCPListenerAttribute', 'LoadBalancerId': load_balancer_id, 'ListenerPort': listener_port} if healthy_threshold is not None: params['HealthyThreshold'] = healthy_threshold if unhealthy_threshold is not None: params['UnhealthyThreshold'] = unhealthy_threshold if scheduler is not None: params['Scheduler'] = scheduler if health_check is not None: params['HealthCheck'] = 'on' if health_check else 'off' if connect_timeout is not None: params['ConnectTimeout'] = connect_timeout if interval is not None: params['Interval'] = interval if connect_port is not None: params['ConnectPort'] = connect_port if persistence_timeout is not None: params['PersistenceTimeout'] = persistence_timeout self.get(params)
[docs] def update_http_listener(self, load_balancer_id, listener_port, healthy_threshold=None, unhealthy_threshold=None, scheduler=None, health_check=None, health_check_timeout=None, interval=None, x_forwarded_for=None, sticky_session=None, sticky_session_type=None, cookie_timeout=None, cookie=None, domain=None, uri=None): """Update an existing HTTP SLB Listener Args: load_balancer_id (str): LoadBalancerId unique identifier of the SLB. listener_port (int): Port for the SLB to listen on healthy_threshold (int): Number of successful healthchecks before considering the listener healthy. Default 3. unhealthy_threshold (int): Number of failed healthchecks before considering the listener unhealthy. Default 3. scheduler (str): wrr or wlc. Round Robin (default) or Least Connections. health_check (bool): True for 'on' and False for 'off' (default) health_check_timeout (int): number of seconds to timeout and fail a health check interval (int): number of seconds between health checks x_forwarded_for (bool): wether or not to append ips to x-fordwarded-for http header sticky_session (bool): use slb sticky sessions. default false. sticky_session_type (str): 'insert' to have the SLB add a cookie to requests 'server' to have the SLB look for a server-injected cookie sticky_session must be 'on' cookie_timeout (int [0-86400]): Lifetime of cookie in seconds. Max 1 day. sticky_session must be 'on' cookie (str): The Cookie key to use as sticky_session indicator. sticky_session_type must be 'server' domain (str): the Host header to use for the health check uri (str): URL path for healthcheck. E.g. /health """ params = {'Action': 'SetLoadBalancerHTTPListenerAttribute', 'LoadBalancerId': load_balancer_id, 'ListenerPort': int(listener_port), } if healthy_threshold is not None: params['HealthyThreshold'] = healthy_threshold if unhealthy_threshold is not None: params['UnhealthyThreshold'] = unhealthy_threshold if scheduler: params['Scheduler'] = scheduler if health_check is not None: params['HealthCheck'] = 'on' if health_check else 'off' if health_check_timeout is not None: params['HealthCheckTimeout'] = health_check_timeout if interval is not None: params['Interval'] = interval if x_forwarded_for is not None: params['XForwardedFor'] = 'on' if x_forwarded_for else 'off' if sticky_session is not None: params['StickySession'] = 'on' if sticky_session else 'off' if sticky_session_type is not None: params['StickySessionapiType'] = sticky_session_type if cookie_timeout is not None: params['CookieTimeout'] = cookie_timeout if cookie is not None: params['Cookie'] = cookie if domain is not None: params['Domain'] = domain if uri is not None: params['URI'] = uri self.get(params)
[docs] def start_load_balancer_listener(self, load_balancer_id, listener_port): """Start a listener Args: load_balancer_id (str): Aliyun SLB LoadBalancerId listener_port (int): The listener port to activate """ params = { 'Action': 'StartLoadBalancerListener', 'LoadBalancerId': str(load_balancer_id), 'ListenerPort': int(listener_port) } self.get(params)
[docs] def stop_load_balancer_listener(self, load_balancer_id, listener_port): """Stop a listener Args: load_balancer_id (str): Aliyun SLB LoadBalancerId listener_port (int): The listener port to activate """ params = { 'Action': 'StopLoadBalancerListener', 'LoadBalancerId': str(load_balancer_id), 'ListenerPort': int(listener_port) } self.get(params)
[docs] def get_backend_servers(self, load_balancer_id, listener_port=None): """Get backend servers for a given load balancer and its listener port. If listener_port is not specified, all listeners are listed separately. Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to retrieve. listener_port (int, optional): the port to get backend server statuses for Returns: List of ListenerStatus """ params = { 'Action': 'DescribeBackendServers', 'LoadBalancerId': load_balancer_id, } if listener_port is not None: params['ListenerPort'] = listener_port listeners = [] resp = self.get(params) for listener in resp['Listeners']['Listener']: backends = [] for bs in listener['BackendServers']['BackendServer']: backends.append( BackendServerStatus(bs['ServerId'], bs['ServerHealthStatus'])) listeners.append( ListenerStatus(listener['ListenerPort'], backends)) return listeners
[docs] def get_backend_server_ids(self, load_balancer_id, listener_port=None): backends = [] statuses = self.get_backend_servers(load_balancer_id, listener_port) for status in statuses: backends.extend([bs.server_id for bs in status.backend_servers]) return list(set(backends))
[docs] def remove_backend_servers(self, load_balancer_id, backend_servers): """Remove backend servers from a load balancer Note: the SLB API ignores Weight when Removing Backend Servers. So you're probably better off using remove_backend_server_id anyway. Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to retrieve. backend_servers (list of BackendServer): the backend servers to remove """ params = { 'Action': 'RemoveBackendServers', 'LoadBalancerId': load_balancer_id } backends = [] for bs in backend_servers: backends.append({'ServerId': bs.instance_id}) params['BackendServers'] = backends return self.get(params)
[docs] def remove_backend_server_ids(self, load_balancer_id, backend_server_ids): """Helper wrapper to remove backend server IDs specified from the SLB specified. Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to retrieve. backend_server_ids (list of str): the backend server ids to remove """ backends = [BackendServer(bsid, None) for bsid in backend_server_ids] return self.remove_backend_servers(load_balancer_id, backends)
[docs] def add_backend_servers(self, load_balancer_id, backend_servers): """Add backend servers to a load balancer Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to retrieve. backend_servers (list of BackendServer): the backend servers to add """ params = { 'Action': 'AddBackendServers', 'LoadBalancerId': load_balancer_id } backends = [] for bs in backend_servers: if bs.weight is not None: backends.append( {'ServerId': bs.instance_id, 'Weight': bs.weight}) else: backends.append({'ServerId': bs.instance_id}) params['BackendServers'] = backends return self.get(params)
[docs] def add_backend_server_ids(self, load_balancer_id, backend_server_ids): """Helper wrapper to add backend server IDs specified to the SLB specified. Args: load_balancer_id (str): Aliyun SLB LoadBalancerId to retrieve. backend_server_ids (list of str): the backend server ids to add """ backends = [BackendServer(bsid, None) for bsid in backend_server_ids] return self.add_backend_servers(load_balancer_id, backends)
[docs] def deregister_backend_server_ids(self, server_ids): """Helper wrapper to get load balancers with the server id in them and remove the server from each load balancer. Args: server_id (List of str): List of Aliyun ECS Instance IDs Returns: List of SLB IDs that were modified. """ lbs = collections.defaultdict(list) for instance_id in server_ids: for lb_status in self.get_all_load_balancer_status(instance_id): lbs[lb_status.load_balancer_id].append(instance_id) for lb_id, bs_ids in lbs.iteritems(): self.remove_backend_server_ids(lb_id, list(set(bs_ids))) return lbs.keys()
[docs] def deregister_backend_servers(self, backend_servers): return ( self.deregister_backend_server_ids( [bs.instance_id for bs in backend_servers]) )