You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/03/26 11:04:59 UTC

[3/6] libcloud git commit: Generic pagination, anti-affinity rules, create firewall expansion

Generic pagination, anti-affinity rules, create firewall expansion


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/8607704e
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/8607704e
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/8607704e

Branch: refs/heads/trunk
Commit: 8607704e57cd896264a4cadb397e2e953ec28753
Parents: 9908ce1
Author: Jeffrey Dunham <je...@gmail.com>
Authored: Wed Mar 23 21:01:35 2016 -0400
Committer: anthony-shaw <an...@gmail.com>
Committed: Sat Mar 26 21:04:19 2016 +1100

----------------------------------------------------------------------
 libcloud/common/dimensiondata.py          |  30 +++++-
 libcloud/compute/drivers/dimensiondata.py | 144 ++++++++++++++++++++++++-
 2 files changed, 168 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/8607704e/libcloud/common/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/common/dimensiondata.py b/libcloud/common/dimensiondata.py
index ee7d831..8802820 100644
--- a/libcloud/common/dimensiondata.py
+++ b/libcloud/common/dimensiondata.py
@@ -22,7 +22,6 @@ from libcloud.utils.py3 import b
 from libcloud.common.base import ConnectionUserAndKey, XmlResponse
 from libcloud.common.types import LibcloudError, InvalidCredsError
 from libcloud.compute.base import Node
-from libcloud.utils.py3 import basestring
 from libcloud.utils.xml import findtext
 
 # Roadmap / TODO:
@@ -434,6 +433,22 @@ class DimensionDataConnection(ConnectionUserAndKey):
             params=params, data=data,
             method=method, headers=headers)
 
+    def paginated_request_with_orgId_api_2(self, action, params=None, data='',
+                                           headers=None, method='GET',
+                                           return_generator=False,
+                                           page_count=250):
+
+        paged_resp = self.request_with_orgId_api_2(action, params,
+                                                   data, headers,
+                                                   method).object
+        yield paged_resp
+
+        while paged_resp.get('pageCount') >= paged_resp.get('pageSize'):
+            params['pageNumber'] = int(paged_resp.get('pageNumber')) + 1
+            paged_resp = self._list_nodes_single_page(action, params, data,
+                                                      headers, method).object
+            yield paged_resp
+
     def get_resource_path_api_1(self):
         """
         This method returns a resource path which is necessary for referencing
@@ -731,6 +746,19 @@ class DimensionDataNatRule(object):
                 % (self.id, self.status))
 
 
+class DimensionDataAntiAffinityRule(object):
+    """
+    Anti-Affinity Rule
+    """
+    def __init__(self, id, node_list):
+        self.id = id
+        self.node_list = node_list
+
+    def __repr__(self):
+        return (('<DimensionDataAntiAffinityRule: id=%s>')
+                % (self.id))
+
+
 class DimensionDataVlan(object):
     """
     DimensionData VLAN.

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8607704e/libcloud/compute/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py
index 6762e36..575f447 100644
--- a/libcloud/compute/drivers/dimensiondata.py
+++ b/libcloud/compute/drivers/dimensiondata.py
@@ -36,6 +36,7 @@ from libcloud.common.dimensiondata import DimensionDataPublicIpBlock
 from libcloud.common.dimensiondata import DimensionDataFirewallRule
 from libcloud.common.dimensiondata import DimensionDataFirewallAddress
 from libcloud.common.dimensiondata import DimensionDataNatRule
+from libcloud.common.dimensiondata import DimensionDataAntiAffinityRule
 from libcloud.common.dimensiondata import NetworkDomainServicePlan
 from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION
 from libcloud.common.dimensiondata import TYPES_URN
@@ -686,6 +687,68 @@ class DimensionDataNodeDriver(NodeDriver):
         response_code = findtext(body, 'result', GENERAL_NS)
         return response_code in ['IN_PROGRESS', 'SUCCESS']
 
+    def ex_create_anti_affinity_rule(self, node_list):
+        if not isinstance(node_list, (list, tuple)):
+            raise TypeError("Node list must be a list or a tuple.")
+        anti_affinity_xml_request = ET.Element('NewAntiAffinityRule',
+                                               {'xmlns': SERVER_NS})
+        for node in node_list:
+            ET.SubElement(anti_affinity_xml_request, 'serverId').text = \
+                self._node_to_node_id(node)
+        result = self.connection.request_with_orgId_api_1(
+            'antiAffinityRule',
+            method='POST',
+            data=ET.tostring(anti_affinity_xml_request)).object
+        response_code = findtext(result, 'result', GENERAL_NS)
+        return response_code in ['IN_PROGRESS', 'SUCCESS']
+
+    def ex_delete_anti_affinity_rule(self, anti_affinity_rule):
+        rule_id = self._anti_affinity_rule_to_anti_affinity_rule_id(
+            anti_affinity_rule)
+        result = self.connection.request_with_orgId_api_1(
+            'antiAffinityRule/%s?delete' % (rule_id),
+            method='GET').object
+        response_code = findtext(result, 'result', GENERAL_NS)
+        return response_code in ['IN_PROGRESS', 'SUCCESS']
+
+    def ex_list_anti_affinity_rules(self, network=None, network_domain=None,
+                                    node=None, ex_filter_id=None,
+                                    ex_filter_state=None,
+                                    return_generator=False):
+
+        not_none_arguments = [key
+                              for key in (network, network_domain, node)
+                              if key is not None]
+        if len(not_none_arguments) != 1:
+            raise ValueError("One and ONLY one of network, "
+                             "network_domain, or node must be set")
+
+        params = {}
+        if network_domain is not None:
+            params['networkDomainId'] = \
+                self._network_domain_to_network_domain_id(network_domain)
+        if network is not None:
+            params['networkId'] = \
+                self._network_to_network_id(network)
+        if node is not None:
+            params['serverId'] = \
+                self._node_to_node_id(network_domain)
+        if ex_filter_id is not None:
+            params['id'] = ex_filter_id
+        if ex_filter_state is not None:
+            params['state'] = ex_filter_state
+
+        paged_result = self.connection.paginated_request_with_orgId_api_2(
+            'server/antiAffinityRule',
+            method='GET',
+            params=params
+        )
+
+        rules = []
+        for result in paged_result:
+            rules.extend(self._to_anti_affinity_rules(result))
+        return rules
+
     def ex_attach_node_to_vlan(self, node, vlan):
         """
         Attach a node to a VLAN by adding an additional NIC to
@@ -1084,10 +1147,10 @@ class DimensionDataNodeDriver(NodeDriver):
 
     def ex_delete_vlan(self, vlan):
         """
-        Deletes an existing VLAN
+        deletes an existing vlan
 
-        :param      vlan: The VLAN to delete
-        :type       vlan: :class:`DimensionDataNetworkDomain`
+        :param      vlan: the vlan to delete
+        :type       vlan: :class:`dimensiondatanetworkdomain`
 
         :rtype: ``bool``
         """
@@ -1205,9 +1268,38 @@ class DimensionDataNodeDriver(NodeDriver):
                                       params=params).object
         return self._to_firewall_rules(response, network_domain)
 
-    def ex_create_firewall_rule(self, network_domain, rule, position):
+    def ex_create_firewall_rule(self, network_domain, rule, position,
+                                position_relative_to_rule=None):
+        """
+        Creates a firewall rule
+
+        :param network_domain: The network domain in which to create
+                                the firewall rule
+        :type  network_domain: :class:`DimensionDataNetworkDomain` or ``str``
+
+        :param rule: The rule in which to create
+        :type  rule: :class:`DimensionDataFirewallRule`
+
+        :param position: The position in which to create the rule
+                         There are two types of positions
+                         with position_relative_to_rule arg and without it
+                         With: 'BEFORE' or 'AFTER'
+                         Without: 'FIRST' or 'LAST'
+        :type  position: ``str``
+
+        :param position_relative_to_rule: The rule or rule name in
+                                          which to decide positioning by
+        :type  position_relative_to_rule:
+            :class:`DimensionDataFirewallRule` or ``str``
+
+        :rtype: ``bool``
+        """
+        positions_without_rule = ('FIRST', 'LAST')
+        positions_with_rule = ('BEFORE', 'AFTER')
+
         create_node = ET.Element('createFirewallRule', {'xmlns': TYPES_URN})
-        ET.SubElement(create_node, "networkDomainId").text = network_domain.id
+        ET.SubElement(create_node, "networkDomainId").text = \
+            self._network_domain_to_network_domain_id(network_domain)
         ET.SubElement(create_node, "name").text = rule.name
         ET.SubElement(create_node, "action").text = rule.action
         ET.SubElement(create_node, "ipVersion").text = rule.ip_version
@@ -1241,7 +1333,25 @@ class DimensionDataNodeDriver(NodeDriver):
             if rule.destination.port_end is not None:
                 dest_port.set('end', rule.destination.port_end)
         ET.SubElement(create_node, "enabled").text = 'true'
+
+        # Set up positioning of rule
         placement = ET.SubElement(create_node, "placement")
+        if position_relative_to_rule is not None:
+            if position not in positions_with_rule:
+                raise ValueError("When position_relative_to_rule is specified"
+                                 " position must be %s"
+                                 % ', '.join(positions_with_rule))
+            if isinstance(position_relative_to_rule,
+                          DimensionDataFirewallRule):
+                rule_name = position_relative_to_rule.name
+            else:
+                rule_name = position_relative_to_rule
+            placement.set('relativeToRule', rule_name)
+        else:
+            if position not in positions_without_rule:
+                raise ValueError("When position_relative_to_rule is not"
+                                 " specified position must be %s"
+                                 % ', '.join(positions_without_rule))
         placement.set('position', position)
 
         response = self.connection.request_with_orgId_api_2(
@@ -1782,6 +1892,22 @@ class DimensionDataNodeDriver(NodeDriver):
             external_ip=findtext(element, 'externalIp', TYPES_URN),
             status=findtext(element, 'state', TYPES_URN))
 
+    def _to_anti_affinity_rules(self, object):
+        rules = []
+        for element in findall(object, 'antiAffinityRule', TYPES_URN):
+            rules.append(
+                self._to_anti_affinity_rule(element))
+        return rules
+
+    def _to_anti_affinity_rule(self, element):
+        node_list = []
+        for node in findall(element, 'serverSummary', TYPES_URN):
+            node_list.append(node.get('id'))
+        return DimensionDataAntiAffinityRule(
+            id=element.get('id'),
+            node_list=node_list
+        )
+
     def _to_firewall_rules(self, object, network_domain):
         rules = []
         locations = self.list_locations()
@@ -2090,6 +2216,10 @@ class DimensionDataNodeDriver(NodeDriver):
                 return NodeState.TERMINATED
 
     @staticmethod
+    def _node_to_node_id(node):
+        return dd_object_to_id(node, Node)
+
+    @staticmethod
     def _location_to_location_id(location):
         return dd_object_to_id(location, NodeLocation)
 
@@ -2106,5 +2236,9 @@ class DimensionDataNodeDriver(NodeDriver):
         return dd_object_to_id(network, DimensionDataNetwork)
 
     @staticmethod
+    def _anti_affinity_rule_to_anti_affinity_rule_id(rule):
+        return dd_object_to_id(rule, DimensionDataAntiAffinityRule)
+
+    @staticmethod
     def _network_domain_to_network_domain_id(network_domain):
         return dd_object_to_id(network_domain, DimensionDataNetworkDomain)