Repository: libcloud Updated Branches: refs/heads/trunk 2bca79b41 -> ab6a7508e
DimensionData: Adding ability to remove backup clients/privatize translation functions Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/352a5d84 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/352a5d84 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/352a5d84 Branch: refs/heads/trunk Commit: 352a5d842263dfbc248831e227f2ca5dcdb48ef7 Parents: 6e24baf Author: Jeffrey Dunham <[email protected]> Authored: Fri Feb 12 01:24:13 2016 -0500 Committer: anthony-shaw <[email protected]> Committed: Sun Feb 14 13:44:39 2016 +1100 ---------------------------------------------------------------------- libcloud/backup/drivers/dimensiondata.py | 1 + libcloud/common/dimensiondata.py | 9 +-- libcloud/compute/drivers/dimensiondata.py | 8 +- ...4_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml | 2 +- ...c_b39d_3b72be0384c8_remove_backup_client.xml | 7 ++ ...d_3b72be0384c8_remove_backup_client_BUSY.xml | 7 ++ libcloud/test/backup/test_dimensiondata.py | 85 +++++++++++++++++++- libcloud/test/compute/test_dimensiondata.py | 17 ++++ 8 files changed, 123 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/backup/drivers/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/backup/drivers/dimensiondata.py b/libcloud/backup/drivers/dimensiondata.py index 6c30d36..8e892a1 100644 --- a/libcloud/backup/drivers/dimensiondata.py +++ b/libcloud/backup/drivers/dimensiondata.py @@ -32,6 +32,7 @@ from libcloud.common.dimensiondata import DimensionDataBackupStoragePolicy from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION from libcloud.common.dimensiondata import TYPES_URN from libcloud.common.dimensiondata import GENERAL_NS, BACKUP_NS +from libcloud.utils.py3 import basestring from libcloud.utils.xml import fixxpath, findtext, findall http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/common/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/common/dimensiondata.py b/libcloud/common/dimensiondata.py index c8c08f0..030c7a2 100644 --- a/libcloud/common/dimensiondata.py +++ b/libcloud/common/dimensiondata.py @@ -18,10 +18,9 @@ Dimension Data Common Components from base64 import b64encode from time import sleep from libcloud.utils.py3 import httplib -from libcloud.utils.py3 import b, basestring +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 NodeLocation from libcloud.utils.xml import findtext # Roadmap / TODO: @@ -1087,7 +1086,7 @@ class DimensionDataBackupClient(object): def __repr__(self): return (('<DimensionDataBackupClient: id=%s>') - % (self.asset_id)) + % (self.id)) class DimensionDataBackupClientAlert(object): @@ -1109,8 +1108,8 @@ class DimensionDataBackupClientAlert(object): self.notify_list = notify_list def __repr__(self): - return (('<DimensionDataBackupClientAlert: id=%s>') - % (self.asset_id)) + return (('<DimensionDataBackupClientAlert: trigger=%s>') + % (self.trigger)) class DimensionDataBackupClientRunningJob(object): http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/compute/drivers/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py index 653fc9c..651536e 100644 --- a/libcloud/compute/drivers/dimensiondata.py +++ b/libcloud/compute/drivers/dimensiondata.py @@ -827,9 +827,11 @@ class DimensionDataNodeDriver(NodeDriver): :rtype: :class:`DimensionDataNetworkDomain` """ create_node = ET.Element('deployNetworkDomain', {'xmlns': TYPES_URN}) - ET.SubElement(create_node, - "datacenterId" - ).text = self._location_to_location_id(location) + ET.SubElement( + create_node, + "datacenterId" + ).text = self._location_to_location_id(location) + ET.SubElement(create_node, "name").text = name if description is not None: ET.SubElement(create_node, "description").text = description http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml index c3a9c37..51e4494 100644 --- a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml +++ b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml @@ -2,6 +2,6 @@ <ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> <ns6:operation>Get Backup Details</ns6:operation> <ns6:result>ERROR</ns6:result> - <ns6:resultDetail>Server e75ead52-692f-4314_8725-c8a4f4d13a87 has not been provisioned for backup</ns6:resultDetail> + <ns6:resultDetail>Server e75ead52-692f-4314-8725-c8a4f4d13a87 has not been provisioned for backup</ns6:resultDetail> <ns6:resultCode>REASON_543</ns6:resultCode> </ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client.xml b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client.xml new file mode 100644 index 0000000..4ce51c6 --- /dev/null +++ b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> + <ns6:operation>Disable Backup Client</ns6:operation> + <ns6:result>SUCCESS</ns6:result> + <ns6:resultDetail>Backup Client disabled</ns6:resultDetail> + <ns6:resultCode>REASON_0</ns6:resultCode> +</ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client_BUSY.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client_BUSY.xml b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client_BUSY.xml new file mode 100644 index 0000000..6c2db63 --- /dev/null +++ b/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client_BUSY.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip"> + <ns6:operation>Disable Backup Client</ns6:operation> + <ns6:result>ERROR</ns6:result> + <ns6:resultDetail>DISABLE_BACKUP_CLIENT 'didata-backup-test6[172-16-1-14]' - failed - Unexpected error occurred with NA9 Backup system at 2016-02-12 00:03:50.952, TransactionId: (9d483a7a-1cc9-441b-920c-e11fb0e94ba6), PCSOperation: DeprovisionBackupClient, Backup Client is currently performing another operation: Backup client is currently busy</ns6:resultDetail> + <ns6:resultCode>REASON_547</ns6:resultCode> +</ns6:Status> http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/test/backup/test_dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/test/backup/test_dimensiondata.py b/libcloud/test/backup/test_dimensiondata.py index 5ce91e3..b376f24 100644 --- a/libcloud/test/backup/test_dimensiondata.py +++ b/libcloud/test/backup/test_dimensiondata.py @@ -91,7 +91,7 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): def test_ex_add_client_to_target_STR(self): self.assertTrue( - self.driver.ex_add_client_to_target('e75ead52-692f-4314_8725-c8a4f4d13a87', 'FA.Linux', '14 Day Storage Policy', + self.driver.ex_add_client_to_target('e75ead52-692f-4314-8725-c8a4f4d13a87', 'FA.Linux', '14 Day Storage Policy', '12AM - 6AM', 'ON_FAILURE', '[email protected]') ) @@ -109,7 +109,7 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): """Test a backup info for a target that does not have a client""" def test_ex_get_backup_details_for_target_NO_CLIENT(self): DimensionDataMockHttp.type = 'NOCLIENT' - response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87') + response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87') self.assertEqual(response.service_plan, 'Essentials') self.assertEqual(len(response.clients), 0) @@ -130,9 +130,9 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): def test_ex_get_backup_details_for_target_DISABLED(self): DimensionDataMockHttp.type = 'DISABLED' with self.assertRaises(DimensionDataAPIException) as context: - self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87') + self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87') self.assertEqual(context.exception.code, 'ERROR') - self.assertEqual(context.exception.msg, 'Server e75ead52-692f-4314_8725-c8a4f4d13a87 has not been provisioned for backup') + self.assertEqual(context.exception.msg, 'Server e75ead52-692f-4314-8725-c8a4f4d13a87 has not been provisioned for backup') def test_ex_list_available_client_types(self): target = self.driver.list_targets()[0] @@ -158,6 +158,63 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): self.assertEqual(answer[0].name, '12AM - 6AM') self.assertEqual(answer[0].description, 'Daily backup will start between 12AM - 6AM') + def test_ex_remove_client_from_target(self): + target = self.driver.list_targets()[0] + client = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87').clients[0] + self.assertTrue(self.driver.ex_remove_client_from_target(target, client)) + + def test_ex_remove_client_from_target_STR(self): + self.assertTrue( + self.driver.ex_remove_client_from_target( + 'e75ead52-692f-4314-8725-c8a4f4d13a87', + '30b1ff76-c76d-4d7c-b39d-3b72be0384c8' + ) + ) + + def test_ex_remove_client_from_target_BUSY(self): + DimensionDataMockHttp.type = 'BUSY' + with self.assertRaises(DimensionDataAPIException) as context: + self.driver.ex_remove_client_from_target( + 'e75ead52-692f-4314-8725-c8a4f4d13a87', + '30b1ff76-c76d-4d7c-b39d-3b72be0384c8' + ) + self.assertEqual(context.exception.code, 'ERROR') + self.assertTrue('Backup Client is currently performing another operation' in context.exception.msg) + + def test_priv_target_to_target_address(self): + target = self.driver.list_targets()[0] + self.assertEqual( + self.driver._target_to_target_address(target), + 'e75ead52-692f-4314-8725-c8a4f4d13a87' + ) + + def test_priv_target_to_target_address_STR(self): + self.assertEqual( + self.driver._target_to_target_address('e75ead52-692f-4314-8725-c8a4f4d13a87'), + 'e75ead52-692f-4314-8725-c8a4f4d13a87' + ) + + def test_priv_target_to_target_address_TYPEERROR(self): + with self.assertRaises(TypeError): + self.driver._target_to_target_address([1, 2, 3]) + + def test_priv_client_to_client_id(self): + client = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87').clients[0] + self.assertEqual( + self.driver._client_to_client_id(client), + '30b1ff76-c76d-4d7c-b39d-3b72be0384c8' + ) + + def test_priv_client_to_client_id_STR(self): + self.assertEqual( + self.driver._client_to_client_id('30b1ff76-c76d-4d7c-b39d-3b72be0384c8'), + '30b1ff76-c76d-4d7c-b39d-3b72be0384c8' + ) + + def test_priv_client_to_client_id_TYPEERROR(self): + with self.assertRaises(TypeError): + self.driver._client_to_client_id([1, 2, 3]) + class InvalidRequestError(Exception): def __init__(self, tag): @@ -183,6 +240,10 @@ class DimensionDataMockHttp(MockHttp): body = self.fixtures.load('oec_0_9_myaccount.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _oec_0_9_myaccount_BUSY(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_myaccount.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _oec_0_9_myaccount_NOCLIENT(self, method, url, body, headers): body = self.fixtures.load('oec_0_9_myaccount.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) @@ -286,6 +347,22 @@ class DimensionDataMockHttp(MockHttp): 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8( + self, method, url, body, headers): + body = self.fixtures.load( + ('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87' + '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client.xml') + ) + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_BUSY( + self, method, url, body, headers): + body = self.fixtures.load( + ('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87' + '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_remove_backup_client_BUSY.xml') + ) + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + if __name__ == '__main__': sys.exit(unittest.main()) http://git-wip-us.apache.org/repos/asf/libcloud/blob/352a5d84/libcloud/test/compute/test_dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_dimensiondata.py b/libcloud/test/compute/test_dimensiondata.py index 4ddba15..3bb0210 100644 --- a/libcloud/test/compute/test_dimensiondata.py +++ b/libcloud/test/compute/test_dimensiondata.py @@ -552,6 +552,23 @@ class DimensionDataTests(unittest.TestCase, TestCaseMixin): location = self.driver.ex_get_location_by_id(None) self.assertIsNone(location) + def test_priv_location_to_location_id(self): + location = self.driver.ex_get_location_by_id('NA9') + self.assertEqual( + self.driver._location_to_location_id(location), + 'NA9' + ) + + def test_priv_location_to_location_id_STR(self): + self.assertEqual( + self.driver._location_to_location_id('NA9'), + 'NA9' + ) + + def test_priv_location_to_location_id_TYPEERROR(self): + with self.assertRaises(TypeError): + self.driver._location_to_location_id([1, 2, 3]) + class InvalidRequestError(Exception): def __init__(self, tag):
