Repository: libcloud
Updated Branches:
  refs/heads/trunk 7e3648a17 -> ecbaa0b57


LIBCLOUD-750 support for creating servers in both MCP 1 and 2 data centers

Closes #587


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

Branch: refs/heads/trunk
Commit: 09ec78d951e896ce202e7194263e4123ea943292
Parents: 7e3648a
Author: Anthony Shaw <[email protected]>
Authored: Wed Sep 23 16:25:15 2015 +1000
Committer: Tomaz Muraus <[email protected]>
Committed: Sat Sep 26 22:37:03 2015 +0200

----------------------------------------------------------------------
 libcloud/compute/drivers/dimensiondata.py       | 73 +++++++++++---------
 ...8a_9cbc_8dabe5a7d0e4_server_deployServer.xml |  9 +++
 libcloud/test/compute/test_dimensiondata.py     | 18 +++++
 3 files changed, 66 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/09ec78d9/libcloud/compute/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/dimensiondata.py 
b/libcloud/compute/drivers/dimensiondata.py
index 9339f27..a7de6d4 100644
--- a/libcloud/compute/drivers/dimensiondata.py
+++ b/libcloud/compute/drivers/dimensiondata.py
@@ -76,7 +76,9 @@ class DimensionDataNodeDriver(NodeDriver):
         return kwargs
 
     def create_node(self, name, image, auth, ex_description,
-                    ex_network, ex_is_started=True, **kwargs):
+                    ex_network=None, ex_network_domain=None,
+                    ex_vlan=None,
+                    ex_is_started=True, **kwargs):
         """
         Create a new DimensionData node
 
@@ -93,61 +95,64 @@ class DimensionDataNodeDriver(NodeDriver):
         :keyword    ex_description:  description for this node (required)
         :type       ex_description:  ``str``
 
-        :keyword    ex_network:  Network to create the node within (required)
+        :keyword    ex_network:  Network to create the node within (required,
+                                unless using Network Domain)
         :type       ex_network: :class:`DimensionDataNetwork`
 
+        :keyword    ex_network_domain:  Network Domain to create the node
+                                        (required unless using network)
+        :type       ex_network_domain: :class:`DimensionDataNetworkDomain`
+
+        :keyword    ex_vlan:  VLAN to create the node within
+                                        (required unless using network)
+        :type       ex_vlan: :class:`DimensionDataVlan`
+
         :keyword    ex_is_started:  Start server after creation? default
                                    true (required)
         :type       ex_is_started:  ``bool``
 
-        :return: The newly created :class:`Node`. NOTE: DimensionData does not
-                 provide a
-                 way to determine the ID of the server that was just created,
-                 so the returned :class:`Node` is not guaranteed to be the same
-                 one that was created.  This is only the case when multiple
-                 nodes with the same name exist.
+        :return: The newly created :class:`Node`.
         :rtype: :class:`Node`
         """
 
-        # XXX:  Node sizes can be adjusted after a node is created, but
-        #       cannot be set at create time because size is part of the
-        #       image definition.
         password = None
         auth_obj = self._get_and_check_auth(auth)
         password = auth_obj.password
 
         if not isinstance(ex_network, DimensionDataNetwork):
-            raise ValueError('ex_network must be of DimensionDataNetwork type')
-        vlanResourcePath = "%s/%s" % (
-            self.connection.get_resource_path_api_1(),
-            ex_network.id)
-
-        imageResourcePath = None
-        if 'resourcePath' in image.extra:
-            imageResourcePath = image.extra['resourcePath']
-        else:
-            imageResourcePath = "%s/%s" % (
-                self.connection.get_resource_path_api_1(),
-                image.id)
+            if not isinstance(ex_network_domain, DimensionDataNetworkDomain):
+                raise ValueError('ex_network must be of DimensionDataNetwork '
+                                 'type or ex_network_domain must be of '
+                                 'DimensionDataNetworkDomain type')
 
-        server_elm = ET.Element('Server', {'xmlns': SERVER_NS})
+        server_elm = ET.Element('deployServer', {'xmlns': TYPES_URN})
         ET.SubElement(server_elm, "name").text = name
         ET.SubElement(server_elm, "description").text = ex_description
-        ET.SubElement(server_elm, "vlanResourcePath").text = vlanResourcePath
-        ET.SubElement(server_elm, "imageResourcePath").text = imageResourcePath
+        ET.SubElement(server_elm, "imageId").text = image.id
+        ET.SubElement(server_elm, "start").text = str(ex_is_started)
         ET.SubElement(server_elm, "administratorPassword").text = password
-        ET.SubElement(server_elm, "isStarted").text = str(ex_is_started)
 
-        self.connection.request_with_orgId_api_1(
-            'server',
+        if ex_network is not None:
+            network_elm = ET.SubElement(server_elm, "network")
+            ET.SubElement(network_elm, "networkId").text = ex_network.id
+        if ex_network_domain is not None:
+            network_inf_elm = ET.SubElement(server_elm, "networkInfo",
+                                            {'networkDomainId':
+                                             ex_network_domain.id})
+            pri_nic = ET.SubElement(network_inf_elm, "primaryNic")
+            ET.SubElement(pri_nic, "vlanId").text = ex_vlan.id
+
+        response = self.connection.request_with_orgId_api_2(
+            'server/deployServer',
             method='POST',
             data=ET.tostring(server_elm)).object
 
-        # XXX: return the last node in the list that has a matching name.  this
-        #      is likely but not guaranteed to be the node we just created
-        #      because DimensionData allows multiple
-        #      nodes to have the same name
-        node = list(filter(lambda x: x.name == name, self.list_nodes()))[-1]
+        node_id = None
+        for info in findall(response, 'info', TYPES_URN):
+            if info.get('name') == 'serverId':
+                node_id = info.get('value')
+
+        node = list(filter(lambda x: x.id == node_id, self.list_nodes()))[-1]
 
         if getattr(auth_obj, "generated", False):
             node.extra['password'] = auth_obj.password

http://git-wip-us.apache.org/repos/asf/libcloud/blob/09ec78d9/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployServer.xml
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployServer.xml
 
b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployServer.xml
new file mode 100644
index 0000000..74077be
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployServer.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<response xmlns="urn:didata.com:api:cloud:types" 
requestId="NA9/2015-03-08T10:43:34.168-04:00/7c4ea967-1723-4a06-80e2-fcdf50f3fa82">
+<operation>DEPLOY</operation>
+<responseCode>IN_PROGRESS</responseCode>
+<message>Request to deploy Server 'Production FTPS Server' has been
+accepted and is being processed.</message>
+© 2015 Dimension Data Cloud Solutions 162
+<info name="serverId" value="e75ead52-692f-4314-8725-c8a4f4d13a87"/>
+</response>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/09ec78d9/libcloud/test/compute/test_dimensiondata.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_dimensiondata.py 
b/libcloud/test/compute/test_dimensiondata.py
index cd8a258..4051b9e 100644
--- a/libcloud/test/compute/test_dimensiondata.py
+++ b/libcloud/test/compute/test_dimensiondata.py
@@ -115,6 +115,19 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin):
         self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
         self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
 
+    def test_create_node_response_network_domain(self):
+        rootPw = NodeAuthPassword('pass123')
+        image = self.driver.list_images()[0]
+        network_domain = self.driver.ex_list_network_domains()[0]
+        vlan = self.driver.ex_list_vlans()[0]
+        node = self.driver.create_node(name='test2', image=image, auth=rootPw,
+                                       ex_description='test2 node',
+                                       ex_network_domain=network_domain,
+                                       ex_vlan=vlan,
+                                       ex_isStarted=False)
+        self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+        self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER')
+
     def test_create_node_no_network(self):
         rootPw = NodeAuthPassword('pass123')
         image = self.driver.list_images()[0]
@@ -370,5 +383,10 @@ class DimensionDataMockHttp(MockHttp):
             'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_vlan.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+    def 
_caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployServer(self, 
method, url, body, headers):
+        body = self.fixtures.load(
+            
'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployServer.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 if __name__ == '__main__':
     sys.exit(unittest.main())

Reply via email to