コード例 #1
0
ファイル: cloudflare.py プロジェクト: dimasmamot/libcloud
from libcloud.common.types import InvalidCredsError, LibcloudError
from libcloud.dns.base import DNSDriver, Zone, Record
from libcloud.dns.types import Provider, RecordType
from libcloud.dns.types import RecordAlreadyExistsError, ZoneAlreadyExistsError
from libcloud.dns.types import RecordDoesNotExistError, ZoneDoesNotExistError
from libcloud.utils.misc import merge_valid_keys, reverse_dict

API_HOST = 'api.cloudflare.com'
API_BASE = '/client/v4'

CLOUDFLARE_TO_LIBCLOUD_ZONE_TYPE = {
    'full': 'master',
    'partial': 'slave',
}

LIBCLOUD_TO_CLOUDFLARE_ZONE_TYPE = reverse_dict(
    CLOUDFLARE_TO_LIBCLOUD_ZONE_TYPE)

ZONE_EXTRA_ATTRIBUTES = {
    'development_mode',
    'original_name_servers',
    'original_registrar',
    'original_dnshost',
    'created_on',
    'modified_on',
    'activated_on',
    'owner',
    'account',
    'permissions',
    'plan',
    'plan_pending',
    'status',
コード例 #2
0
class GoGridLBDriver(BaseGoGridDriver, Driver):
    connectionCls = GoGridLBConnection
    api_name = 'gogrid_lb'
    name = 'GoGrid LB'

    LB_STATE_MAP = {'On': State.RUNNING, 'Unknown': State.UNKNOWN}
    _VALUE_TO_ALGORITHM_MAP = {
        'round robin': Algorithm.ROUND_ROBIN,
        'least connect': Algorithm.LEAST_CONNECTIONS
    }
    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_protocols(self):
        # GoGrid only supports http
        return ['http']

    def list_balancers(self):
        return self._to_balancers(
            self.connection.request('/api/grid/loadbalancer/list').object)

    def ex_create_balancer_nowait(self,
                                  name,
                                  members,
                                  protocol='http',
                                  port=80,
                                  algorithm=DEFAULT_ALGORITHM):
        algorithm = self._algorithm_to_value(algorithm)

        params = {
            'name': name,
            'loadbalancer.type': algorithm,
            'virtualip.ip': self._get_first_ip(),
            'virtualip.port': port
        }
        params.update(self._members_to_params(members))

        resp = self.connection.request('/api/grid/loadbalancer/add',
                                       method='GET',
                                       params=params)
        return self._to_balancers(resp.object)[0]

    def create_balancer(self,
                        name,
                        members,
                        protocol='http',
                        port=80,
                        algorithm=DEFAULT_ALGORITHM):
        balancer = self.ex_create_balancer_nowait(name, members, protocol,
                                                  port, algorithm)

        timeout = 60 * 20
        waittime = 0
        interval = 2 * 15

        if balancer.id is not None:
            return balancer
        else:
            while waittime < timeout:
                balancers = self.list_balancers()

                for i in balancers:
                    if i.name == balancer.name and i.id is not None:
                        return i

                waittime += interval
                time.sleep(interval)

        raise Exception('Failed to get id')

    def destroy_balancer(self, balancer):
        try:
            resp = self.connection.request('/api/grid/loadbalancer/delete',
                                           method='POST',
                                           params={'id': balancer.id})
        except Exception:
            e = sys.exc_info()[1]
            if "Update request for LoadBalancer" in str(e):
                raise LibcloudLBImmutableError(
                    "Cannot delete immutable object", GoGridLBDriver)
            else:
                raise

        return resp.status == 200

    def get_balancer(self, **kwargs):
        params = {}

        try:
            params['name'] = kwargs['ex_balancer_name']
        except KeyError:
            balancer_id = kwargs['balancer_id']
            params['id'] = balancer_id

        resp = self.connection.request('/api/grid/loadbalancer/get',
                                       params=params)

        return self._to_balancers(resp.object)[0]

    def balancer_attach_member(self, balancer, member):
        members = self.balancer_list_members(balancer)
        members.append(member)

        params = {"id": balancer.id}

        params.update(self._members_to_params(members))

        resp = self._update_balancer(params)

        return [
            m for m in self._to_members(resp.object["list"][0]["realiplist"])
            if m.ip == member.ip
        ][0]

    def balancer_detach_member(self, balancer, member):
        members = self.balancer_list_members(balancer)

        remaining_members = [n for n in members if n.id != member.id]

        params = {"id": balancer.id}
        params.update(self._members_to_params(remaining_members))

        resp = self._update_balancer(params)

        return resp.status == 200

    def balancer_list_members(self, balancer):
        resp = self.connection.request('/api/grid/loadbalancer/get',
                                       params={'id': balancer.id})
        return self._to_members(resp.object["list"][0]["realiplist"])

    def _update_balancer(self, params):
        try:
            return self.connection.request('/api/grid/loadbalancer/edit',
                                           method='POST',
                                           params=params)
        except Exception:
            e = sys.exc_info()[1]
            if "Update already pending" in str(e):
                raise LibcloudLBImmutableError("Balancer is immutable",
                                               GoGridLBDriver)

        raise LibcloudError(value='Exception: %s' % str(err), driver=self)

    def _members_to_params(self, members):
        """
        Helper method to convert list of L{Member} objects
        to GET params.

        """

        params = {}

        i = 0
        for member in members:
            params["realiplist.%s.ip" % i] = member.ip
            params["realiplist.%s.port" % i] = member.port
            i += 1

        return params

    def _to_balancers(self, object):
        return [self._to_balancer(el) for el in object["list"]]

    def _to_balancer(self, el):
        lb = LoadBalancer(id=el.get("id"),
                          name=el["name"],
                          state=self.LB_STATE_MAP.get(el["state"]["name"],
                                                      State.UNKNOWN),
                          ip=el["virtualip"]["ip"]["ip"],
                          port=el["virtualip"]["port"],
                          driver=self.connection.driver)
        return lb

    def _to_members(self, object):
        return [self._to_member(el) for el in object]

    def _to_member(self, el):
        member = Member(id=el["ip"]["id"], ip=el["ip"]["ip"], port=el["port"])
        return member
コード例 #3
0
class SoftlayerLBDriver(Driver):
    name = 'Softlayer Load Balancing'
    website = 'http://www.softlayer.com/'
    connectionCls = SoftLayerConnection

    _VALUE_TO_ALGORITHM_MAP = {
        'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
        'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
        'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE,
        'PERSISTENT_IP': Algorithm.PERSISTENT_IP
    }

    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_balancers(self):
        mask = {
            'adcLoadBalancers': {
                'ipAddress': '',
                'loadBalancerHardware': {
                    'datacenter': ''
                },
                'virtualServers': {
                    'serviceGroups': {
                        'routingMethod': '',
                        'routingType': '',
                        'services': {
                            'ipAddress': ''
                        }
                    }
                }
            }
        }

        res = self.connection.request(
            'SoftLayer_Account', 'getAdcLoadBalancers',
            object_mask=mask).object

        return [self._to_balancer(lb) for lb in res]

    def get_balancer(self, balancer_id):
        balancers = self.list_balancers()
        balancer = find(balancers, lambda b: b.id == balancer_id)
        if not balancer:
            raise LibcloudError(value='No balancer found for id: %s' %
                                balancer_id, driver=self)
        return balancer

    def list_protocols(self):
        """
        Return a list of supported protocols.

        :rtype: ``list`` of ``str``
        """
        return ['dns', 'ftp', 'http', 'https', 'tcp', 'udp']

    def balancer_list_members(self, balancer):
        lb = self._get_balancer_model(balancer.id)
        members = []
        vs = self._locate_service_group(lb, balancer.port)
        if vs:
            if vs['serviceGroups']:
                srvgrp = vs['serviceGroups'][0]
                members = [self._to_member(srv, balancer) for
                           srv in srvgrp['services']]

        return members

    def balancer_attach_member(self, balancer, member):
        lb = self._get_balancer_model(balancer.id)
        vs = self._locate_service_group(lb, balancer.port)
        if not vs:
            raise LibcloudError(value='No service_group found for balancer '
                                'port: %s' % balancer.port, driver=self)

        if vs['serviceGroups']:
            services = vs['serviceGroups'][0]['services']
            services.append(self._to_service_template(member.ip,
                                                      member.port))

        self.connection.request(lb_service, 'editObject', lb, id=balancer.id)

        return [m for m in balancer.list_members() if m.ip == member.ip][0]

    def balancer_detach_member(self, balancer, member):
        svc_lbsrv = 'SoftLayer_Network_Application_Delivery_Controller_'\
            'LoadBalancer_Service'

        self.connection.request(svc_lbsrv, 'deleteObject', id=member.id)
        return True

    def destroy_balancer(self, balancer):
        res_billing = self.connection.request(lb_service, 'getBillingItem',
                                              id=balancer.id).object

        self.connection.request('SoftLayer_Billing_Item', 'cancelService',
                                id=res_billing['id'])
        return True

    def ex_list_balancer_packages(self):
        """
        Retrieves the available local load balancer packages.

        :rtype: ``list`` of :class:`LBPackage`
        """
        mask = {
            'prices': ''
        }
        res = self.connection.request('SoftLayer_Product_Package', 'getItems',
                                      id=0, object_mask=mask).object

        res_lb_pkgs = [r for r in res if r['description'].find
                       ('Load Balancer') != -1]
        res_lb_pkgs = [r for r in res_lb_pkgs if not r['description'].
                       startswith('Global')]

        return [self._to_lb_package(r) for r in res_lb_pkgs]

    def ex_place_balancer_order(self, package, location):
        """
        Places an order for a local loadbalancer in the specified
        location.

        :param package: The package to create the loadbalancer from.
        :type package: :class:`LBPackage`

        :param string location: The location (datacenter) to create the
                                loadbalancer.
        :type location: :class:`NodeLocation`

        :return: ``True`` if ex_place_balancer_order was successful.
        :rtype: ``bool``
        """
        data = {
            'complexType': 'SoftLayer_Container_Product_Order_Network_'
                           'LoadBalancer',
            'quantity': 1,
            'packageId': 0,
            'location': self._get_location(location.id),
            'prices': [{'id': package.price_id}]
        }

        self.connection.request('SoftLayer_Product_Order', 'placeOrder',
                                data)
        return True

    def ex_configure_load_balancer(self, balancer, port=80,
                                   protocol='http',
                                   algorithm=DEFAULT_ALGORITHM,
                                   ex_allocation=100):
        """
        Configure the loadbalancer by adding it with a front-end port (aka
        a service group in the Softlayer loadbalancer model).

        Softlayer loadbalancer may be defined with multiple service
        groups (front-end ports) each defined with a unique port number.

        :param balancer: The loadbalancer.
        :type  balancer: :class:`LoadBalancer`

        :param port: Port of the service group, defaults to 80.
        :type  port: ``int``

        :param protocol: Loadbalancer protocol, defaults to http.
        :type  protocol: ``str``

        :param algorithm: Load balancing algorithm, defaults to
                            Algorithm.ROUND_ROBIN
        :type  algorithm: :class:`Algorithm`

        :param ex_allocation: The percentage of the total connection
                              allocations to allocate for this group.
        :type  ex_allocation: ``int``

        :return: ``True`` if ex_add_service_group was successful.
        :rtype: ``bool``
        """
        _types = self._get_routing_types()
        _methods = self._get_routing_methods()

        rt = find(_types, lambda t: t['keyname'] == protocol.upper())
        if not rt:
            raise LibcloudError(value='Invalid protocol %s' % protocol,
                                driver=self)

        value = self._algorithm_to_value(algorithm)
        meth = find(_methods, lambda m: m['keyname'] == value)
        if not meth:
            raise LibcloudError(value='Invalid algorithm %s' % algorithm,
                                driver=self)

        service_group_template = {
            'port': port,
            'allocation': ex_allocation,
            'serviceGroups': [{
                'routingTypeId': rt['id'],
                'routingMethodId': meth['id']
            }]
        }

        lb = self._get_balancer_model(balancer.id)
        if len(lb['virtualServers']) > 0:
            port = lb['virtualServers'][0]['port']
            raise LibcloudError(value='Loadbalancer already configured with '
                                'a service group (front-end port)' % port,
                                driver=self)

        lb['virtualServers'].append(service_group_template)
        self.connection.request(lb_service, 'editObject', lb, id=balancer.id)
        return True

    def _get_balancer_model(self, balancer_id):
        """
        Retrieve Softlayer loadbalancer model.
        """
        lb_mask = {
            'virtualServers': {
                'serviceGroups': {
                    'services': {
                        'ipAddress': '',
                        'groupReferences': '',
                    }
                }
            }
        }

        lb_res = self.connection.request(lb_service, 'getObject',
                                         object_mask=lb_mask, id=balancer_id).\
            object
        return lb_res

    def _locate_service_group(self, lb, port):
        """
        Locate service group with given port.

        Return virtualServers (vs) entry whose port matches the
        supplied parameter port. For a negative port, just return
        the first vs entry.
        None is returned if no match found.

        :param lb: Softlayer loadbalancer model.
        :type lb: ``dict``

        :param port: loadbalancer front-end port.
        :type port: ``int``

        :return: Matched entry in the virtualServers array of the supplied
        model.
        :rtype: ``dict``
        """
        vs = None
        if port < 0:
            vs = lb['virtualServers'][0] if lb['virtualServers']\
                else None
        else:
            vs = find(lb['virtualServers'], lambda v: v['port'] == port)

        return vs

    def _get_routing_types(self):
        svc_rtype = 'SoftLayer_Network_Application_Delivery_Controller_'\
            'LoadBalancer_Routing_Type'

        return self.connection.request(svc_rtype, 'getAllObjects').object

    def _get_routing_methods(self):
        svc_rmeth = 'SoftLayer_Network_Application_Delivery_Controller_'\
            'LoadBalancer_Routing_Method'

        return self.connection.request(svc_rmeth, 'getAllObjects').object

    def _get_location(self, location_id):
        res = self.connection.request('SoftLayer_Location_Datacenter',
                                      'getDatacenters').object

        dcenter = find(res, lambda d: d['name'] == location_id)
        if not dcenter:
            raise LibcloudError(value='Invalid value %s' % location_id,
                                driver=self)
        return dcenter['id']

    def _get_ipaddress(self, ip):
        svc_ipaddress = 'SoftLayer_Network_Subnet_IpAddress'

        return self.connection.request(svc_ipaddress, 'getByIpAddress',
                                       ip).object

    def _to_lb_package(self, pkg):
        try:
            price_id = pkg['prices'][0]['id']
        except Exception:
            price_id = -1

        capacity = int(pkg.get('capacity', 0))
        return LBPackage(id=pkg['id'], name=pkg['keyName'],
                         description=pkg['description'],
                         price_id=price_id, capacity=capacity)

    def _to_service_template(self, ip, port):
        """
        Builds single member entry in Softlayer loadbalancer model
        """
        template = {
            'enabled': 1,  # enable the service
            'port': port,  # back-end port
            'ipAddressId': self._get_ipaddress(ip)['id'],
            'healthChecks': [{
                'healthCheckTypeId': 21  # default health check
            }],
            'groupReferences': [{
                'weight': 1
            }]
        }

        return template

    def _to_balancer(self, lb):
        ipaddress = lb['ipAddress']['ipAddress']

        extra = {}
        extra['connection_limit'] = lb['connectionLimit']
        extra['ssl_active'] = lb['sslActiveFlag']
        extra['ssl_enabled'] = lb['sslEnabledFlag']
        extra['ha'] = lb['highAvailabilityFlag']
        extra['datacenter'] = \
            lb['loadBalancerHardware'][0]['datacenter']['name']

        # In Softlayer, there could be multiple group of members (aka service
        # groups), so retrieve the first one
        vs = self._locate_service_group(lb, -1)
        if vs:
            port = vs['port']
            if vs['serviceGroups']:
                srvgrp = vs['serviceGroups'][0]
                routing_method = srvgrp['routingMethod']['keyname']
                routing_type = srvgrp['routingType']['keyname']
                try:
                    extra['algorithm'] = self.\
                        _value_to_algorithm(routing_method)
                except Exception:
                    pass
                extra['protocol'] = routing_type.lower()

        if not vs:
            port = -1

        balancer = LoadBalancer(
            id=lb['id'],
            name='',
            state=State.UNKNOWN,
            ip=ipaddress,
            port=port,
            driver=self.connection.driver,
            extra=extra
        )

        return balancer

    def _to_member(self, srv, balancer=None):
        svc_id = srv['id']
        ip = srv['ipAddress']['ipAddress']
        port = srv['port']

        extra = {}
        extra['status'] = srv['status']
        extra['enabled'] = srv['enabled']
        return Member(id=svc_id, ip=ip, port=port, balancer=balancer,
                      extra=extra)
コード例 #4
0
class DimensionDataLBDriver(Driver):
    """
    DimensionData node driver.
    """

    selected_region = None
    connectionCls = DimensionDataConnection
    name = 'Dimension Data Load Balancer'
    website = 'https://cloud.dimensiondata.com/'
    type = Provider.DIMENSIONDATA
    api_version = 1.0

    network_domain_id = None

    _VALUE_TO_ALGORITHM_MAP = {
        'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
        'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
        'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE,
        'PERSISTENT_IP': Algorithm.PERSISTENT_IP
    }
    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    _VALUE_TO_STATE_MAP = {
        'NORMAL': State.RUNNING,
        'PENDING_ADD': State.PENDING,
        'PENDING_CHANGE': State.PENDING,
        'PENDING_DELETE': State.PENDING,
        'FAILED_ADD': State.ERROR,
        'FAILED_CHANGE': State.ERROR,
        'FAILED_DELETE': State.ERROR,
        'REQUIRES_SUPPORT': State.ERROR
    }

    def __init__(self,
                 key,
                 secret=None,
                 secure=True,
                 host=None,
                 port=None,
                 api_version=None,
                 region=DEFAULT_REGION,
                 **kwargs):

        if region not in API_ENDPOINTS:
            raise ValueError('Invalid region: %s' % (region))

        self.selected_region = API_ENDPOINTS[region]

        super(DimensionDataLBDriver, self).__init__(key=key,
                                                    secret=secret,
                                                    secure=secure,
                                                    host=host,
                                                    port=port,
                                                    api_version=api_version,
                                                    region=region,
                                                    **kwargs)

    def _ex_connection_class_kwargs(self):
        """
            Add the region to the kwargs before the connection is instantiated
        """

        kwargs = super(DimensionDataLBDriver,
                       self)._ex_connection_class_kwargs()
        kwargs['region'] = self.selected_region
        return kwargs

    def create_balancer(self, name, port, protocol, algorithm, members):
        """
        Create a new load balancer instance

        :param name: Name of the new load balancer (required)
        :type  name: ``str``

        :param port: Port the load balancer should listen on,
                    defaults to 80 (required)
        :type  port: ``str``

        :param protocol: Loadbalancer protocol, defaults to http.
        :type  protocol: ``str``

        :param members: list of Members to attach to balancer (optional)
        :type  members: ``list`` of :class:`Member`

        :param algorithm: Load balancing algorithm, defaults to ROUND_ROBIN.
        :type algorithm: :class:`.Algorithm`

        :rtype: :class:`LoadBalancer`
        """
        network_domain_id = self.network_domain_id
        if port is None:
            port = 80
        if protocol is None:
            protocol = 'http'
        if algorithm is None:
            algorithm = Algorithm.ROUND_ROBIN

        # Create a pool first
        pool = self.ex_create_pool(
            network_domain_id=network_domain_id,
            name=name,
            ex_description=None,
            balancer_method=self._ALGORITHM_TO_VALUE_MAP[algorithm])

        # Attach the members to the pool as nodes
        if members is not None:
            for member in members:
                node = self.ex_create_node(network_domain_id=network_domain_id,
                                           name=member.ip,
                                           ip=member.ip,
                                           ex_description=None)
                self.ex_create_pool_member(pool=pool, node=node, port=port)

        # Create the virtual listener (balancer)
        listener = self.ex_create_virtual_listener(
            network_domain_id=network_domain_id,
            name=name,
            ex_description=name,
            port=port,
            pool=pool)

        return LoadBalancer(id=listener.id,
                            name=listener.name,
                            state=State.RUNNING,
                            ip=listener.ip,
                            port=port,
                            driver=self,
                            extra={
                                'pool_id': pool.id,
                                'network_domain_id': network_domain_id
                            })

    def list_balancers(self):
        """
        List all loadbalancers inside a geography.

        In Dimension Data terminology these are known as virtual listeners

        :rtype: ``list`` of :class:`LoadBalancer`
        """

        return self._to_balancers(
            self.connection.request_with_orgId_api_2(
                'networkDomainVip/virtualListener').object)

    def get_balancer(self, balancer_id):
        """
        Return a :class:`LoadBalancer` object.

        :param balancer_id: id of a load balancer you want to fetch
        :type  balancer_id: ``str``

        :rtype: :class:`LoadBalancer`
        """

        bal = self.connection \
            .request_with_orgId_api_2('networkDomainVip/virtualListener/%s'
                                      % balancer_id).object
        return self._to_balancer(bal)

    def list_protocols(self):
        """
        Return a list of supported protocols.

        Since all protocols are support by Dimension Data, this is a list
        of common protocols.

        :rtype: ``list`` of ``str``
        """
        return ['http', 'https', 'tcp', 'udp']

    def balancer_list_members(self, balancer):
        """
        Return list of members attached to balancer.

        In Dimension Data terminology these are the members of the pools
        within a virtual listener.

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :rtype: ``list`` of :class:`Member`
        """
        pool_members = self.ex_get_pool_members(balancer.extra['pool_id'])
        members = []
        for pool_member in pool_members:
            members.append(
                Member(id=pool_member.id,
                       ip=pool_member.ip,
                       port=pool_member.port,
                       balancer=balancer,
                       extra=None))
        return members

    def balancer_attach_member(self, balancer, member):
        """
        Attach a member to balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param member: Member to join to the balancer
        :type member: :class:`Member`

        :return: Member after joining the balancer.
        :rtype: :class:`Member`
        """
        node = self.ex_create_node(
            network_domain_id=balancer.extra['network_domain_id'],
            name='Member.' + member.ip,
            ip=member.ip,
            ex_description='')
        if node is False:
            return False
        pool = self.ex_get_pool(balancer.extra['pool_id'])
        pool_member = self.ex_create_pool_member(pool=pool,
                                                 node=node,
                                                 port=member.port)
        member.id = pool_member.id
        return member

    def balancer_detach_member(self, balancer, member):
        """
        Detach member from balancer

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :param member: Member which should be used
        :type member: :class:`Member`

        :return: ``True`` if member detach was successful, otherwise ``False``.
        :rtype: ``bool``
        """
        create_pool_m = ET.Element('removePoolMember', {
            'xmlns': TYPES_URN,
            'id': member.id
        })

        result = self.connection.request_with_orgId_api_2(
            'networkDomainVip/removePoolMember',
            method='POST',
            data=ET.tostring(create_pool_m)).object
        response_code = findtext(result, 'responseCode', TYPES_URN)
        return response_code in ['IN_PROGRESS', 'OK']

    def destroy_balancer(self, balancer):
        """
        Destroy a load balancer (virtual listener)

        :param balancer: LoadBalancer which should be used
        :type  balancer: :class:`LoadBalancer`

        :return: ``True`` if the destroy was successful, otherwise ``False``.
        :rtype: ``bool``
        """
        delete_listener = ET.Element('deleteVirtualListener', {
            'xmlns': TYPES_URN,
            'id': balancer.id
        })

        result = self.connection.request_with_orgId_api_2(
            'networkDomainVip/deleteVirtualListener',
            method='POST',
            data=ET.tostring(delete_listener)).object
        response_code = findtext(result, 'responseCode', TYPES_URN)
        return response_code in ['IN_PROGRESS', 'OK']

    def ex_set_current_network_domain(self, network_domain_id):
        """
        Set the network domain (part of the network) of the driver

        :param network_domain_id: ID of the pool (required)
        :type  network_domain_id: ``str``
        """
        self.network_domain_id = network_domain_id

    def ex_get_current_network_domain(self):
        """
        Get the current network domain ID of the driver.

        :return: ID of the network domain
        :rtype: ``str``
        """
        return self.network_domain_id

    def ex_create_pool_member(self, pool, node, port=None):
        """
        Create a new member in an existing pool from an existing node

        :param pool: Instance of ``DimensionDataPool`` (required)
        :type  pool: ``DimensionDataPool``

        :param node: Instance of ``DimensionDataVIPNode`` (required)
        :type  node: ``DimensionDataVIPNode``

        :param port: Port the the service will listen on
        :type  port: ``str``

        :return: The node member, instance of ``DimensionDataPoolMember``
        :rtype: ``DimensionDataPoolMember``
        """
        create_pool_m = ET.Element('addPoolMember', {'xmlns': TYPES_URN})
        ET.SubElement(create_pool_m, "poolId").text = pool.id
        ET.SubElement(create_pool_m, "nodeId").text = node.id
        if port is not None:
            ET.SubElement(create_pool_m, "port").text = str(port)
        ET.SubElement(create_pool_m, "status").text = 'ENABLED'

        response = self.connection.request_with_orgId_api_2(
            'networkDomainVip/addPoolMember',
            method='POST',
            data=ET.tostring(create_pool_m)).object

        member_id = None
        node_name = None
        for info in findall(response, 'info', TYPES_URN):
            if info.get('name') == 'poolMemberId':
                member_id = info.get('value')
            if info.get('name') == 'nodeName':
                node_name = info.get('value')

        return DimensionDataPoolMember(id=member_id,
                                       name=node_name,
                                       status=State.RUNNING,
                                       ip=node.ip,
                                       port=port,
                                       node_id=node.id)

    def ex_create_node(self,
                       network_domain_id,
                       name,
                       ip,
                       ex_description,
                       connection_limit=25000,
                       connection_rate_limit=2000):
        """
        Create a new node

        :param network_domain_id: Network Domain ID (required)
        :type  name: ``str``

        :param name: name of the node (required)
        :type  name: ``str``

        :param ip: IPv4 address of the node (required)
        :type  ip: ``str``

        :param ex_description: Description of the node (required)
        :type  ex_description: ``str``

        :param connection_limit: Maximum number
                of concurrent connections per sec
        :type  connection_limit: ``int``

        :param connection_rate_limit: Maximum number of concurrent sessions
        :type  connection_rate_limit: ``int``

        :return: Instance of ``DimensionDataVIPNode``
        :rtype: ``DimensionDataVIPNode``
        """
        create_node_elm = ET.Element('createNode', {'xmlns': TYPES_URN})
        ET.SubElement(create_node_elm, "networkDomainId") \
            .text = network_domain_id
        ET.SubElement(create_node_elm, "name").text = name
        ET.SubElement(create_node_elm, "description").text \
            = str(ex_description)
        ET.SubElement(create_node_elm, "ipv4Address").text = ip
        ET.SubElement(create_node_elm, "status").text = 'ENABLED'
        ET.SubElement(create_node_elm, "connectionLimit") \
            .text = str(connection_limit)
        ET.SubElement(create_node_elm, "connectionRateLimit") \
            .text = str(connection_rate_limit)

        response = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/createNode',
            method='POST',
            data=ET.tostring(create_node_elm)).object

        node_id = None
        node_name = None
        for info in findall(response, 'info', TYPES_URN):
            if info.get('name') == 'nodeId':
                node_id = info.get('value')
            if info.get('name') == 'name':
                node_name = info.get('value')
        return DimensionDataVIPNode(id=node_id,
                                    name=node_name,
                                    status=State.RUNNING,
                                    ip=ip)

    def ex_update_node(self, node):
        """
        Update the properties of a node

        :param pool: The instance of ``DimensionDataNode`` to update
        :type  pool: ``DimensionDataNode``

        :return: The instance of ``DimensionDataNode``
        :rtype: ``DimensionDataNode``
        """
        create_node_elm = ET.Element('editNode', {'xmlns': TYPES_URN})
        ET.SubElement(create_node_elm, "connectionLimit") \
            .text = str(node.connection_limit)
        ET.SubElement(create_node_elm, "connectionRateLimit") \
            .text = str(node.connection_rate_limit)

        self.connection.request_with_orgId_api_2(
            action='networkDomainVip/createNode',
            method='POST',
            data=ET.tostring(create_node_elm)).object
        return node

    def ex_set_node_state(self, node, enabled):
        """
        Change the state of a node (enable/disable)

        :param pool: The instance of ``DimensionDataNode`` to update
        :type  pool: ``DimensionDataNode``

        :param enabled: The target state of the node
        :type  enabled: ``bool``

        :return: The instance of ``DimensionDataNode``
        :rtype: ``DimensionDataNode``
        """
        create_node_elm = ET.Element('editNode', {'xmlns': TYPES_URN})
        ET.SubElement(create_node_elm, "status") \
            .text = "ENABLED" if enabled is True else "DISABLED"

        self.connection.request_with_orgId_api_2(
            action='networkDomainVip/editNode',
            method='POST',
            data=ET.tostring(create_node_elm)).object
        return node

    def ex_create_pool(self,
                       network_domain_id,
                       name,
                       balancer_method,
                       ex_description,
                       health_monitors=None,
                       service_down_action='NONE',
                       slow_ramp_time=30):
        """
        Create a new pool

        :param network_domain_id: Network Domain ID (required)
        :type  name: ``str``

        :param name: name of the node (required)
        :type  name: ``str``

        :param balancer_method: The load balancer algorithm (required)
        :type  balancer_method: ``str``

        :param ex_description: Description of the node (required)
        :type  ex_description: ``str``

        :param health_monitors: A list of health monitors to use for the pool.
        :type  health_monitors: ``list`` of
            :class:`DimensionDataDefaultHealthMonitor`

        :param service_down_action: What to do when node
                                    is unavailable NONE, DROP or RESELECT
        :type  service_down_action: ``str``

        :param slow_ramp_time: Number of seconds to stagger ramp up of nodes
        :type  slow_ramp_time: ``int``

        :return: Instance of ``DimensionDataPool``
        :rtype: ``DimensionDataPool``
        """
        # Names cannot contain spaces.
        name.replace(' ', '_')
        create_node_elm = ET.Element('createPool', {'xmlns': TYPES_URN})
        ET.SubElement(create_node_elm, "networkDomainId") \
            .text = network_domain_id
        ET.SubElement(create_node_elm, "name").text = name
        ET.SubElement(create_node_elm, "description").text \
            = str(ex_description)
        ET.SubElement(create_node_elm, "loadBalanceMethod") \
            .text = str(balancer_method)

        if health_monitors is not None:
            for monitor in health_monitors:
                ET.SubElement(create_node_elm, "healthMonitorId") \
                    .text = str(monitor.id)

        ET.SubElement(create_node_elm, "serviceDownAction") \
            .text = service_down_action
        ET.SubElement(create_node_elm, "slowRampTime").text \
            = str(slow_ramp_time)

        response = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/createPool',
            method='POST',
            data=ET.tostring(create_node_elm)).object

        pool_id = None
        for info in findall(response, 'info', TYPES_URN):
            if info.get('name') == 'poolId':
                pool_id = info.get('value')

        return DimensionDataPool(id=pool_id,
                                 name=name,
                                 description=ex_description,
                                 status=State.RUNNING,
                                 load_balance_method=str(balancer_method),
                                 health_monitor_id=None,
                                 service_down_action=service_down_action,
                                 slow_ramp_time=str(slow_ramp_time))

    def ex_create_virtual_listener(self,
                                   network_domain_id,
                                   name,
                                   ex_description,
                                   port,
                                   pool,
                                   listener_ip_address=None,
                                   persistence_profile=None,
                                   fallback_persistence_profile=None,
                                   irule=None,
                                   protocol='TCP',
                                   connection_limit=25000,
                                   connection_rate_limit=2000,
                                   source_port_preservation='PRESERVE'):
        """
        Create a new virtual listener (load balancer)

        :param network_domain_id: Network Domain ID (required)
        :type  name: ``str``

        :param name: name of the listener (required)
        :type  name: ``str``

        :param ex_description: Description of the node (required)
        :type  ex_description: ``str``

        :param port: Description of the node (required)
        :type  port: ``str``

        :param pool: The pool to use for the listener
        :type  pool: :class:`DimensionDataPool`

        :param listener_ip_address: The IPv4 Address of the virtual listener
        :type  listener_ip_address: ``str``

        :param persistence_profile: Persistence profile
        :type  persistence_profile: :class:`DimensionDataPersistenceProfile`

        :param fallback_persistence_profile: Fallback persistence profile
        :type  fallback_persistence_profile:
            :class:`DimensionDataPersistenceProfile`

        :param irule: The iRule to apply
        :type  irule: :class:`DimensionDataDefaultiRule`

        :param protocol: For STANDARD type, ANY, TCP or UDP
                         for PERFORMANCE_LAYER_4 choice of ANY, TCP, UDP, HTTP
        :type  protcol: ``str``

        :param connection_limit: Maximum number
                                of concurrent connections per sec
        :type  connection_limit: ``int``

        :param connection_rate_limit: Maximum number of concurrent sessions
        :type  connection_rate_limit: ``int``

        :param source_port_preservation: Choice of PRESERVE,
                                        PRESERVE_STRICT or CHANGE
        :type  source_port_preservation: ``str``

        :return: Instance of the listener
        :rtype: ``DimensionDataVirtualListener``
        """
        if port is 80 or 443:
            listener_type = 'PERFORMANCE_LAYER_4'
            protocol = 'HTTP'
        else:
            listener_type = 'STANDARD'

        create_node_elm = ET.Element('createVirtualListener',
                                     {'xmlns': TYPES_URN})
        ET.SubElement(create_node_elm, "networkDomainId") \
            .text = network_domain_id
        ET.SubElement(create_node_elm, "name").text = name
        ET.SubElement(create_node_elm, "description").text = \
            str(ex_description)
        ET.SubElement(create_node_elm, "type").text = listener_type
        ET.SubElement(create_node_elm, "protocol") \
            .text = protocol
        if listener_ip_address is not None:
            ET.SubElement(create_node_elm, "listenerIpAddress").text = \
                str(listener_ip_address)
        ET.SubElement(create_node_elm, "port").text = str(port)
        ET.SubElement(create_node_elm, "enabled").text = 'true'
        ET.SubElement(create_node_elm, "connectionLimit") \
            .text = str(connection_limit)
        ET.SubElement(create_node_elm, "connectionRateLimit") \
            .text = str(connection_rate_limit)
        ET.SubElement(create_node_elm, "sourcePortPreservation") \
            .text = source_port_preservation
        ET.SubElement(create_node_elm, "poolId") \
            .text = pool.id
        if persistence_profile is not None:
            ET.SubElement(create_node_elm, "persistenceProfileId") \
                .text = persistence_profile.id
        if fallback_persistence_profile is not None:
            ET.SubElement(create_node_elm, "fallbackPersistenceProfileId") \
                .text = fallback_persistence_profile.id
        if irule is not None:
            ET.SubElement(create_node_elm, "iruleId") \
                .text = irule.id

        response = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/createVirtualListener',
            method='POST',
            data=ET.tostring(create_node_elm)).object

        virtual_listener_id = None
        virtual_listener_ip = None
        for info in findall(response, 'info', TYPES_URN):
            if info.get('name') == 'virtualListenerId':
                virtual_listener_id = info.get('value')
            if info.get('name') == 'listenerIpAddress':
                virtual_listener_ip = info.get('value')

        return DimensionDataVirtualListener(id=virtual_listener_id,
                                            name=name,
                                            ip=virtual_listener_ip,
                                            status=State.RUNNING)

    def ex_get_pools(self):
        """
        Get all of the pools inside the current geography

        :return: Returns a ``list`` of type ``DimensionDataPool``
        :rtype: ``list`` of ``DimensionDataPool``
        """
        pools = self.connection \
            .request_with_orgId_api_2('networkDomainVip/pool').object
        return self._to_pools(pools)

    def ex_get_pool(self, pool_id):
        """
        Get a specific pool inside the current geography

        :param pool_id: The identifier of the pool
        :type  pool_id: ``str``

        :return: Returns an instance of ``DimensionDataPool``
        :rtype: ``DimensionDataPool``
        """
        pool = self.connection \
            .request_with_orgId_api_2('networkDomainVip/pool/%s'
                                      % pool_id).object
        return self._to_pool(pool)

    def ex_update_pool(self, pool):
        """
        Update the properties of an existing pool
        only method, serviceDownAction and slowRampTime are updated

        :param pool: The instance of ``DimensionDataPool`` to update
        :type  pool: ``DimensionDataPool``

        :return: ``True`` for success, ``False`` for failure
        :rtype: ``bool``
        """
        create_node_elm = ET.Element('editPool', {'xmlns': TYPES_URN})

        ET.SubElement(create_node_elm, "loadBalanceMethod") \
            .text = str(pool.load_balance_method)
        ET.SubElement(create_node_elm, "serviceDownAction") \
            .text = pool.service_down_action
        ET.SubElement(create_node_elm, "slowRampTime").text \
            = str(pool.slow_ramp_time)

        response = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/editPool',
            method='POST',
            data=ET.tostring(create_node_elm)).object
        response_code = findtext(response, 'responseCode', TYPES_URN)
        return response_code in ['IN_PROGRESS', 'OK']

    def ex_destroy_pool(self, pool):
        """
        Destroy an existing pool

        :param pool: The instance of ``DimensionDataPool`` to destroy
        :type  pool: ``DimensionDataPool``

        :return: ``True`` for success, ``False`` for failure
        :rtype: ``bool``
        """
        destroy_request = ET.Element('deletePool', {
            'xmlns': TYPES_URN,
            'id': pool.id
        })

        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/deletePool',
            method='POST',
            data=ET.tostring(destroy_request)).object
        response_code = findtext(result, 'responseCode', TYPES_URN)
        return response_code in ['IN_PROGRESS', 'OK']

    def ex_get_pool_members(self, pool_id):
        """
        Get the members of a pool

        :param pool: The instance of a pool
        :type  pool: ``DimensionDataPool``

        :return: Returns an ``list`` of ``DimensionDataPoolMember``
        :rtype: ``list`` of ``DimensionDataPoolMember``
        """
        members = self.connection \
            .request_with_orgId_api_2('networkDomainVip/poolMember?poolId=%s'
                                      % pool_id).object
        return self._to_members(members)

    def ex_get_pool_member(self, pool_member_id):
        """
        Get a specific member of a pool

        :param pool: The id of a pool member
        :type  pool: ``str``

        :return: Returns an instance of ``DimensionDataPoolMember``
        :rtype: ``DimensionDataPoolMember``
        """
        member = self.connection \
            .request_with_orgId_api_2('networkDomainVip/poolMember/%s'
                                      % pool_member_id).object
        return self._to_member(member)

    def ex_set_pool_member_state(self, member, enabled=True):
        request = ET.Element('editPoolMember', {
            'xmlns': TYPES_URN,
            'id': member.id
        })
        state = "ENABLED" if enabled is True else "DISABLED"
        ET.SubElement(request, 'status').text = state

        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/editPoolMember',
            method='POST',
            data=ET.tostring(request)).object

        response_code = findtext(result, 'responseCode', TYPES_URN)
        return response_code in ['IN_PROGRESS', 'OK']

    def ex_destroy_pool_member(self, member, destroy_node=False):
        """
        Destroy a specific member of a pool

        :param pool: The instance of a pool member
        :type  pool: ``DimensionDataPoolMember``

        :param destroy_node: Also destroy the associated node
        :type  destroy_node: ``bool``

        :return: ``True`` for success, ``False`` for failure
        :rtype: ``bool``
        """
        # remove the pool member
        destroy_request = ET.Element('removePoolMember', {
            'xmlns': TYPES_URN,
            'id': member.id
        })

        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/removePoolMember',
            method='POST',
            data=ET.tostring(destroy_request)).object

        if member.node_id is not None and destroy_node is True:
            return self.ex_destroy_node(member.node_id)
        else:
            response_code = findtext(result, 'responseCode', TYPES_URN)
            return response_code in ['IN_PROGRESS', 'OK']

    def ex_get_nodes(self):
        """
        Get the nodes within this geography

        :return: Returns an ``list`` of ``DimensionDataVIPNode``
        :rtype: ``list`` of ``DimensionDataVIPNode``
        """
        nodes = self.connection \
            .request_with_orgId_api_2('networkDomainVip/node').object
        return self._to_nodes(nodes)

    def ex_get_node(self, node_id):
        """
        Get the node specified by node_id

        :return: Returns an instance of ``DimensionDataVIPNode``
        :rtype: Instance of ``DimensionDataVIPNode``
        """
        nodes = self.connection \
            .request_with_orgId_api_2('networkDomainVip/node/%s'
                                      % node_id).object
        return self._to_node(nodes)

    def ex_destroy_node(self, node_id):
        """
        Destroy a specific node

        :param node_id: The ID of of a ``DimensionDataVIPNode``
        :type  node_id: ``str``

        :return: ``True`` for success, ``False`` for failure
        :rtype: ``bool``
        """
        # Destroy the node
        destroy_request = ET.Element('deleteNode', {
            'xmlns': TYPES_URN,
            'id': node_id
        })

        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/deleteNode',
            method='POST',
            data=ET.tostring(destroy_request)).object
        response_code = findtext(result, 'responseCode', TYPES_URN)
        return response_code in ['IN_PROGRESS', 'OK']

    def ex_wait_for_state(self,
                          state,
                          func,
                          poll_interval=2,
                          timeout=60,
                          *args,
                          **kwargs):
        """
        Wait for the function which returns a instance
        with field status to match

        Keep polling func until one of the desired states is matched

        :param state: Either the desired state (`str`) or a `list` of states
        :type  state: ``str`` or ``list``

        :param  func: The function to call, e.g. ex_get_vlan
        :type   func: ``function``

        :param  poll_interval: The number of seconds to wait between checks
        :type   poll_interval: `int`

        :param  timeout: The total number of seconds to wait to reach a state
        :type   timeout: `int`

        :param  args: The arguments for func
        :type   args: Positional arguments

        :param  kwargs: The arguments for func
        :type   kwargs: Keyword arguments
        """
        return self.connection.wait_for_state(state, func, poll_interval,
                                              timeout, *args, **kwargs)

    def ex_get_default_health_monitors(self, network_domain_id):
        """
        Get the default health monitors available for a network domain

        :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain``
        :type  network_domain_id: ``str``

        :rtype: `list` of :class:`DimensionDataDefaultHealthMonitor`
        """
        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/defaultHealthMonitor',
            params={
                'networkDomainId': network_domain_id
            },
            method='GET').object
        return self._to_health_monitors(result)

    def ex_get_default_persistence_profiles(self, network_domain_id):
        """
        Get the default persistence profiles available for a network domain

        :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain``
        :type  network_domain_id: ``str``

        :rtype: `list` of :class:`DimensionDataPersistenceProfile`
        """
        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/defaultPersistenceProfile',
            params={
                'networkDomainId': network_domain_id
            },
            method='GET').object
        return self._to_persistence_profiles(result)

    def ex_get_default_irules(self, network_domain_id):
        """
        Get the default iRules available for a network domain

        :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain``
        :type  network_domain_id: ``str``

        :rtype: `list` of :class:`DimensionDataDefaultiRule`
        """
        result = self.connection.request_with_orgId_api_2(
            action='networkDomainVip/defaultIrule',
            params={
                'networkDomainId': network_domain_id
            },
            method='GET').object
        return self._to_irules(result)

    def _to_irules(self, object):
        irules = []
        matches = object.findall(fixxpath('defaultIrule', TYPES_URN))
        for element in matches:
            irules.append(self._to_irule(element))
        return irules

    def _to_irule(self, element):
        compatible = []
        matches = element.findall(
            fixxpath('virtualListenerCompatibility', TYPES_URN))
        for match_element in matches:
            compatible.append(
                DimensionDataVirtualListenerCompatibility(
                    type=match_element.get('type'),
                    protocol=match_element.get('protocol', None)))
        irule_element = element.find(fixxpath('irule', TYPES_URN))
        return DimensionDataDefaultiRule(id=irule_element.get('id'),
                                         name=irule_element.get('name'),
                                         compatible_listeners=compatible)

    def _to_persistence_profiles(self, object):
        profiles = []
        matches = object.findall(
            fixxpath('defaultPersistenceProfile', TYPES_URN))
        for element in matches:
            profiles.append(self._to_persistence_profile(element))
        return profiles

    def _to_persistence_profile(self, element):
        compatible = []
        matches = element.findall(
            fixxpath('virtualListenerCompatibility', TYPES_URN))
        for match_element in matches:
            compatible.append(
                DimensionDataVirtualListenerCompatibility(
                    type=match_element.get('type'),
                    protocol=match_element.get('protocol', None)))

        return DimensionDataPersistenceProfile(
            id=element.get('id'),
            fallback_compatible=bool(
                element.get('fallbackCompatible') == "true"),
            name=findtext(element, 'name', TYPES_URN),
            compatible_listeners=compatible)

    def _to_health_monitors(self, object):
        monitors = []
        matches = object.findall(fixxpath('defaultHealthMonitor', TYPES_URN))
        for element in matches:
            monitors.append(self._to_health_monitor(element))
        return monitors

    def _to_health_monitor(self, element):
        return DimensionDataDefaultHealthMonitor(
            id=element.get('id'),
            name=findtext(element, 'name', TYPES_URN),
            node_compatible=bool(
                findtext(element, 'nodeCompatible', TYPES_URN) == "true"),
            pool_compatible=bool(
                findtext(element, 'poolCompatible', TYPES_URN) == "true"),
        )

    def _to_nodes(self, object):
        nodes = []
        for element in object.findall(fixxpath("node", TYPES_URN)):
            nodes.append(self._to_node(element))

        return nodes

    def _to_node(self, element):
        ipaddress = findtext(element, 'ipv4Address', TYPES_URN)
        if ipaddress is None:
            ipaddress = findtext(element, 'ipv6Address', TYPES_URN)

        name = findtext(element, 'name', TYPES_URN)

        node = DimensionDataVIPNode(
            id=element.get('id'),
            name=name,
            status=self._VALUE_TO_STATE_MAP.get(
                findtext(element, 'state', TYPES_URN), State.UNKNOWN),
            connection_rate_limit=findtext(element, 'connectionRateLimit',
                                           TYPES_URN),
            connection_limit=findtext(element, 'connectionLimit', TYPES_URN),
            ip=ipaddress)

        return node

    def _to_balancers(self, object):
        loadbalancers = []
        for element in object.findall(fixxpath("virtualListener", TYPES_URN)):
            loadbalancers.append(self._to_balancer(element))

        return loadbalancers

    def _to_balancer(self, element):
        ipaddress = findtext(element, 'listenerIpAddress', TYPES_URN)
        name = findtext(element, 'name', TYPES_URN)
        port = findtext(element, 'port', TYPES_URN)
        extra = {}

        pool_element = element.find(fixxpath('pool', TYPES_URN))
        if pool_element is None:
            extra['pool_id'] = None

        else:
            extra['pool_id'] = pool_element.get('id')

        extra['network_domain_id'] = findtext(element, 'networkDomainId',
                                              TYPES_URN)

        balancer = LoadBalancer(id=element.get('id'),
                                name=name,
                                state=self._VALUE_TO_STATE_MAP.get(
                                    findtext(element, 'state', TYPES_URN),
                                    State.UNKNOWN),
                                ip=ipaddress,
                                port=port,
                                driver=self.connection.driver,
                                extra=extra)

        return balancer

    def _to_members(self, object):
        members = []
        for element in object.findall(fixxpath("poolMember", TYPES_URN)):
            members.append(self._to_member(element))

        return members

    def _to_member(self, element):
        port = findtext(element, 'port', TYPES_URN)
        if port is not None:
            port = int(port)
        pool_member = DimensionDataPoolMember(
            id=element.get('id'),
            name=element.find(fixxpath('node', TYPES_URN)).get('name'),
            status=findtext(element, 'state', TYPES_URN),
            node_id=element.find(fixxpath('node', TYPES_URN)).get('id'),
            ip=element.find(fixxpath('node', TYPES_URN)).get('ipAddress'),
            port=port)
        return pool_member

    def _to_pools(self, object):
        pools = []
        for element in object.findall(fixxpath("pool", TYPES_URN)):
            pools.append(self._to_pool(element))

        return pools

    def _to_pool(self, element):
        pool = DimensionDataPool(
            id=element.get('id'),
            name=findtext(element, 'name', TYPES_URN),
            status=findtext(element, 'state', TYPES_URN),
            description=findtext(element, 'description', TYPES_URN),
            load_balance_method=findtext(element, 'loadBalanceMethod',
                                         TYPES_URN),
            health_monitor_id=findtext(element, 'healthMonitorId', TYPES_URN),
            service_down_action=findtext(element, 'serviceDownAction',
                                         TYPES_URN),
            slow_ramp_time=findtext(element, 'slowRampTime', TYPES_URN),
        )
        return pool
コード例 #5
0
class CloudStackLBDriver(CloudStackDriverMixIn, Driver):
    """Driver for CloudStack load balancers."""

    api_name = 'cloudstack_lb'

    _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 list_protocols(self):
        """We don't actually have any protocol awareness beyond TCP."""
        return ['tcp']

    def list_balancers(self):
        balancers = self._sync_request('listLoadBalancerRules')
        balancers = balancers.get('loadbalancerrule', [])
        return [self._to_balancer(balancer) for balancer in balancers]

    def get_balancer(self, balancer_id):
        balancer = self._sync_request('listLoadBalancerRules', id=balancer_id)
        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):
        if location is None:
            locations = self._sync_request('listZones')
            location = locations['zone'][0]['id']
        else:
            location = location.id
        if private_port is None:
            private_port = port

        result = self._async_request('associateIpAddress', zoneid=location)
        public_ip = result['ipaddress']

        result = self._sync_request(
            'createLoadBalancerRule',
            algorithm=self._ALGORITHM_TO_VALUE_MAP[algorithm],
            name=name,
            privateport=private_port,
            publicport=port,
            publicipid=public_ip['id'],
        )

        balancer = self._to_balancer(result['loadbalancer'])

        for member in members:
            balancer.attach_member(member)

        return balancer

    def destroy_balancer(self, balancer):
        self._async_request('deleteLoadBalancerRule', id=balancer.id)
        self._async_request('disassociateIpAddress',
                            id=balancer.ex_public_ip_id)

    def balancer_attach_member(self, balancer, member):
        member.port = balancer.ex_private_port
        self._async_request('assignToLoadBalancerRule',
                            id=balancer.id,
                            virtualmachineids=member.id)
        return True

    def balancer_detach_member(self, balancer, member):
        self._async_request('removeFromLoadBalancerRule',
                            id=balancer.id,
                            virtualmachineids=member.id)
        return True

    def balancer_list_members(self, balancer):
        members = self._sync_request('listLoadBalancerRuleInstances',
                                     id=balancer.id)
        members = members['loadbalancerruleinstance']
        return [self._to_member(m, balancer.ex_private_port) 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):
        return Member(id=obj['id'], ip=obj['nic'][0]['ipaddress'], port=port)
コード例 #6
0
ファイル: cloudstack.py プロジェクト: schevalier/libcloud
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('listLoadBalancerRules')
        balancers = balancers.get('loadbalancerrule', [])
        return [self._to_balancer(balancer) for balancer in balancers]

    def get_balancer(self, balancer_id):
        balancer = self._sync_request('listLoadBalancerRules', id=balancer_id)
        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):
        """
        @inherits: :class:`Driver.create_balancer`

        :param location: Location
        :type  location: :class:`NodeLocation`

        :param private_port: Private port
        :type  private_port: ``int``
        """
        if location is None:
            locations = self._sync_request('listZones')
            location = locations['zone'][0]['id']
        else:
            location = location.id
        if private_port is None:
            private_port = port

        result = self._async_request('associateIpAddress', zoneid=location)
        public_ip = result['ipaddress']

        result = self._sync_request(
            'createLoadBalancerRule',
            algorithm=self._ALGORITHM_TO_VALUE_MAP[algorithm],
            name=name,
            privateport=private_port,
            publicport=port,
            publicipid=public_ip['id'],
        )

        balancer = self._to_balancer(result['loadbalancer'])

        for member in members:
            balancer.attach_member(member)

        return balancer

    def destroy_balancer(self, balancer):
        self._async_request('deleteLoadBalancerRule', id=balancer.id)
        self._async_request('disassociateIpAddress',
                            id=balancer.ex_public_ip_id)

    def balancer_attach_member(self, balancer, member):
        member.port = balancer.ex_private_port
        self._async_request('assignToLoadBalancerRule', id=balancer.id,
                            virtualmachineids=member.id)
        return True

    def balancer_detach_member(self, balancer, member):
        self._async_request('removeFromLoadBalancerRule', id=balancer.id,
                            virtualmachineids=member.id)
        return True

    def balancer_list_members(self, balancer):
        members = self._sync_request('listLoadBalancerRuleInstances',
                                     id=balancer.id)
        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
        )
コード例 #7
0
ファイル: rackspace.py プロジェクト: mlibcloud/mlibcloud
class RackspaceLBDriver(Driver):
    connectionCls = RackspaceConnection
    api_name = 'rackspace_lb'
    name = 'Rackspace LB'

    LB_STATE_MAP = {'ACTIVE': State.RUNNING, 'BUILD': State.PENDING}
    _VALUE_TO_ALGORITHM_MAP = {
        'RANDOM': Algorithm.RANDOM,
        'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
        'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS
    }
    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_protocols(self):
        return self._to_protocols(
            self.connection.request('/loadbalancers/protocols').object)

    def list_balancers(self, ex_member_address=None):
        """
        @param ex_member_address: Optional IP address of the attachment member.
                                  If provided, only the load balancers which
                                  have this member attached will be returned.
        @type ex_member_address: C{str}
        """
        params = {}

        if ex_member_address:
            params['nodeaddress'] = ex_member_address

        return self._to_balancers(
            self.connection.request('/loadbalancers', params=params).object)

    def create_balancer(self,
                        name,
                        members,
                        protocol='http',
                        port=80,
                        algorithm=DEFAULT_ALGORITHM):
        algorithm = self._algorithm_to_value(algorithm)

        balancer_object = {
            "loadBalancer": {
                "name":
                name,
                "port":
                port,
                "algorithm":
                algorithm,
                "protocol":
                protocol.upper(),
                "virtualIps": [{
                    "type": "PUBLIC"
                }],
                "nodes": [{
                    "address": member.ip,
                    "port": member.port,
                    "condition": "ENABLED"
                } for member in members],
            }
        }

        resp = self.connection.request('/loadbalancers',
                                       method='POST',
                                       data=json.dumps(balancer_object))
        return self._to_balancer(resp.object["loadBalancer"])

    def destroy_balancer(self, balancer):
        uri = '/loadbalancers/%s' % (balancer.id)
        resp = self.connection.request(uri, method='DELETE')

        return resp.status == 202

    def get_balancer(self, balancer_id):
        uri = '/loadbalancers/%s' % (balancer_id)
        resp = self.connection.request(uri)

        return self._to_balancer(resp.object["loadBalancer"])

    def balancer_attach_member(self, balancer, member):
        ip = member.ip
        port = member.port

        member_object = {
            "nodes": [{
                "port": port,
                "address": ip,
                "condition": "ENABLED"
            }]
        }

        uri = '/loadbalancers/%s/nodes' % (balancer.id)
        resp = self.connection.request(uri,
                                       method='POST',
                                       data=json.dumps(member_object))
        return self._to_members(resp.object)[0]

    def balancer_detach_member(self, balancer, member):
        # Loadbalancer always needs to have at least 1 member.
        # Last member cannot be detached. You can only disable it or destroy the
        # balancer.
        uri = '/loadbalancers/%s/nodes/%s' % (balancer.id, member.id)
        resp = self.connection.request(uri, method='DELETE')

        return resp.status == 202

    def balancer_list_members(self, balancer):
        uri = '/loadbalancers/%s/nodes' % (balancer.id)
        return self._to_members(self.connection.request(uri).object)

    def _to_protocols(self, object):
        protocols = []
        for item in object["protocols"]:
            protocols.append(item['name'].lower())
        return protocols

    def _to_balancers(self, object):
        return [self._to_balancer(el) for el in object["loadBalancers"]]

    def _to_balancer(self, el):
        ip = None
        port = None

        if 'virtualIps' in el:
            ip = el["virtualIps"][0]["address"]

        if 'port' in el:
            port = el["port"]

        lb = LoadBalancer(id=el["id"],
                          name=el["name"],
                          state=self.LB_STATE_MAP.get(el["status"],
                                                      State.UNKNOWN),
                          ip=ip,
                          port=port,
                          driver=self.connection.driver)
        return lb

    def _to_members(self, object):
        return [self._to_member(el) for el in object["nodes"]]

    def _to_member(self, el):
        lbmember = Member(id=el["id"], ip=el["address"], port=el["port"])
        return lbmember
コード例 #8
0
class BrightboxLBDriver(Driver):
    connectionCls = BrightboxConnection

    name = 'Brightbox'
    website = 'http://www.brightbox.co.uk/'

    LB_STATE_MAP = {
        'creating': State.PENDING,
        'active': State.RUNNING,
        'deleting': State.UNKNOWN,
        'deleted': State.UNKNOWN,
        'failing': State.UNKNOWN,
        'failed': State.UNKNOWN,
    }

    _VALUE_TO_ALGORITHM_MAP = {
        'round-robin': Algorithm.ROUND_ROBIN,
        'least-connections': Algorithm.LEAST_CONNECTIONS
    }

    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_protocols(self):
        return ['tcp', 'http']

    def list_balancers(self):
        data = self.connection.request('/%s/load_balancers' % API_VERSION) \
                   .object

        return list(map(self._to_balancer, data))

    def create_balancer(self, name, port, protocol, algorithm, members):
        response = self._post(
            '/%s/load_balancers' % API_VERSION, {
                'name': name,
                'nodes': list(map(self._member_to_node, members)),
                'policy': self._algorithm_to_value(algorithm),
                'listeners': [{
                    'in': port,
                    'out': port,
                    'protocol': protocol
                }],
                'healthcheck': {
                    'type': protocol,
                    'port': port
                }
            })

        return self._to_balancer(response.object)

    def destroy_balancer(self, balancer):
        response = self.connection.request('/%s/load_balancers/%s' %
                                           (API_VERSION, balancer.id),
                                           method='DELETE')

        return response.status == httplib.ACCEPTED

    def get_balancer(self, balancer_id):
        data = self.connection.request('/%s/load_balancers/%s' %
                                       (API_VERSION, balancer_id)).object
        return self._to_balancer(data)

    def balancer_attach_compute_node(self, balancer, node):
        return self.balancer_attach_member(balancer, node)

    def balancer_attach_member(self, balancer, member):
        path = '/%s/load_balancers/%s/add_nodes' % (API_VERSION, balancer.id)

        self._post(path, {'nodes': [self._member_to_node(member)]})

        return member

    def balancer_detach_member(self, balancer, member):
        path = '/%s/load_balancers/%s/remove_nodes' % (API_VERSION,
                                                       balancer.id)

        response = self._post(path, {'nodes': [self._member_to_node(member)]})

        return response.status == httplib.ACCEPTED

    def balancer_list_members(self, balancer):
        path = '/%s/load_balancers/%s' % (API_VERSION, balancer.id)

        data = self.connection.request(path).object

        def func(data):
            return self._node_to_member(data, balancer)

        return list(map(func, data['nodes']))

    def _post(self, path, data={}):
        headers = {'Content-Type': 'application/json'}

        return self.connection.request(path,
                                       data=data,
                                       headers=headers,
                                       method='POST')

    def _to_balancer(self, data):
        return LoadBalancer(id=data['id'],
                            name=data['name'],
                            state=self.LB_STATE_MAP.get(
                                data['status'], State.UNKNOWN),
                            ip=self._public_ip(data),
                            port=data['listeners'][0]['in'],
                            driver=self.connection.driver)

    def _member_to_node(self, member):
        return {'node': member.id}

    def _node_to_member(self, data, balancer):
        return Member(id=data['id'], ip=None, port=None, balancer=balancer)

    def _public_ip(self, data):
        if len(data['cloud_ips']) > 0:
            ip = data['cloud_ips'][0]['public_ip']
        else:
            ip = None

        return ip
コード例 #9
0
class RackspaceLBDriver(Driver):
    connectionCls = RackspaceConnection
    api_name = 'rackspace_lb'
    name = 'Rackspace LB'

    LB_STATE_MAP = {
        'ACTIVE': State.RUNNING,
        'BUILD': State.PENDING,
        'ERROR': State.ERROR,
        'DELETED': State.DELETED,
        'PENDING_UPDATE': State.PENDING,
        'PENDING_DELETE': State.PENDING
    }

    LB_MEMBER_CONDITION_MAP = {
        'ENABLED': MemberCondition.ENABLED,
        'DISABLED': MemberCondition.DISABLED,
        'DRAINING': MemberCondition.DRAINING
    }

    _VALUE_TO_ALGORITHM_MAP = {
        'RANDOM': Algorithm.RANDOM,
        'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
        'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
        'WEIGHTED_ROUND_ROBIN': Algorithm.WEIGHTED_ROUND_ROBIN,
        'WEIGHTED_LEAST_CONNECTIONS': Algorithm.WEIGHTED_LEAST_CONNECTIONS
    }

    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_protocols(self):
        return self._to_protocols(
            self.connection.request('/loadbalancers/protocols').object)

    def list_balancers(self, ex_member_address=None):
        """
        @param ex_member_address: Optional IP address of the attachment member.
                                  If provided, only the load balancers which
                                  have this member attached will be returned.
        @type ex_member_address: C{str}
        """
        params = {}

        if ex_member_address:
            params['nodeaddress'] = ex_member_address

        return self._to_balancers(
            self.connection.request('/loadbalancers', params=params).object)

    def create_balancer(self,
                        name,
                        members,
                        protocol='http',
                        port=80,
                        algorithm=DEFAULT_ALGORITHM):
        balancer_attrs = self._kwargs_to_mutable_attrs(name=name,
                                                       protocol=protocol,
                                                       port=port,
                                                       algorithm=algorithm)

        balancer_attrs.update({
            "virtualIps": [{
                "type": "PUBLIC"
            }],
            "nodes": [{
                "address": member.ip,
                "port": member.port,
                "condition": "ENABLED"
            } for member in members],
        })
        balancer_object = {"loadBalancer": balancer_attrs}

        resp = self.connection.request('/loadbalancers',
                                       method='POST',
                                       data=json.dumps(balancer_object))
        return self._to_balancer(resp.object["loadBalancer"])

    def destroy_balancer(self, balancer):
        uri = '/loadbalancers/%s' % (balancer.id)
        resp = self.connection.request(uri, method='DELETE')

        return resp.status == httplib.ACCEPTED

    def get_balancer(self, balancer_id):
        uri = '/loadbalancers/%s' % (balancer_id)
        resp = self.connection.request(uri)

        return self._to_balancer(resp.object["loadBalancer"])

    def balancer_attach_member(self, balancer, member):
        ip = member.ip
        port = member.port

        member_object = {
            "nodes": [{
                "port": port,
                "address": ip,
                "condition": "ENABLED"
            }]
        }

        uri = '/loadbalancers/%s/nodes' % (balancer.id)
        resp = self.connection.request(uri,
                                       method='POST',
                                       data=json.dumps(member_object))
        return self._to_members(resp.object)[0]

    def balancer_detach_member(self, balancer, member):
        # Loadbalancer always needs to have at least 1 member.
        # Last member cannot be detached. You can only disable it or destroy
        # the balancer.
        uri = '/loadbalancers/%s/nodes/%s' % (balancer.id, member.id)
        resp = self.connection.request(uri, method='DELETE')

        return resp.status == httplib.ACCEPTED

    def balancer_list_members(self, balancer):
        uri = '/loadbalancers/%s/nodes' % (balancer.id)
        return self._to_members(self.connection.request(uri).object)

    def update_balancer(self, balancer, **kwargs):
        attrs = self._kwargs_to_mutable_attrs(**kwargs)
        resp = self.connection.async_request(action='/loadbalancers/%s' %
                                             balancer.id,
                                             method='PUT',
                                             data=json.dumps(attrs))
        return self._to_balancer(resp.object["loadBalancer"])

    def ex_update_balancer_no_poll(self, balancer, **kwargs):
        attrs = self._kwargs_to_mutable_attrs(**kwargs)
        resp = self.connection.request(action='/loadbalancers/%s' %
                                       balancer.id,
                                       method='PUT',
                                       data=json.dumps(attrs))
        return resp.status == httplib.ACCEPTED

    def ex_list_algorithm_names(self):
        """
        Lists algorithms supported by the API.  Returned as strings because
        this list may change in the future.
        """
        response = self.connection.request('/loadbalancers/algorithms')
        return [a["name"].upper() for a in response.object["algorithms"]]

    def ex_get_balancer_error_page(self, balancer):
        uri = '/loadbalancers/%s/errorpage' % (balancer.id)
        resp = self.connection.request(uri)

        return resp.object["errorpage"]["content"]

    def ex_balancer_access_list(self, balancer):
        uri = '/loadbalancers/%s/accesslist' % (balancer.id)
        resp = self.connection.request(uri)

        return [self._to_access_rule(el) for el in resp.object["accessList"]]

    def _to_protocols(self, object):
        protocols = []
        for item in object["protocols"]:
            protocols.append(item['name'].lower())
        return protocols

    def _to_balancers(self, object):
        return [self._to_balancer(el) for el in object["loadBalancers"]]

    def _to_balancer(self, el):
        ip = None
        port = None
        sourceAddresses = {}

        if 'virtualIps' in el:
            ip = el["virtualIps"][0]["address"]

        if 'port' in el:
            port = el["port"]

        if 'sourceAddresses' in el:
            sourceAddresses = el['sourceAddresses']

        extra = {
            "publicVips": self._ex_public_virtual_ips(el),
            "privateVips": self._ex_private_virtual_ips(el),
            "ipv6PublicSource": sourceAddresses.get("ipv6Public"),
            "ipv4PublicSource": sourceAddresses.get("ipv4Public"),
            "ipv4PrivateSource": sourceAddresses.get("ipv4Servicenet"),
        }

        if 'protocol' in el:
            extra['protocol'] = el['protocol']

        if 'algorithm' in el and el["algorithm"] in \
            self._VALUE_TO_ALGORITHM_MAP:
            extra["algorithm"] = self._value_to_algorithm(el["algorithm"])

        if 'healthMonitor' in el:
            health_monitor = self._to_health_monitor(el)
            if health_monitor:
                extra["healthMonitor"] = health_monitor

        if 'connectionThrottle' in el:
            extra["connectionThrottle"] = self._to_connection_throttle(el)

        if 'sessionPersistence' in el:
            persistence = el["sessionPersistence"]
            extra["sessionPersistenceType"] = \
                    persistence.get("persistenceType")

        if 'connectionLogging' in el:
            logging = el["connectionLogging"]
            extra["connectionLoggingEnabled"] = logging.get("enabled")

        if 'nodes' in el:
            extra['members'] = self._to_members(el)

        if 'created' in el:
            extra['created'] = self._iso_to_datetime(el['created']['time'])

        if 'updated' in el:
            extra['updated'] = self._iso_to_datetime(el['updated']['time'])

        return LoadBalancer(id=el["id"],
                            name=el["name"],
                            state=self.LB_STATE_MAP.get(
                                el["status"], State.UNKNOWN),
                            ip=ip,
                            port=port,
                            driver=self.connection.driver,
                            extra=extra)

    def _to_members(self, object):
        return [self._to_member(el) for el in object["nodes"]]

    def _to_member(self, el):
        extra = {}
        if 'weight' in el:
            extra['weight'] = el["weight"]

        if 'condition' in el and el['condition'] in \
           self.LB_MEMBER_CONDITION_MAP:
            extra['condition'] = \
                    self.LB_MEMBER_CONDITION_MAP.get(el["condition"])

        if 'status' in el:
            extra['status'] = el["status"]

        lbmember = Member(id=el["id"],
                          ip=el["address"],
                          port=el["port"],
                          extra=extra)
        return lbmember

    def _kwargs_to_mutable_attrs(self, **attrs):
        update_attrs = {}
        if "name" in attrs:
            update_attrs['name'] = attrs['name']

        if "algorithm" in attrs:
            algorithm_value = self._algorithm_to_value(attrs['algorithm'])
            update_attrs['algorithm'] = algorithm_value

        if "protocol" in attrs:
            update_attrs['protocol'] = attrs['protocol'].upper()

        if "port" in attrs:
            update_attrs['port'] = int(attrs['port'])

        return update_attrs

    def _ex_private_virtual_ips(self, el):
        if not 'virtualIps' in el:
            return None

        servicenet_vips = [
            ip for ip in el['virtualIps'] if ip['type'] == 'SERVICENET'
        ]
        return [vip["address"] for vip in servicenet_vips]

    def _ex_public_virtual_ips(self, el):
        if not 'virtualIps' in el:
            return None

        public_vips = [ip for ip in el['virtualIps'] if ip['type'] == 'PUBLIC']
        return [vip["address"] for vip in public_vips]

    def _to_health_monitor(self, el):
        health_monitor_data = el["healthMonitor"]

        type = health_monitor_data.get("type")
        delay = health_monitor_data.get("delay")
        timeout = health_monitor_data.get("timeout")
        attempts_before_deactivation = \
                health_monitor_data.get("attemptsBeforeDeactivation")

        if type == "CONNECT":
            return RackspaceHealthMonitor(
                type=type,
                delay=delay,
                timeout=timeout,
                attempts_before_deactivation=attempts_before_deactivation)

        if type == "HTTP" or type == "HTTPS":
            return RackspaceHTTPHealthMonitor(
                type=type,
                delay=delay,
                timeout=timeout,
                attempts_before_deactivation=attempts_before_deactivation,
                path=health_monitor_data.get("path"),
                status_regex=health_monitor_data.get("statusRegex"),
                body_regex=health_monitor_data.get("bodyRegex"))

        return None

    def _to_connection_throttle(self, el):
        connection_throttle_data = el["connectionThrottle"]

        min_connections = connection_throttle_data.get("minConnections")
        max_connections = connection_throttle_data.get("maxConnections")
        max_connection_rate = connection_throttle_data.get("maxConnectionRate")
        rate_interval = connection_throttle_data.get("rateInterval")

        return RackspaceConnectionThrottle(
            min_connections=min_connections,
            max_connections=max_connections,
            max_connection_rate=max_connection_rate,
            rate_interval_seconds=rate_interval)

    def _to_access_rule(self, el):
        return RackspaceAccessRule(id=el.get("id"),
                                   rule_type=self._to_access_rule_type(
                                       el.get("type")),
                                   address=el.get("address"))

    def _to_access_rule_type(self, type):
        if type == "ALLOW":
            return RackspaceAccessRuleType.ALLOW
        elif type == "DENY":
            return RackspaceAccessRuleType.DENY

    def _iso_to_datetime(self, isodate):
        date_formats = ('%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S%z')
        date = None

        for date_format in date_formats:
            try:
                date = datetime.strptime(isodate, date_format)
            except ValueError:
                pass

            if date:
                break

        return date
コード例 #10
0
ファイル: gogrid.py プロジェクト: wandera/libcloud
class GoGridLBDriver(BaseGoGridDriver, Driver):
    connectionCls = GoGridLBConnection
    api_name = "gogrid_lb"
    name = "GoGrid LB"
    website = "http://www.gogrid.com/"

    LB_STATE_MAP = {"On": State.RUNNING, "Unknown": State.UNKNOWN}
    _VALUE_TO_ALGORITHM_MAP = {
        "round robin": Algorithm.ROUND_ROBIN,
        "least connect": Algorithm.LEAST_CONNECTIONS,
    }
    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def __init__(self, *args, **kwargs):
        """
        @inherits: :class:`Driver.__init__`
        """
        super(GoGridLBDriver, self).__init__(*args, **kwargs)

    def list_protocols(self):
        # GoGrid only supports http
        return ["http"]

    def list_balancers(self):
        return self._to_balancers(
            self.connection.request("/api/grid/loadbalancer/list").object)

    def ex_create_balancer_nowait(self,
                                  name,
                                  members,
                                  protocol="http",
                                  port=80,
                                  algorithm=DEFAULT_ALGORITHM):
        """
        @inherits: :class:`Driver.create_balancer`
        """
        algorithm = self._algorithm_to_value(algorithm)

        params = {
            "name": name,
            "loadbalancer.type": algorithm,
            "virtualip.ip": self._get_first_ip(),
            "virtualip.port": port,
        }
        params.update(self._members_to_params(members))

        resp = self.connection.request("/api/grid/loadbalancer/add",
                                       method="GET",
                                       params=params)
        return self._to_balancers(resp.object)[0]

    def create_balancer(self,
                        name,
                        members,
                        protocol="http",
                        port=80,
                        algorithm=DEFAULT_ALGORITHM):
        balancer = self.ex_create_balancer_nowait(name, members, protocol,
                                                  port, algorithm)

        timeout = 60 * 20
        waittime = 0
        interval = 2 * 15

        if balancer.id is not None:
            return balancer
        else:
            while waittime < timeout:
                balancers = self.list_balancers()

                for i in balancers:
                    if i.name == balancer.name and i.id is not None:
                        return i

                waittime += interval
                time.sleep(interval)

        raise Exception("Failed to get id")

    def destroy_balancer(self, balancer):
        try:
            resp = self.connection.request(
                "/api/grid/loadbalancer/delete",
                method="POST",
                params={"id": balancer.id},
            )
        except Exception as e:
            if "Update request for LoadBalancer" in str(e):
                raise LibcloudLBImmutableError(
                    "Cannot delete immutable object", GoGridLBDriver)
            else:
                raise

        return resp.status == 200

    def get_balancer(self, **kwargs):
        params = {}

        try:
            params["name"] = kwargs["ex_balancer_name"]
        except KeyError:
            balancer_id = kwargs["balancer_id"]
            params["id"] = balancer_id

        resp = self.connection.request("/api/grid/loadbalancer/get",
                                       params=params)

        return self._to_balancers(resp.object)[0]

    def balancer_attach_member(self, balancer, member):
        members = self.balancer_list_members(balancer)
        members.append(member)

        params = {"id": balancer.id}

        params.update(self._members_to_params(members))

        resp = self._update_balancer(params)
        return [
            m for m in self._to_members(resp.object["list"][0]["realiplist"],
                                        balancer) if m.ip == member.ip
        ][0]

    def balancer_detach_member(self, balancer, member):
        members = self.balancer_list_members(balancer)

        remaining_members = [n for n in members if n.id != member.id]

        params = {"id": balancer.id}
        params.update(self._members_to_params(remaining_members))

        resp = self._update_balancer(params)

        return resp.status == 200

    def balancer_list_members(self, balancer):
        resp = self.connection.request("/api/grid/loadbalancer/get",
                                       params={"id": balancer.id})
        return self._to_members(resp.object["list"][0]["realiplist"], balancer)

    def _update_balancer(self, params):
        try:
            return self.connection.request("/api/grid/loadbalancer/edit",
                                           method="POST",
                                           params=params)
        except Exception as e:
            if "Update already pending" in str(e):
                raise LibcloudLBImmutableError("Balancer is immutable",
                                               GoGridLBDriver)

            raise LibcloudError(value="Exception: %s" % str(e), driver=self)

    def _members_to_params(self, members):
        """
        Helper method to convert list of :class:`Member` objects
        to GET params.

        """

        params = {}

        i = 0
        for member in members:
            params["realiplist.%s.ip" % i] = member.ip
            params["realiplist.%s.port" % i] = member.port
            i += 1

        return params

    def _to_balancers(self, object):
        return [self._to_balancer(el) for el in object["list"]]

    def _to_balancer(self, el):
        lb = LoadBalancer(
            id=el.get("id"),
            name=el["name"],
            state=self.LB_STATE_MAP.get(el["state"]["name"], State.UNKNOWN),
            ip=el["virtualip"]["ip"]["ip"],
            port=el["virtualip"]["port"],
            driver=self.connection.driver,
        )
        return lb

    def _to_members(self, object, balancer=None):
        return [self._to_member(el, balancer) for el in object]

    def _to_member(self, el, balancer=None):
        member = Member(id=el["ip"]["id"],
                        ip=el["ip"]["ip"],
                        port=el["port"],
                        balancer=balancer)
        return member
コード例 #11
0
ファイル: softlayer.py プロジェクト: wandera/libcloud
class SoftlayerLBDriver(Driver):
    name = "Softlayer Load Balancing"
    website = "http://www.softlayer.com/"
    connectionCls = SoftLayerConnection

    _VALUE_TO_ALGORITHM_MAP = {
        "ROUND_ROBIN": Algorithm.ROUND_ROBIN,
        "LEAST_CONNECTIONS": Algorithm.LEAST_CONNECTIONS,
        "SHORTEST_RESPONSE": Algorithm.SHORTEST_RESPONSE,
        "PERSISTENT_IP": Algorithm.PERSISTENT_IP,
    }

    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_balancers(self):
        mask = {
            "adcLoadBalancers": {
                "ipAddress": "",
                "loadBalancerHardware": {
                    "datacenter": ""
                },
                "virtualServers": {
                    "serviceGroups": {
                        "routingMethod": "",
                        "routingType": "",
                        "services": {
                            "ipAddress": ""
                        },
                    }
                },
            }
        }

        res = self.connection.request("SoftLayer_Account",
                                      "getAdcLoadBalancers",
                                      object_mask=mask).object

        return [self._to_balancer(lb) for lb in res]

    def get_balancer(self, balancer_id):
        balancers = self.list_balancers()
        balancer = find(balancers, lambda b: b.id == balancer_id)
        if not balancer:
            raise LibcloudError(value="No balancer found for id: %s" %
                                balancer_id,
                                driver=self)
        return balancer

    def list_protocols(self):
        """
        Return a list of supported protocols.

        :rtype: ``list`` of ``str``
        """
        return ["dns", "ftp", "http", "https", "tcp", "udp"]

    def balancer_list_members(self, balancer):
        lb = self._get_balancer_model(balancer.id)
        members = []
        vs = self._locate_service_group(lb, balancer.port)
        if vs:
            if vs["serviceGroups"]:
                srvgrp = vs["serviceGroups"][0]
                members = [
                    self._to_member(srv, balancer)
                    for srv in srvgrp["services"]
                ]

        return members

    def balancer_attach_member(self, balancer, member):
        lb = self._get_balancer_model(balancer.id)
        vs = self._locate_service_group(lb, balancer.port)
        if not vs:
            raise LibcloudError(
                value="No service_group found for balancer "
                "port: %s" % balancer.port,
                driver=self,
            )

        if vs["serviceGroups"]:
            services = vs["serviceGroups"][0]["services"]
            services.append(self._to_service_template(member.ip, member.port))

        self.connection.request(lb_service, "editObject", lb, id=balancer.id)

        return [m for m in balancer.list_members() if m.ip == member.ip][0]

    def balancer_detach_member(self, balancer, member):
        svc_lbsrv = ("SoftLayer_Network_Application_Delivery_Controller_"
                     "LoadBalancer_Service")

        self.connection.request(svc_lbsrv, "deleteObject", id=member.id)
        return True

    def destroy_balancer(self, balancer):
        res_billing = self.connection.request(lb_service,
                                              "getBillingItem",
                                              id=balancer.id).object

        self.connection.request("SoftLayer_Billing_Item",
                                "cancelService",
                                id=res_billing["id"])
        return True

    def ex_list_balancer_packages(self):
        """
        Retrieves the available local load balancer packages.

        :rtype: ``list`` of :class:`LBPackage`
        """
        mask = {"prices": ""}
        res = self.connection.request("SoftLayer_Product_Package",
                                      "getItems",
                                      id=0,
                                      object_mask=mask).object

        res_lb_pkgs = [
            r for r in res if r["description"].find("Load Balancer") != -1
        ]
        res_lb_pkgs = [
            r for r in res_lb_pkgs if not r["description"].startswith("Global")
        ]

        return [self._to_lb_package(r) for r in res_lb_pkgs]

    def ex_place_balancer_order(self, package, location):
        """
        Places an order for a local loadbalancer in the specified
        location.

        :param package: The package to create the loadbalancer from.
        :type package: :class:`LBPackage`

        :param string location: The location (datacenter) to create the
                                loadbalancer.
        :type location: :class:`NodeLocation`

        :return: ``True`` if ex_place_balancer_order was successful.
        :rtype: ``bool``
        """
        data = {
            "complexType": "SoftLayer_Container_Product_Order_Network_"
            "LoadBalancer",
            "quantity": 1,
            "packageId": 0,
            "location": self._get_location(location.id),
            "prices": [{
                "id": package.price_id
            }],
        }

        self.connection.request("SoftLayer_Product_Order", "placeOrder", data)
        return True

    def ex_configure_load_balancer(
        self,
        balancer,
        port=80,
        protocol="http",
        algorithm=DEFAULT_ALGORITHM,
        ex_allocation=100,
    ):
        """
        Configure the loadbalancer by adding it with a front-end port (aka
        a service group in the Softlayer loadbalancer model).

        Softlayer loadbalancer may be defined with multiple service
        groups (front-end ports) each defined with a unique port number.

        :param balancer: The loadbalancer.
        :type  balancer: :class:`LoadBalancer`

        :param port: Port of the service group, defaults to 80.
        :type  port: ``int``

        :param protocol: Loadbalancer protocol, defaults to http.
        :type  protocol: ``str``

        :param algorithm: Load balancing algorithm, defaults to
                            Algorithm.ROUND_ROBIN
        :type  algorithm: :class:`Algorithm`

        :param ex_allocation: The percentage of the total connection
                              allocations to allocate for this group.
        :type  ex_allocation: ``int``

        :return: ``True`` if ex_add_service_group was successful.
        :rtype: ``bool``
        """
        _types = self._get_routing_types()
        _methods = self._get_routing_methods()

        rt = find(_types, lambda t: t["keyname"] == protocol.upper())
        if not rt:
            raise LibcloudError(value="Invalid protocol %s" % protocol,
                                driver=self)

        value = self._algorithm_to_value(algorithm)
        meth = find(_methods, lambda m: m["keyname"] == value)
        if not meth:
            raise LibcloudError(value="Invalid algorithm %s" % algorithm,
                                driver=self)

        service_group_template = {
            "port":
            port,
            "allocation":
            ex_allocation,
            "serviceGroups": [{
                "routingTypeId": rt["id"],
                "routingMethodId": meth["id"]
            }],
        }

        lb = self._get_balancer_model(balancer.id)
        if len(lb["virtualServers"]) > 0:
            port = lb["virtualServers"][0]["port"]
            raise LibcloudError(
                value="Loadbalancer already configured with "
                "a service group (front-end port)" % port,
                driver=self,
            )

        lb["virtualServers"].append(service_group_template)
        self.connection.request(lb_service, "editObject", lb, id=balancer.id)
        return True

    def _get_balancer_model(self, balancer_id):
        """
        Retrieve Softlayer loadbalancer model.
        """
        lb_mask = {
            "virtualServers": {
                "serviceGroups": {
                    "services": {
                        "ipAddress": "",
                        "groupReferences": ""
                    }
                }
            }
        }

        lb_res = self.connection.request(lb_service,
                                         "getObject",
                                         object_mask=lb_mask,
                                         id=balancer_id).object
        return lb_res

    def _locate_service_group(self, lb, port):
        """
        Locate service group with given port.

        Return virtualServers (vs) entry whose port matches the
        supplied parameter port. For a negative port, just return
        the first vs entry.
        None is returned if no match found.

        :param lb: Softlayer loadbalancer model.
        :type lb: ``dict``

        :param port: loadbalancer front-end port.
        :type port: ``int``

        :return: Matched entry in the virtualServers array of the supplied
        model.
        :rtype: ``dict``
        """
        vs = None
        if port < 0:
            vs = lb["virtualServers"][0] if lb["virtualServers"] else None
        else:
            vs = find(lb["virtualServers"], lambda v: v["port"] == port)

        return vs

    def _get_routing_types(self):
        svc_rtype = ("SoftLayer_Network_Application_Delivery_Controller_"
                     "LoadBalancer_Routing_Type")

        return self.connection.request(svc_rtype, "getAllObjects").object

    def _get_routing_methods(self):
        svc_rmeth = ("SoftLayer_Network_Application_Delivery_Controller_"
                     "LoadBalancer_Routing_Method")

        return self.connection.request(svc_rmeth, "getAllObjects").object

    def _get_location(self, location_id):
        res = self.connection.request("SoftLayer_Location_Datacenter",
                                      "getDatacenters").object

        dcenter = find(res, lambda d: d["name"] == location_id)
        if not dcenter:
            raise LibcloudError(value="Invalid value %s" % location_id,
                                driver=self)
        return dcenter["id"]

    def _get_ipaddress(self, ip):
        svc_ipaddress = "SoftLayer_Network_Subnet_IpAddress"

        return self.connection.request(svc_ipaddress, "getByIpAddress",
                                       ip).object

    def _to_lb_package(self, pkg):
        try:
            price_id = pkg["prices"][0]["id"]
        except Exception:
            price_id = -1

        capacity = int(pkg.get("capacity", 0))
        return LBPackage(
            id=pkg["id"],
            name=pkg["keyName"],
            description=pkg["description"],
            price_id=price_id,
            capacity=capacity,
        )

    def _to_service_template(self, ip, port):
        """
        Builds single member entry in Softlayer loadbalancer model
        """
        template = {
            "enabled": 1,  # enable the service
            "port": port,  # back-end port
            "ipAddressId": self._get_ipaddress(ip)["id"],
            "healthChecks": [{
                "healthCheckTypeId": 21
            }],  # default health check
            "groupReferences": [{
                "weight": 1
            }],
        }

        return template

    def _to_balancer(self, lb):
        ipaddress = lb["ipAddress"]["ipAddress"]

        extra = {}
        extra["connection_limit"] = lb["connectionLimit"]
        extra["ssl_active"] = lb["sslActiveFlag"]
        extra["ssl_enabled"] = lb["sslEnabledFlag"]
        extra["ha"] = lb["highAvailabilityFlag"]
        extra["datacenter"] = lb["loadBalancerHardware"][0]["datacenter"][
            "name"]

        # In Softlayer, there could be multiple group of members (aka service
        # groups), so retrieve the first one
        vs = self._locate_service_group(lb, -1)
        if vs:
            port = vs["port"]
            if vs["serviceGroups"]:
                srvgrp = vs["serviceGroups"][0]
                routing_method = srvgrp["routingMethod"]["keyname"]
                routing_type = srvgrp["routingType"]["keyname"]
                try:
                    extra["algorithm"] = self._value_to_algorithm(
                        routing_method)
                except Exception:
                    pass
                extra["protocol"] = routing_type.lower()

        if not vs:
            port = -1

        balancer = LoadBalancer(
            id=lb["id"],
            name="",
            state=State.UNKNOWN,
            ip=ipaddress,
            port=port,
            driver=self.connection.driver,
            extra=extra,
        )

        return balancer

    def _to_member(self, srv, balancer=None):
        svc_id = srv["id"]
        ip = srv["ipAddress"]["ipAddress"]
        port = srv["port"]

        extra = {}
        extra["status"] = srv["status"]
        extra["enabled"] = srv["enabled"]
        return Member(id=svc_id,
                      ip=ip,
                      port=port,
                      balancer=balancer,
                      extra=extra)
コード例 #12
0
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
        )
コード例 #13
0
class BrightboxLBDriver(Driver):
    connectionCls = BrightboxConnection

    name = "Brightbox"
    website = "http://www.brightbox.co.uk/"

    LB_STATE_MAP = {
        "creating": State.PENDING,
        "active": State.RUNNING,
        "deleting": State.UNKNOWN,
        "deleted": State.UNKNOWN,
        "failing": State.UNKNOWN,
        "failed": State.UNKNOWN,
    }

    _VALUE_TO_ALGORITHM_MAP = {
        "round-robin": Algorithm.ROUND_ROBIN,
        "least-connections": Algorithm.LEAST_CONNECTIONS,
    }

    _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)

    def list_protocols(self):
        return ["tcp", "http"]

    def list_balancers(self):
        data = self.connection.request("/%s/load_balancers" % API_VERSION).object

        return list(map(self._to_balancer, data))

    def create_balancer(self, name, port, protocol, algorithm, members):
        response = self._post(
            "/%s/load_balancers" % API_VERSION,
            {
                "name": name,
                "nodes": list(map(self._member_to_node, members)),
                "policy": self._algorithm_to_value(algorithm),
                "listeners": [{"in": port, "out": port, "protocol": protocol}],
                "healthcheck": {"type": protocol, "port": port},
            },
        )

        return self._to_balancer(response.object)

    def destroy_balancer(self, balancer):
        response = self.connection.request(
            "/%s/load_balancers/%s" % (API_VERSION, balancer.id), method="DELETE"
        )

        return response.status == httplib.ACCEPTED

    def get_balancer(self, balancer_id):
        data = self.connection.request(
            "/%s/load_balancers/%s" % (API_VERSION, balancer_id)
        ).object
        return self._to_balancer(data)

    def balancer_attach_compute_node(self, balancer, node):
        return self.balancer_attach_member(balancer, node)

    def balancer_attach_member(self, balancer, member):
        path = "/%s/load_balancers/%s/add_nodes" % (API_VERSION, balancer.id)

        self._post(path, {"nodes": [self._member_to_node(member)]})

        return member

    def balancer_detach_member(self, balancer, member):
        path = "/%s/load_balancers/%s/remove_nodes" % (API_VERSION, balancer.id)

        response = self._post(path, {"nodes": [self._member_to_node(member)]})

        return response.status == httplib.ACCEPTED

    def balancer_list_members(self, balancer):
        path = "/%s/load_balancers/%s" % (API_VERSION, balancer.id)

        data = self.connection.request(path).object

        def func(data):
            return self._node_to_member(data, balancer)

        return list(map(func, data["nodes"]))

    def _post(self, path, data={}):
        headers = {"Content-Type": "application/json"}

        return self.connection.request(path, data=data, headers=headers, method="POST")

    def _to_balancer(self, data):
        return LoadBalancer(
            id=data["id"],
            name=data["name"],
            state=self.LB_STATE_MAP.get(data["status"], State.UNKNOWN),
            ip=self._public_ip(data),
            port=data["listeners"][0]["in"],
            driver=self.connection.driver,
        )

    def _member_to_node(self, member):
        return {"node": member.id}

    def _node_to_member(self, data, balancer):
        return Member(id=data["id"], ip=None, port=None, balancer=balancer)

    def _public_ip(self, data):
        if len(data["cloud_ips"]) > 0:
            ip = data["cloud_ips"][0]["public_ip"]
        else:
            ip = None

        return ip