libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From anthonys...@apache.org
Subject [1/4] libcloud git commit: DimensionData: Adding ability to remove backup clients/privatize translation functions
Date Sun, 14 Feb 2016 02:45:11 GMT
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 <jeffrey.a.dunham@gmail.com>
Authored: Fri Feb 12 01:24:13 2016 -0500
Committer: anthony-shaw <anthony.p.shaw@gmail.com>
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', 'nobody@example.com')
         )
 
@@ -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):


Mime
View raw message