| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from datetime import datetime |
| |
| from libcloud.utils.py3 import httplib |
| from libcloud.utils.misc import reverse_dict |
| from libcloud.common.base import JsonResponse, PollingConnection |
| from libcloud.common.types import LibcloudError |
| from libcloud.common.openstack import OpenStackDriverMixin |
| from libcloud.common.rackspace import AUTH_URL |
| from libcloud.loadbalancer.base import DEFAULT_ALGORITHM, Driver, Member, Algorithm, LoadBalancer |
| from libcloud.loadbalancer.types import State, MemberCondition |
| from libcloud.compute.drivers.rackspace import RackspaceConnection |
| |
| try: |
| import simplejson as json |
| except ImportError: |
| import json |
| |
| |
| ENDPOINT_ARGS_MAP = { |
| "dfw": { |
| "service_type": "rax:load-balancer", |
| "name": "cloudLoadBalancers", |
| "region": "DFW", |
| }, |
| "ord": { |
| "service_type": "rax:load-balancer", |
| "name": "cloudLoadBalancers", |
| "region": "ORD", |
| }, |
| "iad": { |
| "service_type": "rax:load-balancer", |
| "name": "cloudLoadBalancers", |
| "region": "IAD", |
| }, |
| "lon": { |
| "service_type": "rax:load-balancer", |
| "name": "cloudLoadBalancers", |
| "region": "LON", |
| }, |
| "syd": { |
| "service_type": "rax:load-balancer", |
| "name": "cloudLoadBalancers", |
| "region": "SYD", |
| }, |
| "hkg": { |
| "service_type": "rax:load-balancer", |
| "name": "cloudLoadBalancers", |
| "region": "HKG", |
| }, |
| } |
| |
| |
| class RackspaceResponse(JsonResponse): |
| def parse_body(self): |
| if not self.body: |
| return None |
| return super().parse_body() |
| |
| def success(self): |
| return 200 <= int(self.status) <= 299 |
| |
| |
| class RackspaceHealthMonitor: |
| """ |
| :param type: type of load balancer. currently CONNECT (connection |
| monitoring), HTTP, HTTPS (connection and HTTP |
| monitoring) are supported. |
| :type type: ``str`` |
| |
| :param delay: minimum seconds to wait before executing the health |
| monitor. (Must be between 1 and 3600) |
| :type delay: ``int`` |
| |
| :param timeout: maximum seconds to wait when establishing a |
| connection before timing out. (Must be between 1 |
| and 3600) |
| :type timeout: ``int`` |
| |
| :param attempts_before_deactivation: Number of monitor failures |
| before removing a node from |
| rotation. (Must be between 1 |
| and 10) |
| :type attempts_before_deactivation: ``int`` |
| """ |
| |
| def __init__(self, type, delay, timeout, attempts_before_deactivation): |
| self.type = type |
| self.delay = delay |
| self.timeout = timeout |
| self.attempts_before_deactivation = attempts_before_deactivation |
| |
| def __repr__(self): |
| return ( |
| "<RackspaceHealthMonitor: type=%s, delay=%d, timeout=%d, " |
| "attempts_before_deactivation=%d>" |
| % (self.type, self.delay, self.timeout, self.attempts_before_deactivation) |
| ) |
| |
| def _to_dict(self): |
| return { |
| "type": self.type, |
| "delay": self.delay, |
| "timeout": self.timeout, |
| "attemptsBeforeDeactivation": self.attempts_before_deactivation, |
| } |
| |
| |
| class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor): |
| """ |
| A HTTP health monitor adds extra features to a Rackspace health monitor. |
| |
| :param path: the HTTP path to monitor. |
| :type path: ``str`` |
| |
| :param body_regex: Regular expression used to evaluate the body of |
| the HTTP response. |
| :type body_regex: ``str`` |
| |
| :param status_regex: Regular expression used to evaluate the HTTP |
| status code of the response. |
| :type status_regex: ``str`` |
| """ |
| |
| def __init__( |
| self, |
| type, |
| delay, |
| timeout, |
| attempts_before_deactivation, |
| path, |
| body_regex, |
| status_regex, |
| ): |
| super().__init__(type, delay, timeout, attempts_before_deactivation) |
| self.path = path |
| self.body_regex = body_regex |
| self.status_regex = status_regex |
| |
| def __repr__(self): |
| return ( |
| "<RackspaceHTTPHealthMonitor: type=%s, delay=%d, timeout=%d, " |
| "attempts_before_deactivation=%d, path=%s, body_regex=%s, " |
| "status_regex=%s>" |
| % ( |
| self.type, |
| self.delay, |
| self.timeout, |
| self.attempts_before_deactivation, |
| self.path, |
| self.body_regex, |
| self.status_regex, |
| ) |
| ) |
| |
| def _to_dict(self): |
| super_dict = super()._to_dict() |
| super_dict["path"] = self.path |
| super_dict["statusRegex"] = self.status_regex |
| |
| if self.body_regex: |
| super_dict["bodyRegex"] = self.body_regex |
| |
| return super_dict |
| |
| |
| class RackspaceConnectionThrottle: |
| """ |
| :param min_connections: Minimum number of connections per IP address |
| before applying throttling. |
| :type min_connections: ``int`` |
| |
| :param max_connections: Maximum number of connections per IP address. |
| (Must be between 0 and 100000, 0 allows an |
| unlimited number of connections.) |
| :type max_connections: ``int`` |
| |
| :param max_connection_rate: Maximum number of connections allowed |
| from a single IP address within the |
| given rate_interval_seconds. (Must be |
| between 0 and 100000, 0 allows an |
| unlimited number of connections.) |
| :type max_connection_rate: ``int`` |
| |
| :param rate_interval_seconds: Interval at which the |
| max_connection_rate is enforced. |
| (Must be between 1 and 3600.) |
| :type rate_interval_seconds: ``int`` |
| """ |
| |
| def __init__( |
| self, |
| min_connections, |
| max_connections, |
| max_connection_rate, |
| rate_interval_seconds, |
| ): |
| self.min_connections = min_connections |
| self.max_connections = max_connections |
| self.max_connection_rate = max_connection_rate |
| self.rate_interval_seconds = rate_interval_seconds |
| |
| def __repr__(self): |
| return ( |
| "<RackspaceConnectionThrottle: min_connections=%d, " |
| "max_connections=%d, max_connection_rate=%d, " |
| "rate_interval_seconds=%d>" |
| % ( |
| self.min_connections, |
| self.max_connections, |
| self.max_connection_rate, |
| self.rate_interval_seconds, |
| ) |
| ) |
| |
| def _to_dict(self): |
| return { |
| "maxConnections": self.max_connections, |
| "minConnections": self.min_connections, |
| "maxConnectionRate": self.max_connection_rate, |
| "rateInterval": self.rate_interval_seconds, |
| } |
| |
| |
| class RackspaceAccessRuleType: |
| ALLOW = 0 |
| DENY = 1 |
| |
| _RULE_TYPE_STRING_MAP = {ALLOW: "ALLOW", DENY: "DENY"} |
| |
| |
| class RackspaceAccessRule: |
| """ |
| An access rule allows or denies traffic to a Load Balancer based on the |
| incoming IPs. |
| |
| :param id: Unique identifier to refer to this rule by. |
| :type id: ``str`` |
| |
| :param rule_type: RackspaceAccessRuleType.ALLOW or |
| RackspaceAccessRuleType.DENY. |
| :type id: ``int`` |
| |
| :param address: IP address or cidr (can be IPv4 or IPv6). |
| :type address: ``str`` |
| """ |
| |
| def __init__(self, id=None, rule_type=None, address=None): |
| self.id = id |
| self.rule_type = rule_type |
| self.address = address |
| |
| def _to_dict(self): |
| type_string = RackspaceAccessRuleType._RULE_TYPE_STRING_MAP[self.rule_type] |
| |
| as_dict = {"type": type_string, "address": self.address} |
| |
| if self.id is not None: |
| as_dict["id"] = self.id |
| |
| return as_dict |
| |
| |
| class RackspaceConnection(RackspaceConnection, PollingConnection): |
| responseCls = RackspaceResponse |
| auth_url = AUTH_URL |
| poll_interval = 2 |
| timeout = 80 |
| cache_busting = True |
| |
| def request(self, action, params=None, data="", headers=None, method="GET"): |
| if not headers: |
| headers = {} |
| if not params: |
| params = {} |
| |
| if method in ("POST", "PUT"): |
| headers["Content-Type"] = "application/json" |
| |
| return super().request( |
| action=action, params=params, data=data, method=method, headers=headers |
| ) |
| |
| def get_poll_request_kwargs(self, response, context, request_kwargs): |
| return {"action": request_kwargs["action"], "method": "GET"} |
| |
| def has_completed(self, response): |
| state = response.object["loadBalancer"]["status"] |
| if state == "ERROR": |
| raise LibcloudError("Load balancer entered an ERROR state.", driver=self.driver) |
| |
| return state == "ACTIVE" |
| |
| def encode_data(self, data): |
| return data |
| |
| |
| class RackspaceLBDriver(Driver, OpenStackDriverMixin): |
| connectionCls = RackspaceConnection |
| api_name = "rackspace_lb" |
| name = "Rackspace LB" |
| website = "http://www.rackspace.com/" |
| |
| 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, |
| } |
| |
| CONDITION_LB_MEMBER_MAP = reverse_dict(LB_MEMBER_CONDITION_MAP) |
| |
| _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 __init__( |
| self, |
| key, |
| secret=None, |
| secure=True, |
| host=None, |
| port=None, |
| region="ord", |
| **kwargs, |
| ): |
| ex_force_region = kwargs.pop("ex_force_region", None) |
| if ex_force_region: |
| # For backward compatibility |
| region = ex_force_region |
| OpenStackDriverMixin.__init__(self, **kwargs) |
| super().__init__(key=key, secret=secret, secure=secure, host=host, port=port, region=region) |
| |
| @classmethod |
| def list_regions(cls): |
| return ENDPOINT_ARGS_MAP.keys() |
| |
| def _ex_connection_class_kwargs(self): |
| endpoint_args = ENDPOINT_ARGS_MAP[self.region] |
| kwargs = self.openstack_connection_kwargs() |
| kwargs["get_endpoint_args"] = endpoint_args |
| return kwargs |
| |
| def list_protocols(self): |
| return self._to_protocols(self.connection.request("/loadbalancers/protocols").object) |
| |
| def ex_list_protocols_with_default_ports(self): |
| """ |
| List protocols with default ports. |
| |
| :rtype: ``list`` of ``tuple`` |
| :return: A list of protocols with default ports included. |
| """ |
| return self._to_protocols_with_default_ports( |
| self.connection.request("/loadbalancers/protocols").object |
| ) |
| |
| def list_balancers( |
| self, |
| ex_member_address=None, |
| ex_status=None, |
| ex_changes_since=None, |
| ex_params={}, |
| ): |
| """ |
| @inherits: :class:`Driver.list_balancers` |
| |
| :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: ``str`` |
| |
| :param ex_status: Optional. Filter balancers by status |
| :type ex_status: ``str`` |
| |
| :param ex_changes_since: Optional. List all load balancers that have |
| changed since the specified date/time |
| :type ex_changes_since: ``str`` |
| |
| :param ex_params: Optional. Set parameters to be submitted to the API |
| in the query string |
| :type ex_params: ``dict`` |
| """ |
| |
| params = {} |
| |
| if ex_member_address: |
| params["nodeaddress"] = ex_member_address |
| |
| if ex_status: |
| params["status"] = ex_status |
| |
| if ex_changes_since: |
| params["changes-since"] = ex_changes_since |
| |
| for key, value in ex_params.items(): |
| params[key] = value |
| |
| return self._to_balancers(self.connection.request("/loadbalancers", params=params).object) |
| |
| def create_balancer(self, name, members, protocol="http", port=80, algorithm=DEFAULT_ALGORITHM): |
| return self.ex_create_balancer(name, members, protocol, port, algorithm) |
| |
| def ex_create_balancer( |
| self, |
| name, |
| members, |
| protocol="http", |
| port=80, |
| algorithm=DEFAULT_ALGORITHM, |
| vip="PUBLIC", |
| ): |
| """ |
| Creates a new load balancer instance |
| |
| :param name: Name of the new load balancer (required) |
| :type name: ``str`` |
| |
| :param members: ``list`` of:class:`Member`s to attach to balancer |
| :type members: ``list`` of :class:`Member` |
| |
| :param protocol: Loadbalancer protocol, defaults to http. |
| :type protocol: ``str`` |
| |
| :param port: Port the load balancer should listen on, defaults to 80 |
| :type port: ``str`` |
| |
| :param algorithm: Load balancing algorithm, defaults to |
| LBAlgorithm.ROUND_ROBIN |
| :type algorithm: :class:`Algorithm` |
| |
| :param vip: Virtual ip type of PUBLIC, SERVICENET, or ID of a virtual |
| ip |
| :type vip: ``str`` |
| |
| :rtype: :class:`LoadBalancer` |
| """ |
| balancer_attrs = self._kwargs_to_mutable_attrs( |
| name=name, protocol=protocol, port=port, algorithm=algorithm, vip=vip |
| ) |
| |
| balancer_attrs.update({"nodes": [self._member_attributes(member) for member in members]}) |
| # balancer_attrs['nodes'] = ['fu'] |
| 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 _member_attributes(self, member): |
| member_attributes = {"address": member.ip, "port": member.port} |
| |
| member_attributes.update(self._kwargs_to_mutable_member_attrs(**member.extra)) |
| |
| # If the condition is not specified on the member, then it should be |
| # set to ENABLED by default |
| if "condition" not in member_attributes: |
| member_attributes["condition"] = self.CONDITION_LB_MEMBER_MAP[MemberCondition.ENABLED] |
| |
| return member_attributes |
| |
| def destroy_balancer(self, balancer): |
| uri = "/loadbalancers/%s" % (balancer.id) |
| resp = self.connection.request(uri, method="DELETE") |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_destroy_balancers(self, balancers): |
| """ |
| Destroys a list of Balancers (the API supports up to 10). |
| |
| :param balancers: A list of Balancers to destroy. |
| :type balancers: ``list`` of :class:`LoadBalancer` |
| |
| :return: Returns whether the destroy request was accepted. |
| :rtype: ``bool`` |
| """ |
| ids = [("id", balancer.id) for balancer in balancers] |
| resp = self.connection.request("/loadbalancers", method="DELETE", params=ids) |
| |
| 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): |
| member_object = {"nodes": [self._member_attributes(member)]} |
| |
| uri = "/loadbalancers/%s/nodes" % (balancer.id) |
| resp = self.connection.request(uri, method="POST", data=json.dumps(member_object)) |
| return self._to_members(resp.object, balancer)[0] |
| |
| def ex_balancer_attach_members(self, balancer, members): |
| """ |
| Attaches a list of members to a load balancer. |
| |
| :param balancer: The Balancer to which members will be attached. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param members: A list of Members to attach. |
| :type members: ``list`` of :class:`Member` |
| |
| :rtype: ``list`` of :class:`Member` |
| """ |
| member_objects = {"nodes": [self._member_attributes(member) for member in members]} |
| |
| uri = "/loadbalancers/%s/nodes" % (balancer.id) |
| resp = self.connection.request(uri, method="POST", data=json.dumps(member_objects)) |
| return self._to_members(resp.object, balancer) |
| |
| 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/{}/nodes/{}".format(balancer.id, member.id) |
| resp = self.connection.request(uri, method="DELETE") |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_balancer_detach_members(self, balancer, members): |
| """ |
| Detaches a list of members from a balancer (the API supports up to 10). |
| This method blocks until the detach request has been processed and the |
| balancer is in a RUNNING state again. |
| |
| :param balancer: The Balancer to detach members from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param members: A list of Members to detach. |
| :type members: ``list`` of :class:`Member` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| accepted = self.ex_balancer_detach_members_no_poll(balancer, members) |
| |
| if not accepted: |
| msg = "Detach members request was not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_balancer_detach_members_no_poll(self, balancer, members): |
| """ |
| Detaches a list of members from a balancer (the API supports up to 10). |
| This method returns immediately. |
| |
| :param balancer: The Balancer to detach members from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param members: A list of Members to detach. |
| :type members: ``list`` of :class:`Member` |
| |
| :return: Returns whether the detach request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/nodes" % (balancer.id) |
| ids = [("id", member.id) for member in members] |
| resp = self.connection.request(uri, method="DELETE", params=ids) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def balancer_list_members(self, balancer): |
| uri = "/loadbalancers/%s/nodes" % (balancer.id) |
| data = self.connection.request(uri).object |
| return self._to_members(data, balancer) |
| |
| 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): |
| """ |
| Update balancer no poll. |
| |
| @inherits: :class:`Driver.update_balancer` |
| """ |
| 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_balancer_update_member(self, balancer, member, **kwargs): |
| """ |
| Updates a Member's extra attributes for a Balancer. The attributes can |
| include 'weight' or 'condition'. This method blocks until the update |
| request has been processed and the balancer is in a RUNNING state |
| again. |
| |
| :param balancer: Balancer to update the member on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param member: Member which should be used |
| :type member: :class:`Member` |
| |
| :keyword **kwargs: New attributes. Should contain either 'weight' |
| or 'condition'. 'condition' can be set to 'ENABLED', 'DISABLED'. |
| or 'DRAINING'. 'weight' can be set to a positive integer between |
| 1 and 100, with a higher weight indicating that the node will receive |
| more traffic (assuming the Balancer is using a weighted algorithm). |
| :type **kwargs: ``dict`` |
| |
| :return: Updated Member. |
| :rtype: :class:`Member` |
| """ |
| accepted = self.ex_balancer_update_member_no_poll(balancer, member, **kwargs) |
| |
| if not accepted: |
| msg = "Update member attributes was not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| balancer = self._get_updated_balancer(balancer) |
| members = balancer.extra["members"] |
| |
| updated_members = [m for m in members if m.id == member.id] |
| |
| if not updated_members: |
| raise LibcloudError("Could not find updated member") |
| |
| return updated_members[0] |
| |
| def ex_balancer_update_member_no_poll(self, balancer, member, **kwargs): |
| """ |
| Updates a Member's extra attributes for a Balancer. The attribute can |
| include 'weight' or 'condition'. This method returns immediately. |
| |
| :param balancer: Balancer to update the member on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param member: Member which should be used |
| :type member: :class:`Member` |
| |
| :keyword **kwargs: New attributes. Should contain either 'weight' |
| or 'condition'. 'condition' can be set to 'ENABLED', 'DISABLED'. |
| or 'DRAINING'. 'weight' can be set to a positive integer between |
| 1 and 100, with a higher weight indicating that the node will receive |
| more traffic (assuming the Balancer is using a weighted algorithm). |
| :type **kwargs: ``dict`` |
| |
| :return: Returns whether the update request was accepted. |
| :rtype: ``bool`` |
| """ |
| resp = self.connection.request( |
| action="/loadbalancers/{}/nodes/{}".format(balancer.id, member.id), |
| method="PUT", |
| data=json.dumps(self._kwargs_to_mutable_member_attrs(**kwargs)), |
| ) |
| |
| 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. |
| |
| :rtype: ``list`` of ``str`` |
| """ |
| response = self.connection.request("/loadbalancers/algorithms") |
| return [a["name"].upper() for a in response.object["algorithms"]] |
| |
| def ex_get_balancer_error_page(self, balancer): |
| """ |
| List error page configured for the specified load balancer. |
| |
| :param balancer: Balancer which should be used |
| :type balancer: :class:`LoadBalancer` |
| |
| :rtype: ``str`` |
| """ |
| uri = "/loadbalancers/%s/errorpage" % (balancer.id) |
| resp = self.connection.request(uri) |
| |
| return resp.object["errorpage"]["content"] |
| |
| def ex_balancer_access_list(self, balancer): |
| """ |
| List the access list. |
| |
| :param balancer: Balancer which should be used |
| :type balancer: :class:`LoadBalancer` |
| |
| :rtype: ``list`` of :class:`RackspaceAccessRule` |
| """ |
| uri = "/loadbalancers/%s/accesslist" % (balancer.id) |
| resp = self.connection.request(uri) |
| |
| return [self._to_access_rule(el) for el in resp.object["accessList"]] |
| |
| def _get_updated_balancer(self, balancer): |
| """ |
| Updating a balancer's attributes puts a balancer into |
| 'PENDING_UPDATE' status. Wait until the balancer is |
| back in 'ACTIVE' status and then return the individual |
| balancer details call. |
| """ |
| resp = self.connection.async_request(action="/loadbalancers/%s" % balancer.id, method="GET") |
| |
| return self._to_balancer(resp.object["loadBalancer"]) |
| |
| def ex_update_balancer_health_monitor(self, balancer, health_monitor): |
| """ |
| Sets a Balancer's health monitor. This method blocks until the update |
| request has been processed and the balancer is in a RUNNING state |
| again. |
| |
| :param balancer: Balancer to update. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param health_monitor: Health Monitor for the balancer. |
| :type health_monitor: :class:`RackspaceHealthMonitor` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| accepted = self.ex_update_balancer_health_monitor_no_poll(balancer, health_monitor) |
| if not accepted: |
| msg = "Update health monitor request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_update_balancer_health_monitor_no_poll(self, balancer, health_monitor): |
| """ |
| Sets a Balancer's health monitor. This method returns immediately. |
| |
| :param balancer: Balancer to update health monitor on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param health_monitor: Health Monitor for the balancer. |
| :type health_monitor: :class:`RackspaceHealthMonitor` |
| |
| :return: Returns whether the update request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/healthmonitor" % (balancer.id) |
| |
| resp = self.connection.request( |
| uri, method="PUT", data=json.dumps(health_monitor._to_dict()) |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_disable_balancer_health_monitor(self, balancer): |
| """ |
| Disables a Balancer's health monitor. This method blocks until the |
| disable request has been processed and the balancer is in a RUNNING |
| state again. |
| |
| :param balancer: Balancer to disable health monitor on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_disable_balancer_health_monitor_no_poll(balancer): |
| msg = "Disable health monitor request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_disable_balancer_health_monitor_no_poll(self, balancer): |
| """ |
| Disables a Balancer's health monitor. This method returns |
| immediately. |
| |
| :param balancer: Balancer to disable health monitor on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the disable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/healthmonitor" % (balancer.id) |
| |
| resp = self.connection.request(uri, method="DELETE") |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_update_balancer_connection_throttle(self, balancer, connection_throttle): |
| """ |
| Updates a Balancer's connection throttle. This method blocks until |
| the update request has been processed and the balancer is in a |
| RUNNING state again. |
| |
| :param balancer: Balancer to update connection throttle on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param connection_throttle: Connection Throttle for the balancer. |
| :type connection_throttle: :class:`RackspaceConnectionThrottle` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| accepted = self.ex_update_balancer_connection_throttle_no_poll( |
| balancer, connection_throttle |
| ) |
| |
| if not accepted: |
| msg = "Update connection throttle request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_update_balancer_connection_throttle_no_poll(self, balancer, connection_throttle): |
| """ |
| Sets a Balancer's connection throttle. This method returns |
| immediately. |
| |
| :param balancer: Balancer to update connection throttle on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param connection_throttle: Connection Throttle for the balancer. |
| :type connection_throttle: :class:`RackspaceConnectionThrottle` |
| |
| :return: Returns whether the update request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/connectionthrottle" % (balancer.id) |
| resp = self.connection.request( |
| uri, method="PUT", data=json.dumps(connection_throttle._to_dict()) |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_disable_balancer_connection_throttle(self, balancer): |
| """ |
| Disables a Balancer's connection throttle. This method blocks until |
| the disable request has been processed and the balancer is in a RUNNING |
| state again. |
| |
| :param balancer: Balancer to disable connection throttle on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_disable_balancer_connection_throttle_no_poll(balancer): |
| msg = "Disable connection throttle request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_disable_balancer_connection_throttle_no_poll(self, balancer): |
| """ |
| Disables a Balancer's connection throttle. This method returns |
| immediately. |
| |
| :param balancer: Balancer to disable connection throttle on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the disable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/connectionthrottle" % (balancer.id) |
| resp = self.connection.request(uri, method="DELETE") |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_enable_balancer_connection_logging(self, balancer): |
| """ |
| Enables connection logging for a Balancer. This method blocks until |
| the enable request has been processed and the balancer is in a RUNNING |
| state again. |
| |
| :param balancer: Balancer to enable connection logging on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_enable_balancer_connection_logging_no_poll(balancer): |
| msg = "Enable connection logging request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_enable_balancer_connection_logging_no_poll(self, balancer): |
| """ |
| Enables connection logging for a Balancer. This method returns |
| immediately. |
| |
| :param balancer: Balancer to enable connection logging on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the enable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/connectionlogging" % (balancer.id) |
| |
| resp = self.connection.request( |
| uri, method="PUT", data=json.dumps({"connectionLogging": {"enabled": True}}) |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_disable_balancer_connection_logging(self, balancer): |
| """ |
| Disables connection logging for a Balancer. This method blocks until |
| the enable request has been processed and the balancer is in a RUNNING |
| state again. |
| |
| :param balancer: Balancer to disable connection logging on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_disable_balancer_connection_logging_no_poll(balancer): |
| msg = "Disable connection logging request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_disable_balancer_connection_logging_no_poll(self, balancer): |
| """ |
| Disables connection logging for a Balancer. This method returns |
| immediately. |
| |
| :param balancer: Balancer to disable connection logging on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the disable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/connectionlogging" % (balancer.id) |
| resp = self.connection.request( |
| uri, |
| method="PUT", |
| data=json.dumps({"connectionLogging": {"enabled": False}}), |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_enable_balancer_session_persistence(self, balancer): |
| """ |
| Enables session persistence for a Balancer by setting the persistence |
| type to 'HTTP_COOKIE'. This method blocks until the enable request |
| has been processed and the balancer is in a RUNNING state again. |
| |
| :param balancer: Balancer to enable session persistence on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_enable_balancer_session_persistence_no_poll(balancer): |
| msg = "Enable session persistence request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_enable_balancer_session_persistence_no_poll(self, balancer): |
| """ |
| Enables session persistence for a Balancer by setting the persistence |
| type to 'HTTP_COOKIE'. This method returns immediately. |
| |
| :param balancer: Balancer to enable session persistence on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the enable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/sessionpersistence" % (balancer.id) |
| resp = self.connection.request( |
| uri, |
| method="PUT", |
| data=json.dumps({"sessionPersistence": {"persistenceType": "HTTP_COOKIE"}}), |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_disable_balancer_session_persistence(self, balancer): |
| """ |
| Disables session persistence for a Balancer. This method blocks until |
| the disable request has been processed and the balancer is in a RUNNING |
| state again. |
| |
| :param balancer: Balancer to disable session persistence on. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_disable_balancer_session_persistence_no_poll(balancer): |
| msg = "Disable session persistence request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_disable_balancer_session_persistence_no_poll(self, balancer): |
| """ |
| Disables session persistence for a Balancer. This method returns |
| immediately. |
| |
| :param balancer: Balancer to disable session persistence for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the disable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/sessionpersistence" % (balancer.id) |
| resp = self.connection.request(uri, method="DELETE") |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_update_balancer_error_page(self, balancer, page_content): |
| """ |
| Updates a Balancer's custom error page. This method blocks until |
| the update request has been processed and the balancer is in a |
| RUNNING state again. |
| |
| :param balancer: Balancer to update the custom error page for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param page_content: HTML content for the custom error page. |
| :type page_content: ``str`` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| accepted = self.ex_update_balancer_error_page_no_poll(balancer, page_content) |
| if not accepted: |
| msg = "Update error page request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_update_balancer_error_page_no_poll(self, balancer, page_content): |
| """ |
| Updates a Balancer's custom error page. This method returns |
| immediately. |
| |
| :param balancer: Balancer to update the custom error page for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param page_content: HTML content for the custom error page. |
| :type page_content: ``str`` |
| |
| :return: Returns whether the update request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/errorpage" % (balancer.id) |
| resp = self.connection.request( |
| uri, method="PUT", data=json.dumps({"errorpage": {"content": page_content}}) |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_disable_balancer_custom_error_page(self, balancer): |
| """ |
| Disables a Balancer's custom error page, returning its error page to |
| the Rackspace-provided default. This method blocks until the disable |
| request has been processed and the balancer is in a RUNNING state |
| again. |
| |
| :param balancer: Balancer to disable the custom error page for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| if not self.ex_disable_balancer_custom_error_page_no_poll(balancer): |
| msg = "Disable custom error page request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_disable_balancer_custom_error_page_no_poll(self, balancer): |
| """ |
| Disables a Balancer's custom error page, returning its error page to |
| the Rackspace-provided default. This method returns immediately. |
| |
| :param balancer: Balancer to disable the custom error page for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Returns whether the disable request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/errorpage" % (balancer.id) |
| resp = self.connection.request(uri, method="DELETE") |
| |
| # Load Balancer API currently returns 200 OK on custom error page |
| # delete. |
| return resp.status == httplib.OK or resp.status == httplib.ACCEPTED |
| |
| def ex_create_balancer_access_rule(self, balancer, rule): |
| """ |
| Adds an access rule to a Balancer's access list. This method blocks |
| until the update request has been processed and the balancer is in a |
| RUNNING state again. |
| |
| :param balancer: Balancer to create the access rule for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rule: Access Rule to add to the balancer. |
| :type rule: :class:`RackspaceAccessRule` |
| |
| :return: The created access rule. |
| :rtype: :class:`RackspaceAccessRule` |
| """ |
| accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule) |
| if not accepted: |
| msg = "Create access rule not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| balancer = self._get_updated_balancer(balancer) |
| access_list = balancer.extra["accessList"] |
| |
| created_rule = self._find_matching_rule(rule, access_list) |
| |
| if not created_rule: |
| raise LibcloudError("Could not find created rule") |
| |
| return created_rule |
| |
| def ex_create_balancer_access_rule_no_poll(self, balancer, rule): |
| """ |
| Adds an access rule to a Balancer's access list. This method returns |
| immediately. |
| |
| :param balancer: Balancer to create the access rule for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rule: Access Rule to add to the balancer. |
| :type rule: :class:`RackspaceAccessRule` |
| |
| :return: Returns whether the create request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/accesslist" % (balancer.id) |
| resp = self.connection.request( |
| uri, method="POST", data=json.dumps({"networkItem": rule._to_dict()}) |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_create_balancer_access_rules(self, balancer, rules): |
| """ |
| Adds a list of access rules to a Balancer's access list. This method |
| blocks until the update request has been processed and the balancer is |
| in a RUNNING state again. |
| |
| :param balancer: Balancer to create the access rule for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rules: List of :class:`RackspaceAccessRule` to add to the |
| balancer. |
| :type rules: ``list`` of :class:`RackspaceAccessRule` |
| |
| :return: The created access rules. |
| :rtype: :class:`RackspaceAccessRule` |
| """ |
| accepted = self.ex_create_balancer_access_rules_no_poll(balancer, rules) |
| if not accepted: |
| msg = "Create access rules not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| balancer = self._get_updated_balancer(balancer) |
| access_list = balancer.extra["accessList"] |
| |
| created_rules = [] |
| for r in rules: |
| matched_rule = self._find_matching_rule(r, access_list) |
| if matched_rule: |
| created_rules.append(matched_rule) |
| |
| if len(created_rules) != len(rules): |
| raise LibcloudError("Could not find all created rules") |
| |
| return created_rules |
| |
| def _find_matching_rule(self, rule_to_find, access_list): |
| """ |
| LB API does not return the ID for the newly created rules, so we have |
| to search the list to find the rule with a matching rule type and |
| address to return an object with the right identifier.it. The API |
| enforces rule type and address uniqueness. |
| """ |
| for r in access_list: |
| if rule_to_find.rule_type == r.rule_type and rule_to_find.address == r.address: |
| return r |
| |
| return None |
| |
| def ex_create_balancer_access_rules_no_poll(self, balancer, rules): |
| """ |
| Adds a list of access rules to a Balancer's access list. This method |
| returns immediately. |
| |
| :param balancer: Balancer to create the access rule for. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rules: List of :class:`RackspaceAccessRule` to add to |
| the balancer. |
| :type rules: ``list`` of :class:`RackspaceAccessRule` |
| |
| :return: Returns whether the create request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/%s/accesslist" % (balancer.id) |
| resp = self.connection.request( |
| uri, |
| method="POST", |
| data=json.dumps({"accessList": [rule._to_dict() for rule in rules]}), |
| ) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_destroy_balancer_access_rule(self, balancer, rule): |
| """ |
| Removes an access rule from a Balancer's access list. This method |
| blocks until the update request has been processed and the balancer |
| is in a RUNNING state again. |
| |
| :param balancer: Balancer to remove the access rule from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rule: Access Rule to remove from the balancer. |
| :type rule: :class:`RackspaceAccessRule` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| accepted = self.ex_destroy_balancer_access_rule_no_poll(balancer, rule) |
| if not accepted: |
| msg = "Delete access rule not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_destroy_balancer_access_rule_no_poll(self, balancer, rule): |
| """ |
| Removes an access rule from a Balancer's access list. This method |
| returns immediately. |
| |
| :param balancer: Balancer to remove the access rule from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rule: Access Rule to remove from the balancer. |
| :type rule: :class:`RackspaceAccessRule` |
| |
| :return: Returns whether the destroy request was accepted. |
| :rtype: ``bool`` |
| """ |
| uri = "/loadbalancers/{}/accesslist/{}".format(balancer.id, rule.id) |
| resp = self.connection.request(uri, method="DELETE") |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_destroy_balancer_access_rules(self, balancer, rules): |
| """ |
| Removes a list of access rules from a Balancer's access list. This |
| method blocks until the update request has been processed and the |
| balancer is in a RUNNING state again. |
| |
| :param balancer: Balancer to remove the access rules from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rules: List of :class:`RackspaceAccessRule` objects to remove |
| from the balancer. |
| :type rules: ``list`` of :class:`RackspaceAccessRule` |
| |
| :return: Updated Balancer. |
| :rtype: :class:`LoadBalancer` |
| """ |
| accepted = self.ex_destroy_balancer_access_rules_no_poll(balancer, rules) |
| |
| if not accepted: |
| msg = "Destroy access rules request not accepted" |
| raise LibcloudError(msg, driver=self) |
| |
| return self._get_updated_balancer(balancer) |
| |
| def ex_destroy_balancer_access_rules_no_poll(self, balancer, rules): |
| """ |
| Removes a list of access rules from a Balancer's access list. This |
| method returns immediately. |
| |
| :param balancer: Balancer to remove the access rules from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :param rules: List of :class:`RackspaceAccessRule` objects to remove |
| from the balancer. |
| :type rules: ``list`` of :class:`RackspaceAccessRule` |
| |
| :return: Returns whether the destroy request was accepted. |
| :rtype: ``bool`` |
| """ |
| ids = [("id", rule.id) for rule in rules] |
| uri = "/loadbalancers/%s/accesslist" % balancer.id |
| |
| resp = self.connection.request(uri, method="DELETE", params=ids) |
| |
| return resp.status == httplib.ACCEPTED |
| |
| def ex_list_current_usage(self, balancer): |
| """ |
| Return current load balancer usage report. |
| |
| :param balancer: Balancer to remove the access rules from. |
| :type balancer: :class:`LoadBalancer` |
| |
| :return: Raw load balancer usage object. |
| :rtype: ``dict`` |
| """ |
| uri = "/loadbalancers/%s/usage/current" % (balancer.id) |
| resp = self.connection.request(uri, method="GET") |
| |
| return resp.object |
| |
| def _to_protocols(self, object): |
| protocols = [] |
| for item in object["protocols"]: |
| protocols.append(item["name"].lower()) |
| return protocols |
| |
| def _to_protocols_with_default_ports(self, object): |
| protocols = [] |
| for item in object["protocols"]: |
| name = item["name"].lower() |
| port = int(item["port"]) |
| protocols.append((name, port)) |
| |
| 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 "port" in el: |
| port = el["port"] |
| |
| if "sourceAddresses" in el: |
| sourceAddresses = el["sourceAddresses"] |
| |
| extra = { |
| "ipv6PublicSource": sourceAddresses.get("ipv6Public"), |
| "ipv4PublicSource": sourceAddresses.get("ipv4Public"), |
| "ipv4PrivateSource": sourceAddresses.get("ipv4Servicenet"), |
| "service_name": self.connection.get_service_name(), |
| "uri": "https://%s%s/loadbalancers/%s" |
| % (self.connection.host, self.connection.request_path, el["id"]), |
| } |
| |
| if "virtualIps" in el: |
| ip = el["virtualIps"][0]["address"] |
| extra["virtualIps"] = el["virtualIps"] |
| |
| 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"]) |
| |
| if "accessList" in el: |
| extra["accessList"] = [self._to_access_rule(rule) for rule in el["accessList"]] |
| |
| 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, balancer=None): |
| return [self._to_member(el, balancer) for el in object["nodes"]] |
| |
| def _to_member(self, el, balancer=None): |
| 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"], |
| balancer=balancer, |
| extra=extra, |
| ) |
| return lbmember |
| |
| def _protocol_to_value(self, protocol): |
| non_standard_protocols = { |
| "imapv2": "IMAPv2", |
| "imapv3": "IMAPv3", |
| "imapv4": "IMAPv4", |
| } |
| protocol_name = protocol.lower() |
| |
| if protocol_name in non_standard_protocols: |
| protocol_value = non_standard_protocols[protocol_name] |
| else: |
| protocol_value = protocol.upper() |
| |
| return protocol_value |
| |
| 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"] = self._protocol_to_value(attrs["protocol"]) |
| |
| if "port" in attrs: |
| update_attrs["port"] = int(attrs["port"]) |
| |
| if "vip" in attrs: |
| if attrs["vip"] == "PUBLIC" or attrs["vip"] == "SERVICENET": |
| update_attrs["virtualIps"] = [{"type": attrs["vip"]}] |
| else: |
| update_attrs["virtualIps"] = [{"id": attrs["vip"]}] |
| |
| return update_attrs |
| |
| def _kwargs_to_mutable_member_attrs(self, **attrs): |
| update_attrs = {} |
| if "condition" in attrs: |
| update_attrs["condition"] = self.CONDITION_LB_MEMBER_MAP.get(attrs["condition"]) |
| |
| if "weight" in attrs: |
| update_attrs["weight"] = attrs["weight"] |
| |
| return update_attrs |
| |
| 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 |