blob: 53b29837315e760ffebffdd733be55ae658f6a6c [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.
from libcloud.common.cloudstack import CloudStackDriverMixIn
from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
from libcloud.loadbalancer.base import DEFAULT_ALGORITHM
from libcloud.loadbalancer.types import Provider
from libcloud.loadbalancer.types import State
from libcloud.utils.misc import reverse_dict
class CloudStackLBDriver(CloudStackDriverMixIn, Driver):
"""Driver for CloudStack load balancers."""
api_name = "cloudstack_lb"
name = "CloudStack"
website = "http://cloudstack.org/"
type = Provider.CLOUDSTACK
_VALUE_TO_ALGORITHM_MAP = {
"roundrobin": Algorithm.ROUND_ROBIN,
"leastconn": Algorithm.LEAST_CONNECTIONS,
}
_ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
LB_STATE_MAP = {
"Active": State.RUNNING,
}
def __init__(
self,
key,
secret=None,
secure=True,
host=None,
path=None,
port=None,
*args,
**kwargs,
):
"""
@inherits: :class:`Driver.__init__`
"""
host = host if host else self.host
path = path if path else self.path
if path is not None:
self.path = path
if host is not None:
self.host = host
if (self.type == Provider.CLOUDSTACK) and (not host or not path):
raise Exception(
"When instantiating CloudStack driver directly "
+ "you also need to provide host and path argument"
)
super(CloudStackLBDriver, self).__init__(
key=key, secret=secret, secure=secure, host=host, port=port
)
def list_protocols(self):
"""
We don't actually have any protocol awareness beyond TCP.
:rtype: ``list`` of ``str``
"""
return ["tcp"]
def list_balancers(self):
balancers = self._sync_request(command="listLoadBalancerRules", method="GET")
balancers = balancers.get("loadbalancerrule", [])
return [self._to_balancer(balancer) for balancer in balancers]
def get_balancer(self, balancer_id):
balancer = self._sync_request(
command="listLoadBalancerRules", params={"id": balancer_id}, method="GET"
)
balancer = balancer.get("loadbalancerrule", [])
if not balancer:
raise Exception("no such load balancer: " + str(balancer_id))
return self._to_balancer(balancer[0])
def create_balancer(
self,
name,
members,
protocol="http",
port=80,
algorithm=DEFAULT_ALGORITHM,
location=None,
private_port=None,
network_id=None,
vpc_id=None,
):
"""
@inherits: :class:`Driver.create_balancer`
:param location: Location
:type location: :class:`NodeLocation`
:param private_port: Private port
:type private_port: ``int``
:param network_id: The guest network this rule will be created for.
:type network_id: ``str``
"""
args = {}
ip_args = {}
if location is None:
locations = self._sync_request(command="listZones", method="GET")
location = locations["zone"][0]["id"]
else:
location = location.id
if private_port is None:
private_port = port
if network_id is not None:
args["networkid"] = network_id
ip_args["networkid"] = network_id
if vpc_id is not None:
ip_args["vpcid"] = vpc_id
ip_args.update({"zoneid": location, "networkid": network_id, "vpc_id": vpc_id})
result = self._async_request(
command="associateIpAddress", params=ip_args, method="GET"
)
public_ip = result["ipaddress"]
args.update(
{
"algorithm": self._ALGORITHM_TO_VALUE_MAP[algorithm],
"name": name,
"privateport": private_port,
"publicport": port,
"publicipid": public_ip["id"],
}
)
result = self._sync_request(
command="createLoadBalancerRule", params=args, method="GET"
)
listbalancers = self._sync_request(
command="listLoadBalancerRules", params=args, method="GET"
)
listbalancers = [
rule
for rule in listbalancers["loadbalancerrule"]
if rule["id"] == result["id"]
]
if len(listbalancers) != 1:
return None
balancer = self._to_balancer(listbalancers[0])
for member in members:
balancer.attach_member(member)
return balancer
def destroy_balancer(self, balancer):
self._async_request(
command="deleteLoadBalancerRule", params={"id": balancer.id}, method="GET"
)
self._async_request(
command="disassociateIpAddress",
params={"id": balancer.ex_public_ip_id},
method="GET",
)
def balancer_attach_member(self, balancer, member):
member.port = balancer.ex_private_port
self._async_request(
command="assignToLoadBalancerRule",
params={"id": balancer.id, "virtualmachineids": member.id},
method="GET",
)
return True
def balancer_detach_member(self, balancer, member):
self._async_request(
command="removeFromLoadBalancerRule",
params={"id": balancer.id, "virtualmachineids": member.id},
method="GET",
)
return True
def balancer_list_members(self, balancer):
members = self._sync_request(
command="listLoadBalancerRuleInstances",
params={"id": balancer.id},
method="GET",
)
members = members["loadbalancerruleinstance"]
return [self._to_member(m, balancer.ex_private_port, balancer) for m in members]
def _to_balancer(self, obj):
balancer = LoadBalancer(
id=obj["id"],
name=obj["name"],
state=self.LB_STATE_MAP.get(obj["state"], State.UNKNOWN),
ip=obj["publicip"],
port=obj["publicport"],
driver=self.connection.driver,
)
balancer.ex_private_port = obj["privateport"]
balancer.ex_public_ip_id = obj["publicipid"]
return balancer
def _to_member(self, obj, port, balancer):
return Member(
id=obj["id"], ip=obj["nic"][0]["ipaddress"], port=port, balancer=balancer
)