blob: 8e0bd8dda9c035ad994541570c81b63cdc60c2b8 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
__all__ = ["ElasticLBDriver"]
from libcloud.utils.py3 import httplib
from libcloud.utils.xml import findtext, findall
from libcloud.loadbalancer.types import State
from libcloud.loadbalancer.base import Driver, LoadBalancer, Member
from libcloud.common.aws import AWSGenericResponse, SignedAWSConnection
VERSION = "2012-06-01"
HOST = "elasticloadbalancing.%s.amazonaws.com"
ROOT = "/%s/" % (VERSION)
NS = "http://elasticloadbalancing.amazonaws.com/doc/%s/" % (VERSION,)
class ELBResponse(AWSGenericResponse):
"""
Amazon ELB response class.
"""
namespace = NS
exceptions = {}
xpath = "Error"
class ELBConnection(SignedAWSConnection):
version = VERSION
host = HOST
responseCls = ELBResponse
service_name = "elasticloadbalancing"
class ElasticLBDriver(Driver):
name = "Amazon Elastic Load Balancing"
website = "http://aws.amazon.com/elasticloadbalancing/"
connectionCls = ELBConnection
signature_version = "4"
def __init__(self, access_id, secret, region, token=None):
self.token = token
self.region = region
self.region_name = region
super(ElasticLBDriver, self).__init__(
access_id, secret, token=token, host=HOST % region, region=region
)
def list_protocols(self):
return ["tcp", "ssl", "http", "https"]
def list_balancers(self, ex_fetch_tags=False):
params = {"Action": "DescribeLoadBalancers"}
data = self.connection.request(ROOT, params=params).object
balancers = self._to_balancers(data)
if ex_fetch_tags:
for balancer in balancers:
self._ex_populate_balancer_tags(balancer)
return balancers
def create_balancer(
self,
name,
port,
protocol,
algorithm,
members,
ex_members_availability_zones=None,
):
if ex_members_availability_zones is None:
ex_members_availability_zones = ["a"]
params = {
"Action": "CreateLoadBalancer",
"LoadBalancerName": name,
"Listeners.member.1.InstancePort": str(port),
"Listeners.member.1.InstanceProtocol": protocol.upper(),
"Listeners.member.1.LoadBalancerPort": str(port),
"Listeners.member.1.Protocol": protocol.upper(),
}
for i, z in enumerate(ex_members_availability_zones):
zone = "".join((self.region, z))
params["AvailabilityZones.member.%d" % (i + 1)] = zone
data = self.connection.request(ROOT, params=params).object
balancer = LoadBalancer(
id=name,
name=name,
state=State.PENDING,
ip=findtext(element=data, xpath="DNSName", namespace=NS),
port=port,
driver=self.connection.driver,
)
balancer._members = []
return balancer
def destroy_balancer(self, balancer):
params = {"Action": "DeleteLoadBalancer", "LoadBalancerName": balancer.id}
self.connection.request(ROOT, params=params)
return True
def get_balancer(self, balancer_id, ex_fetch_tags=False):
params = {
"Action": "DescribeLoadBalancers",
"LoadBalancerNames.member.1": balancer_id,
}
data = self.connection.request(ROOT, params=params).object
balancer = self._to_balancers(data)[0]
if ex_fetch_tags:
balancer = self._ex_populate_balancer_tags(balancer)
return balancer
def balancer_attach_compute_node(self, balancer, node):
params = {
"Action": "RegisterInstancesWithLoadBalancer",
"LoadBalancerName": balancer.id,
"Instances.member.1.InstanceId": node.id,
}
self.connection.request(ROOT, params=params)
balancer._members.append(Member(node.id, None, None, balancer=self))
def balancer_detach_member(self, balancer, member):
params = {
"Action": "DeregisterInstancesFromLoadBalancer",
"LoadBalancerName": balancer.id,
"Instances.member.1.InstanceId": member.id,
}
self.connection.request(ROOT, params=params)
balancer._members = [m for m in balancer._members if m.id != member.id]
return True
def balancer_list_members(self, balancer):
return balancer._members
def ex_list_balancer_policies(self, balancer):
"""
Return a list of policy description string.
:rtype: ``list`` of ``str``
"""
params = {
"Action": "DescribeLoadBalancerPolicies",
"LoadBalancerName": balancer.id,
}
data = self.connection.request(ROOT, params=params).object
return self._to_policies(data)
def ex_list_balancer_policy_types(self):
"""
Return a list of policy type description string.
:rtype: ``list`` of ``str``
"""
params = {"Action": "DescribeLoadBalancerPolicyTypes"}
data = self.connection.request(ROOT, params=params).object
return self._to_policy_types(data)
def ex_create_balancer_policy(
self, name, policy_name, policy_type, policy_attributes=None
):
"""
Create a new load balancer policy
:param name: Balancer name to create the policy for
:type name: ``str``
:param policy_name: policy to be created
:type policy_name: ``str``
:param policy_type: policy type being used to create policy.
:type policy_type: ``str``
:param policy_attributes: Each list contain values, ['AttributeName',
'value']
:type policy_attributes: ``PolicyAttribute list``
"""
params = {
"Action": "CreateLoadBalancerPolicy",
"LoadBalancerName": name,
"PolicyName": policy_name,
"PolicyTypeName": policy_type,
}
if policy_attributes is not None:
for index, (name, value) in enumerate(policy_attributes.iteritems(), 1):
params[
"PolicyAttributes.member.%d. \
AttributeName"
% (index)
] = name
params[
"PolicyAttributes.member.%d. \
AttributeValue"
% (index)
] = value
response = self.connection.request(ROOT, params=params)
return response.status == httplib.OK
def ex_delete_balancer_policy(self, name, policy_name):
"""
Delete a load balancer policy
:param name: balancer name for which policy will be deleted
:type name: ``str``
:param policy_name: The Mnemonic name for the policy being deleted
:type policy_name: ``str``
"""
params = {
"Action": "DeleteLoadBalancerPolicy",
"LoadBalancerName": name,
"PolicyName": policy_name,
}
response = self.connection.request(ROOT, params=params)
return response.status == httplib.OK
def ex_set_balancer_policies_listener(self, name, port, policies):
"""
Associates, updates, or disables a policy with a listener on
the load balancer
:param name: balancer name to set policies for listerner
:type name: ``str``
:param port: port to use
:type port: ``str``
:param policies: List of policies to be associated with the balancer
:type policies: ``string list``
"""
params = {
"Action": "SetLoadBalancerPoliciesOfListener",
"LoadBalancerName": name,
"LoadBalancerPort": str(port),
}
if policies:
params = self._create_list_params(params, policies, "PolicyNames.member.%d")
response = self.connection.request(ROOT, params=params)
return response.status == httplib.OK
def ex_set_balancer_policies_backend_server(self, name, instance_port, policies):
"""
Replaces the current set of policies associated with a port on
which the back-end server is listening with a new set of policies
:param name: balancer name to set policies of backend server
:type name: ``str``
:param instance_port: Instance Port
:type instance_port: ``int``
:param policies: List of policies to be associated with the balancer
:type policies: ``string list`
"""
params = {
"Action": "SetLoadBalancerPoliciesForBackendServer",
"LoadBalancerName": name,
"InstancePort": str(instance_port),
}
if policies:
params = self._create_list_params(params, policies, "PolicyNames.member.%d")
response = self.connection.request(ROOT, params=params)
return response.status == httplib.OK
def ex_create_balancer_listeners(self, name, listeners=None):
"""
Creates one or more listeners on a load balancer for the specified port
:param name: The mnemonic name associated with the load balancer
:type name: ``str``
:param listeners: Each tuple contain values, (LoadBalancerPortNumber,
InstancePortNumber, Protocol,[SSLCertificateId])
:type listeners: ``list of tuple`
"""
params = {"Action": "CreateLoadBalancerListeners", "LoadBalancerName": name}
for index, listener in enumerate(listeners):
i = index + 1
protocol = listener[2].upper()
params["Listeners.member.%d.LoadBalancerPort" % i] = listener[0]
params["Listeners.member.%d.InstancePort" % i] = listener[1]
params["Listeners.member.%d.Protocol" % i] = listener[2]
if protocol == "HTTPS" or protocol == "SSL":
params[
"Listeners.member.%d. \
SSLCertificateId"
% i
] = listener[3]
else:
return False
response = self.connection.request(ROOT, params=params)
return response.status == httplib.OK
def _to_policies(self, data):
xpath = "DescribeLoadBalancerPoliciesResult/PolicyDescriptions/member"
return [
findtext(element=el, xpath="PolicyName", namespace=NS)
for el in findall(element=data, xpath=xpath, namespace=NS)
]
def _to_policy_types(self, data):
xpath = "DescribeLoadBalancerPolicyTypesResult/"
xpath += "PolicyTypeDescriptions/member"
return [
findtext(element=el, xpath="PolicyTypeName", namespace=NS)
for el in findall(element=data, xpath=xpath, namespace=NS)
]
def _to_balancers(self, data):
xpath = "DescribeLoadBalancersResult/LoadBalancerDescriptions/member"
return [
self._to_balancer(el)
for el in findall(element=data, xpath=xpath, namespace=NS)
]
def _to_balancer(self, el):
name = findtext(element=el, xpath="LoadBalancerName", namespace=NS)
dns_name = findtext(el, xpath="DNSName", namespace=NS)
port = findtext(el, xpath="LoadBalancerPort", namespace=NS)
balancer = LoadBalancer(
id=name,
name=name,
state=State.UNKNOWN,
ip=dns_name,
port=port,
driver=self.connection.driver,
)
xpath = "Instances/member/InstanceId"
members = findall(element=el, xpath=xpath, namespace=NS)
balancer._members = []
for m in members:
balancer._members.append(Member(m.text, None, None, balancer=balancer))
return balancer
def _to_tags(self, data):
"""
return tags dict
"""
tags = {}
xpath = "DescribeTagsResult/TagDescriptions/member/Tags/member"
for el in findall(element=data, xpath=xpath, namespace=NS):
key = findtext(element=el, xpath="Key", namespace=NS)
value = findtext(element=el, xpath="Value", namespace=NS)
if key:
tags[key] = value
return tags
def _create_list_params(self, params, items, label):
"""
return parameter list
"""
if isinstance(items, str):
items = [items]
for index, item in enumerate(items):
params[label % (index + 1)] = item
return params
def _ex_connection_class_kwargs(self):
kwargs = super(ElasticLBDriver, self)._ex_connection_class_kwargs()
if hasattr(self, "token") and self.token is not None:
kwargs["token"] = self.token
kwargs["signature_version"] = "4"
else:
kwargs["signature_version"] = self.signature_version
return kwargs
def _ex_list_balancer_tags(self, balancer_id):
params = {"Action": "DescribeTags", "LoadBalancerNames.member.1": balancer_id}
data = self.connection.request(ROOT, params=params).object
return self._to_tags(data)
def _ex_populate_balancer_tags(self, balancer):
tags = balancer.extra.get("tags", {})
tags.update(self._ex_list_balancer_tags(balancer.id))
if tags:
balancer.extra["tags"] = tags
return balancer