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())
