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 2017/01/09 04:52:42 UTC
[22/51] [abbrv] libcloud git commit: merge update
http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/common/openstack_identity.py
----------------------------------------------------------------------
diff --cc libcloud/common/openstack_identity.py
index 11fdb78,da90f0b..9e841cf
--- a/libcloud/common/openstack_identity.py
+++ b/libcloud/common/openstack_identity.py
@@@ -1536,6 -1539,139 +1539,139 @@@ class OpenStackIdentity_3_0_Connection_
driver=self.driver)
+ class OpenStackIdentity_2_0_Connection_VOMS(OpenStackIdentityConnection,
+ CertificateConnection):
+ """
+ Connection class for Keystone API v2.0. with VOMS proxy support
+ In this case the key parameter will be the path of the VOMS proxy file.
+ """
+
+ responseCls = OpenStackAuthResponse
+ name = 'OpenStack Identity API v2.0 VOMS support'
+ auth_version = '2.0'
+
+ def __init__(self, auth_url, user_id, key, tenant_name=None,
+ domain_name='Default',
+ token_scope=OpenStackIdentityTokenScope.PROJECT,
+ timeout=None, parent_conn=None):
+ CertificateConnection.__init__(self, cert_file=key,
+ url=auth_url,
+ timeout=timeout)
+
+ self.parent_conn = parent_conn
+
+ # enable tests to use the same mock connection classes.
+ if parent_conn:
- self.conn_classes = parent_conn.conn_classes
++ self.conn_class = parent_conn.conn_class
+ self.driver = parent_conn.driver
+ else:
+ self.driver = None
+
+ self.auth_url = auth_url
+ self.tenant_name = tenant_name
+ self.domain_name = domain_name
+ self.token_scope = token_scope
+ self.timeout = timeout
+
+ self.urls = {}
+ self.auth_token = None
+ self.auth_token_expires = None
+ self.auth_user_info = None
+
+ def authenticate(self, force=False):
+ if not self._is_authentication_needed(force=force):
+ return self
+
+ tenant = self.tenant_name
+ if not tenant:
+ # if the tenant name is not specified look for it
+ token = self._get_unscoped_token()
+ tenant = self._get_tenant_name(token)
+
+ data = {'auth': {'voms': True, 'tenantName': tenant}}
+
+ reqbody = json.dumps(data)
+ return self._authenticate_2_0_with_body(reqbody)
+
+ def _get_unscoped_token(self):
+ """
+ Get unscoped token from VOMS proxy
+ """
+ data = {'auth': {'voms': True}}
+ reqbody = json.dumps(data)
+
+ response = self.request('/v2.0/tokens', data=reqbody,
+ headers={'Content-Type': 'application/json'},
+ method='POST')
+
+ if response.status == httplib.UNAUTHORIZED:
+ # Invalid credentials
+ raise InvalidCredsError()
+ elif response.status in [httplib.OK, httplib.CREATED]:
+ try:
+ body = json.loads(response.body)
+ return body['access']['token']['id']
+ except Exception:
+ e = sys.exc_info()[1]
+ raise MalformedResponseError('Failed to parse JSON', e)
+ else:
+ raise MalformedResponseError('Malformed response',
+ driver=self.driver)
+
+ def _get_tenant_name(self, token):
+ """
+ Get the first available tenant name (usually there are only one)
+ """
+ headers = {'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'X-Auth-Token': token}
+ response = self.request('/v2.0/tenants', headers=headers, method='GET')
+
+ if response.status == httplib.UNAUTHORIZED:
+ # Invalid credentials
+ raise InvalidCredsError()
+ elif response.status in [httplib.OK, httplib.CREATED]:
+ try:
+ body = json.loads(response.body)
+ return body["tenants"][0]["name"]
+ except Exception:
+ e = sys.exc_info()[1]
+ raise MalformedResponseError('Failed to parse JSON', e)
+ else:
+ raise MalformedResponseError('Malformed response',
+ driver=self.driver)
+
+ def _authenticate_2_0_with_body(self, reqbody):
+ resp = self.request('/v2.0/tokens', data=reqbody,
+ headers={'Content-Type': 'application/json'},
+ method='POST')
+
+ if resp.status == httplib.UNAUTHORIZED:
+ raise InvalidCredsError()
+ elif resp.status not in [httplib.OK,
+ httplib.NON_AUTHORITATIVE_INFORMATION]:
+ body = 'code: %s body: %s' % (resp.status, resp.body)
+ raise MalformedResponseError('Malformed response', body=body,
+ driver=self.driver)
+ else:
+ body = resp.object
+
+ try:
+ access = body['access']
+ expires = access['token']['expires']
+
+ self.auth_token = access['token']['id']
+ self.auth_token_expires = parse_date(expires)
+ self.urls = access['serviceCatalog']
+ self.auth_user_info = access.get('user', {})
+ except KeyError:
+ e = sys.exc_info()[1]
+ raise MalformedResponseError('Auth JSON response is \
+ missing required elements', e)
+
+ return self
+
+
def get_class_for_auth_version(auth_version):
"""
Retrieve class for the provided auth version.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/common/ovh.py
----------------------------------------------------------------------
diff --cc libcloud/common/ovh.py
index 0b342ab,3854e0b..0eae99b
--- a/libcloud/common/ovh.py
+++ b/libcloud/common/ovh.py
@@@ -102,7 -102,7 +102,7 @@@ class OvhConnection(ConnectionUserAndKe
'Content-Type': 'application/json',
'X-Ovh-Application': user_id,
}
- httpcon = LibcloudConnection(self.host)
- httpcon = LibcloudHTTPSConnection(self.host)
++ httpcon = LibcloudConnection(host=self.host, port=443)
httpcon.request(method='POST', url=action, body=data, headers=headers)
response = httpcon.getresponse()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/httplib_ssl.py
----------------------------------------------------------------------
diff --cc libcloud/httplib_ssl.py
index a4b603e,bbcf752..e1354e2
--- a/libcloud/httplib_ssl.py
+++ b/libcloud/httplib_ssl.py
@@@ -19,12 -19,19 +19,13 @@@ verification, depending on libcloud.sec
"""
import os
-import sys
import socket
-import ssl
-import base64
-import warnings
+
+import requests
import libcloud.security
-from libcloud.utils.py3 import b
+ from libcloud.utils.py3 import httplib
from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import urlunquote
-from libcloud.utils.py3 import match_hostname
-from libcloud.utils.py3 import CertificateError
__all__ = [
@@@ -144,31 -141,69 +145,31 @@@ class LibcloudBaseConnection(object)
return (proxy_scheme, proxy_host, proxy_port, proxy_username,
proxy_password)
- def _setup_http_proxy(self):
- """
- Set up HTTP proxy.
+ def _setup_verify(self):
+ self.verify = libcloud.security.VERIFY_SSL_CERT
- :param proxy_url: Proxy URL (e.g. http://<host>:3128)
- :type proxy_url: ``str``
- """
- headers = {}
-
- if self.proxy_username and self.proxy_password:
- # Include authentication header
- user_pass = '%s:%s' % (self.proxy_username, self.proxy_password)
- encoded = base64.encodestring(b(urlunquote(user_pass))).strip()
- auth_header = 'Basic %s' % (encoded.decode('utf-8'))
- headers['Proxy-Authorization'] = auth_header
-
- if hasattr(self, 'set_tunnel'):
- # Python 2.7 and higher
- # pylint: disable=no-member
- self.set_tunnel(host=self.host, port=self.port, headers=headers)
- elif hasattr(self, '_set_tunnel'):
- # Python 2.6
- # pylint: disable=no-member
- self._set_tunnel(host=self.host, port=self.port, headers=headers)
+ def _setup_ca_cert(self):
+ if self.verify is False:
+ pass
else:
- raise ValueError('Unsupported Python version')
+ self.ca_cert = libcloud.security.CA_CERTS_PATH
- self._set_hostport(host=self.proxy_host, port=self.proxy_port)
- class LibcloudConnection(LibcloudBaseConnection):
- def _activate_http_proxy(self, sock):
- self.sock = sock
- self._tunnel() # pylint: disable=no-member
++class LibcloudConnection(httplib.HTTPSConnection, LibcloudBaseConnection):
+ timeout = None
+ host = None
+ response = None
- def _set_hostport(self, host, port):
- """
- Backported from Python stdlib so Proxy support also works with
- Python 3.4.
- """
- if port is None:
- i = host.rfind(':')
- j = host.rfind(']') # ipv6 addresses have [...]
- if i > j:
- try:
- port = int(host[i + 1:])
- except ValueError:
- msg = "nonnumeric port: '%s'" % (host[i + 1:])
- raise httplib.InvalidURL(msg)
- host = host[:i]
- else:
- port = self.default_port # pylint: disable=no-member
- if host and host[0] == '[' and host[-1] == ']':
- host = host[1:-1]
- self.host = host
- self.port = port
-
-
-class LibcloudHTTPConnection(httplib.HTTPConnection, LibcloudBaseConnection):
- def __init__(self, *args, **kwargs):
+ def __init__(self, host, port, **kwargs):
- self.host = '{}://{}'.format(
++ self.host = '{0}://{1}'.format(
+ 'https' if port == 443 else 'http',
+ host
+ )
# Support for HTTP proxy
proxy_url_env = os.environ.get(HTTP_PROXY_ENV_VARIABLE_NAME, None)
proxy_url = kwargs.pop('proxy_url', proxy_url_env)
- super(LibcloudConnection, self).__init__()
- super(LibcloudHTTPConnection, self).__init__(*args, **kwargs)
++ LibcloudBaseConnection.__init__(self)
if proxy_url:
self.set_http_proxy(proxy_url=proxy_url)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/68bf8534/libcloud/test/backup/test_dimensiondata_v2_3.py
----------------------------------------------------------------------
diff --cc libcloud/test/backup/test_dimensiondata_v2_3.py
index 0000000,b0e581a..80f35a3
mode 000000,100644..100644
--- a/libcloud/test/backup/test_dimensiondata_v2_3.py
+++ b/libcloud/test/backup/test_dimensiondata_v2_3.py
@@@ -1,0 -1,503 +1,503 @@@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-import sys
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.dimensiondata import DimensionDataAPIException
-from libcloud.common.types import InvalidCredsError
-from libcloud.backup.base import BackupTargetJob
-from libcloud.backup.drivers.dimensiondata import DimensionDataBackupDriver as DimensionData
-from libcloud.backup.drivers.dimensiondata import DEFAULT_BACKUP_PLAN
-
-from libcloud.test import MockHttp, unittest
-from libcloud.test.backup import TestCaseMixin
-from libcloud.test.file_fixtures import BackupFileFixtures
-
-from libcloud.test.secrets import DIMENSIONDATA_PARAMS
-
-
-class DimensionData_v2_3_Tests(unittest.TestCase, TestCaseMixin):
-
- def setUp(self):
- DimensionData.connectionCls.active_api_version = '2.3'
- DimensionData.connectionCls.conn_classes = (None, DimensionDataMockHttp)
- DimensionDataMockHttp.type = None
- self.driver = DimensionData(*DIMENSIONDATA_PARAMS)
-
- def test_invalid_region(self):
- with self.assertRaises(ValueError):
- self.driver = DimensionData(*DIMENSIONDATA_PARAMS, region='blah')
-
- def test_invalid_creds(self):
- DimensionDataMockHttp.type = 'UNAUTHORIZED'
- with self.assertRaises(InvalidCredsError):
- self.driver.list_targets()
-
- def test_list_targets(self):
- targets = self.driver.list_targets()
- self.assertEqual(len(targets), 2)
- self.assertEqual(targets[0].id, '5579f3a7-4c32-4cf5-8a7e-b45c36a35c10')
- self.assertEqual(targets[0].address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(targets[0].extra['servicePlan'], 'Enterprise')
-
- def test_create_target(self):
- target = self.driver.create_target(
- 'name',
- 'e75ead52-692f-4314-8725-c8a4f4d13a87',
- extra={'servicePlan': 'Enterprise'})
- self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
- self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(target.extra['servicePlan'], 'Enterprise')
-
- def test_create_target_DEFAULT(self):
- DimensionDataMockHttp.type = 'DEFAULT'
- target = self.driver.create_target(
- 'name',
- 'e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
- self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
-
- def test_create_target_EXISTS(self):
- DimensionDataMockHttp.type = 'EXISTS'
- with self.assertRaises(DimensionDataAPIException) as context:
- self.driver.create_target(
- 'name',
- 'e75ead52-692f-4314-8725-c8a4f4d13a87',
- extra={'servicePlan': 'Enterprise'})
- self.assertEqual(context.exception.code, 'ERROR')
- self.assertEqual(context.exception.msg, 'Cloud backup for this server is already enabled or being enabled (state: NORMAL).')
-
- def test_update_target(self):
- target = self.driver.list_targets()[0]
- extra = {'servicePlan': 'Essentials'}
- new_target = self.driver.update_target(target, extra=extra)
- self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
-
- def test_update_target_DEFAULT(self):
- DimensionDataMockHttp.type = 'DEFAULT'
- target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
- self.driver.update_target(target)
-
- def test_update_target_STR(self):
- target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
- extra = {'servicePlan': 'Essentials'}
- new_target = self.driver.update_target(target, extra=extra)
- self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
-
- def test_delete_target(self):
- target = self.driver.list_targets()[0]
- self.assertTrue(self.driver.delete_target(target))
-
- def test_ex_add_client_to_target(self):
- target = self.driver.list_targets()[0]
- client = self.driver.ex_list_available_client_types(target)[0]
- storage_policy = self.driver.ex_list_available_storage_policies(target)[0]
- schedule_policy = self.driver.ex_list_available_schedule_policies(target)[0]
- self.assertTrue(
- self.driver.ex_add_client_to_target(target, client, storage_policy,
- schedule_policy, 'ON_FAILURE', 'nobody@example.com')
- )
-
- 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',
- '12AM - 6AM', 'ON_FAILURE', 'nobody@example.com')
- )
-
- def test_ex_get_backup_details_for_target(self):
- target = self.driver.list_targets()[0]
- response = self.driver.ex_get_backup_details_for_target(target)
- self.assertEqual(response.service_plan, 'Enterprise')
- client = response.clients[0]
- self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
- self.assertEqual(client.type.type, 'FA.Linux')
- self.assertEqual(client.running_job.progress, 5)
- self.assertTrue(isinstance(client.running_job, BackupTargetJob))
- self.assertEqual(len(client.alert.notify_list), 2)
- self.assertTrue(isinstance(client.alert.notify_list, list))
-
- def test_ex_get_backup_details_for_target_NOBACKUP(self):
- target = self.driver.list_targets()[0].address
- DimensionDataMockHttp.type = 'NOBACKUP'
- response = self.driver.ex_get_backup_details_for_target(target)
- self.assertTrue(response is None)
-
- def test_ex_cancel_target_job(self):
- target = self.driver.list_targets()[0]
- response = self.driver.ex_get_backup_details_for_target(target)
- client = response.clients[0]
- self.assertTrue(isinstance(client.running_job, BackupTargetJob))
- success = client.running_job.cancel()
- self.assertTrue(success)
-
- def test_ex_cancel_target_job_with_extras(self):
- success = self.driver.cancel_target_job(
- None,
- ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
- ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
- )
- self.assertTrue(success)
-
- def test_ex_cancel_target_job_FAIL(self):
- DimensionDataMockHttp.type = 'FAIL'
- with self.assertRaises(DimensionDataAPIException) as context:
- self.driver.cancel_target_job(
- None,
- ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
- ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
- )
- self.assertEqual(context.exception.code, 'ERROR')
-
- """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')
- self.assertEqual(response.service_plan, 'Essentials')
- self.assertEqual(len(response.clients), 0)
-
- """Test a backup details that has a client, but no alerting or running jobs"""
- def test_ex_get_backup_details_for_target_NO_JOB_OR_ALERT(self):
- DimensionDataMockHttp.type = 'NOJOB'
- response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
- self.assertEqual(response.service_plan, 'Enterprise')
- self.assertTrue(isinstance(response.clients, list))
- self.assertEqual(len(response.clients), 1)
- client = response.clients[0]
- self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
- self.assertEqual(client.type.type, 'FA.Linux')
- self.assertIsNone(client.running_job)
- self.assertIsNone(client.alert)
-
- """Test getting backup info for a server that doesn't exist"""
- 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.assertEqual(context.exception.code, 'ERROR')
- 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]
- answer = self.driver.ex_list_available_client_types(target)
- self.assertEqual(len(answer), 1)
- self.assertEqual(answer[0].type, 'FA.Linux')
- self.assertEqual(answer[0].is_file_system, True)
- self.assertEqual(answer[0].description, 'Linux File system')
-
- def test_ex_list_available_storage_policies(self):
- target = self.driver.list_targets()[0]
- answer = self.driver.ex_list_available_storage_policies(target)
- self.assertEqual(len(answer), 1)
- self.assertEqual(answer[0].name,
- '30 Day Storage Policy + Secondary Copy')
- self.assertEqual(answer[0].retention_period, 30)
- self.assertEqual(answer[0].secondary_location, 'Primary')
-
- def test_ex_list_available_schedule_policies(self):
- target = self.driver.list_targets()[0]
- answer = self.driver.ex_list_available_schedule_policies(target)
- self.assertEqual(len(answer), 1)
- 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_FAIL(self):
- DimensionDataMockHttp.type = 'FAIL'
- 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):
- super(InvalidRequestError, self).__init__("Invalid Request - %s" % tag)
-
-
-class DimensionDataMockHttp(MockHttp):
-
- fixtures = BackupFileFixtures('dimensiondata')
-
- def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers):
- return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED])
-
- def _oec_0_9_myaccount(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_EXISTS(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_DEFAULT(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_INPROGRESS(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_FAIL(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])
-
- def _oec_0_9_myaccount_DISABLED(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_NOJOB(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87(self, method, url, body, headers):
- body = self.fixtures.load(
- 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT(self, method, url, body, headers):
- body = self.fixtures.load(
- 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOCLIENT(self, method, url, body, headers):
- body = self.fixtures.load(
- 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOJOB(self, method, url, body, headers):
- body = self.fixtures.load(
- 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DISABLED(self, method, url, body, headers):
- body = self.fixtures.load(
- 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server(self, method, url, body, headers):
- body = self.fixtures.load(
- 'server_server.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_type(self, method, url, body, headers):
- body = self.fixtures.load(
- '_backup_client_type.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_storagePolicy(
- self, method, url, body, headers):
- body = self.fixtures.load(
- '_backup_client_storagePolicy.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_schedulePolicy(
- self, method, url, body, headers):
- body = self.fixtures.load(
- '_backup_client_schedulePolicy.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(
- self, method, url, body, headers):
- if method == 'POST':
- body = self.fixtures.load(
- '_backup_client_SUCCESS_PUT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- else:
- raise ValueError("Unknown Method {0}".format(method))
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOCLIENT(
- self, method, url, body, headers):
- # only gets here are implemented
- # If we get any other method something has gone wrong
- assert(method == 'GET')
- body = self.fixtures.load(
- '_backup_INFO_NOCLIENT.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_DISABLED(
- self, method, url, body, headers):
- # only gets here are implemented
- # If we get any other method something has gone wrong
- assert(method == 'GET')
- body = self.fixtures.load(
- '_backup_INFO_DISABLED.xml')
- return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOJOB(
- self, method, url, body, headers):
- # only gets here are implemented
- # If we get any other method something has gone wrong
- assert(method == 'GET')
- body = self.fixtures.load(
- '_backup_INFO_NOJOB.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_DEFAULT(
- self, method, url, body, headers):
- if method != 'POST':
- raise InvalidRequestError('Only POST is accepted for this test')
- request = ET.fromstring(body)
- service_plan = request.get('servicePlan')
- if service_plan != DEFAULT_BACKUP_PLAN:
- raise InvalidRequestError('The default plan %s should have been passed in. Not %s' % (DEFAULT_BACKUP_PLAN, service_plan))
- body = self.fixtures.load(
- '_backup_ENABLE.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(
- self, method, url, body, headers):
- if method == 'POST':
- body = self.fixtures.load(
- '_backup_ENABLE.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- elif method == 'GET':
- if url.endswith('disable'):
- body = self.fixtures.load(
- '_backup_DISABLE.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- body = self.fixtures.load(
- '_backup_INFO.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- else:
- raise ValueError("Unknown Method {0}".format(method))
-
- def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOBACKUP(
- self, method, url, body, headers):
- assert(method == 'GET')
- body = self.fixtures.load('server_server_NOBACKUP.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_EXISTS(
- self, method, url, body, headers):
- # only POSTs are implemented
- # If we get any other method something has gone wrong
- assert(method == 'POST')
- body = self.fixtures.load(
- '_backup_EXISTS.xml')
- return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify(
- self, method, url, body, headers):
- request = ET.fromstring(body)
- service_plan = request.get('servicePlan')
- if service_plan != 'Essentials':
- raise InvalidRequestError("Expected Essentials backup plan in request")
- body = self.fixtures.load('_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_modify_DEFAULT(
- self, method, url, body, headers):
- request = ET.fromstring(body)
- service_plan = request.get('servicePlan')
- if service_plan != DEFAULT_BACKUP_PLAN:
- raise InvalidRequestError("Expected % backup plan in test" % DEFAULT_BACKUP_PLAN)
- body = self.fixtures.load('_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):
- if url.endswith('disable'):
- body = self.fixtures.load(
- ('_remove_backup_client.xml')
- )
- elif url.endswith('cancelJob'):
- body = self.fixtures.load(
- (''
- '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml')
- )
- else:
- raise ValueError("Unknown URL: %s" % url)
- 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_FAIL(
- self, method, url, body, headers):
- if url.endswith('disable'):
- body = self.fixtures.load(
- ('_remove_backup_client_FAIL.xml')
- )
- elif url.endswith('cancelJob'):
- body = self.fixtures.load(
- (''
- '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml')
- )
- else:
- raise ValueError("Unknown URL: %s" % url)
- return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
++# Licensed to the Apache Software Foundation (ASF) under one or more
++# contributor license agreements. See the NOTICE file distributed with
++# this work for additional information regarding copyright ownership.
++# The ASF licenses this file to You under the Apache License, Version 2.0
++# (the "License"); you may not use this file except in compliance with
++# the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
++try:
++ from lxml import etree as ET
++except ImportError:
++ from xml.etree import ElementTree as ET
++
++import sys
++from libcloud.utils.py3 import httplib
++
++from libcloud.common.dimensiondata import DimensionDataAPIException
++from libcloud.common.types import InvalidCredsError
++from libcloud.backup.base import BackupTargetJob
++from libcloud.backup.drivers.dimensiondata import DimensionDataBackupDriver as DimensionData
++from libcloud.backup.drivers.dimensiondata import DEFAULT_BACKUP_PLAN
++
++from libcloud.test import MockHttp, unittest
++from libcloud.test.backup import TestCaseMixin
++from libcloud.test.file_fixtures import BackupFileFixtures
++
++from libcloud.test.secrets import DIMENSIONDATA_PARAMS
++
++
++class DimensionDataTests(unittest.TestCase, TestCaseMixin):
++
++ def setUp(self):
++ DimensionData.connectionCls.active_api_version = '2.3'
++ DimensionData.connectionCls.conn_class = DimensionDataMockHttp
++ DimensionDataMockHttp.type = None
++ self.driver = DimensionData(*DIMENSIONDATA_PARAMS)
++
++ def test_invalid_region(self):
++ with self.assertRaises(ValueError):
++ self.driver = DimensionData(*DIMENSIONDATA_PARAMS, region='blah')
++
++ def test_invalid_creds(self):
++ DimensionDataMockHttp.type = 'UNAUTHORIZED'
++ with self.assertRaises(InvalidCredsError):
++ self.driver.list_targets()
++
++ def test_list_targets(self):
++ targets = self.driver.list_targets()
++ self.assertEqual(len(targets), 2)
++ self.assertEqual(targets[0].id, '5579f3a7-4c32-4cf5-8a7e-b45c36a35c10')
++ self.assertEqual(targets[0].address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
++ self.assertEqual(targets[0].extra['servicePlan'], 'Enterprise')
++
++ def test_create_target(self):
++ target = self.driver.create_target(
++ 'name',
++ 'e75ead52-692f-4314-8725-c8a4f4d13a87',
++ extra={'servicePlan': 'Enterprise'})
++ self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
++ self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
++ self.assertEqual(target.extra['servicePlan'], 'Enterprise')
++
++ def test_create_target_DEFAULT(self):
++ DimensionDataMockHttp.type = 'DEFAULT'
++ target = self.driver.create_target(
++ 'name',
++ 'e75ead52-692f-4314-8725-c8a4f4d13a87')
++ self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
++ self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
++
++ def test_create_target_EXISTS(self):
++ DimensionDataMockHttp.type = 'EXISTS'
++ with self.assertRaises(DimensionDataAPIException) as context:
++ self.driver.create_target(
++ 'name',
++ 'e75ead52-692f-4314-8725-c8a4f4d13a87',
++ extra={'servicePlan': 'Enterprise'})
++ self.assertEqual(context.exception.code, 'ERROR')
++ self.assertEqual(context.exception.msg, 'Cloud backup for this server is already enabled or being enabled (state: NORMAL).')
++
++ def test_update_target(self):
++ target = self.driver.list_targets()[0]
++ extra = {'servicePlan': 'Essentials'}
++ new_target = self.driver.update_target(target, extra=extra)
++ self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
++
++ def test_update_target_DEFAULT(self):
++ DimensionDataMockHttp.type = 'DEFAULT'
++ target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
++ self.driver.update_target(target)
++
++ def test_update_target_STR(self):
++ target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
++ extra = {'servicePlan': 'Essentials'}
++ new_target = self.driver.update_target(target, extra=extra)
++ self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
++
++ def test_delete_target(self):
++ target = self.driver.list_targets()[0]
++ self.assertTrue(self.driver.delete_target(target))
++
++ def test_ex_add_client_to_target(self):
++ target = self.driver.list_targets()[0]
++ client = self.driver.ex_list_available_client_types(target)[0]
++ storage_policy = self.driver.ex_list_available_storage_policies(target)[0]
++ schedule_policy = self.driver.ex_list_available_schedule_policies(target)[0]
++ self.assertTrue(
++ self.driver.ex_add_client_to_target(target, client, storage_policy,
++ schedule_policy, 'ON_FAILURE', 'nobody@example.com')
++ )
++
++ 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',
++ '12AM - 6AM', 'ON_FAILURE', 'nobody@example.com')
++ )
++
++ def test_ex_get_backup_details_for_target(self):
++ target = self.driver.list_targets()[0]
++ response = self.driver.ex_get_backup_details_for_target(target)
++ self.assertEqual(response.service_plan, 'Enterprise')
++ client = response.clients[0]
++ self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
++ self.assertEqual(client.type.type, 'FA.Linux')
++ self.assertEqual(client.running_job.progress, 5)
++ self.assertTrue(isinstance(client.running_job, BackupTargetJob))
++ self.assertEqual(len(client.alert.notify_list), 2)
++ self.assertTrue(isinstance(client.alert.notify_list, list))
++
++ def test_ex_get_backup_details_for_target_NOBACKUP(self):
++ target = self.driver.list_targets()[0].address
++ DimensionDataMockHttp.type = 'NOBACKUP'
++ response = self.driver.ex_get_backup_details_for_target(target)
++ self.assertTrue(response is None)
++
++ def test_ex_cancel_target_job(self):
++ target = self.driver.list_targets()[0]
++ response = self.driver.ex_get_backup_details_for_target(target)
++ client = response.clients[0]
++ self.assertTrue(isinstance(client.running_job, BackupTargetJob))
++ success = client.running_job.cancel()
++ self.assertTrue(success)
++
++ def test_ex_cancel_target_job_with_extras(self):
++ success = self.driver.cancel_target_job(
++ None,
++ ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
++ ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
++ )
++ self.assertTrue(success)
++
++ def test_ex_cancel_target_job_FAIL(self):
++ DimensionDataMockHttp.type = 'FAIL'
++ with self.assertRaises(DimensionDataAPIException) as context:
++ self.driver.cancel_target_job(
++ None,
++ ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
++ ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
++ )
++ self.assertEqual(context.exception.code, 'ERROR')
++
++ """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')
++ self.assertEqual(response.service_plan, 'Essentials')
++ self.assertEqual(len(response.clients), 0)
++
++ """Test a backup details that has a client, but no alerting or running jobs"""
++ def test_ex_get_backup_details_for_target_NO_JOB_OR_ALERT(self):
++ DimensionDataMockHttp.type = 'NOJOB'
++ response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
++ self.assertEqual(response.service_plan, 'Enterprise')
++ self.assertTrue(isinstance(response.clients, list))
++ self.assertEqual(len(response.clients), 1)
++ client = response.clients[0]
++ self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
++ self.assertEqual(client.type.type, 'FA.Linux')
++ self.assertIsNone(client.running_job)
++ self.assertIsNone(client.alert)
++
++ """Test getting backup info for a server that doesn't exist"""
++ 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.assertEqual(context.exception.code, 'ERROR')
++ 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]
++ answer = self.driver.ex_list_available_client_types(target)
++ self.assertEqual(len(answer), 1)
++ self.assertEqual(answer[0].type, 'FA.Linux')
++ self.assertEqual(answer[0].is_file_system, True)
++ self.assertEqual(answer[0].description, 'Linux File system')
++
++ def test_ex_list_available_storage_policies(self):
++ target = self.driver.list_targets()[0]
++ answer = self.driver.ex_list_available_storage_policies(target)
++ self.assertEqual(len(answer), 1)
++ self.assertEqual(answer[0].name,
++ '30 Day Storage Policy + Secondary Copy')
++ self.assertEqual(answer[0].retention_period, 30)
++ self.assertEqual(answer[0].secondary_location, 'Primary')
++
++ def test_ex_list_available_schedule_policies(self):
++ target = self.driver.list_targets()[0]
++ answer = self.driver.ex_list_available_schedule_policies(target)
++ self.assertEqual(len(answer), 1)
++ 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_FAIL(self):
++ DimensionDataMockHttp.type = 'FAIL'
++ 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):
++ super(InvalidRequestError, self).__init__("Invalid Request - %s" % tag)
++
++
++class DimensionDataMockHttp(MockHttp):
++
++ fixtures = BackupFileFixtures('dimensiondata')
++
++ def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers):
++ return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED])
++
++ def _oec_0_9_myaccount(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_EXISTS(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_DEFAULT(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_INPROGRESS(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_FAIL(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])
++
++ def _oec_0_9_myaccount_DISABLED(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_NOJOB(self, method, url, body, headers):
++ body = self.fixtures.load('oec_0_9_myaccount.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87(self, method, url, body, headers):
++ body = self.fixtures.load(
++ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT(self, method, url, body, headers):
++ body = self.fixtures.load(
++ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOCLIENT(self, method, url, body, headers):
++ body = self.fixtures.load(
++ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOJOB(self, method, url, body, headers):
++ body = self.fixtures.load(
++ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DISABLED(self, method, url, body, headers):
++ body = self.fixtures.load(
++ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server(self, method, url, body, headers):
++ body = self.fixtures.load(
++ 'server_server.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_type(self, method, url, body, headers):
++ body = self.fixtures.load(
++ '_backup_client_type.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_storagePolicy(
++ self, method, url, body, headers):
++ body = self.fixtures.load(
++ '_backup_client_storagePolicy.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_schedulePolicy(
++ self, method, url, body, headers):
++ body = self.fixtures.load(
++ '_backup_client_schedulePolicy.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(
++ self, method, url, body, headers):
++ if method == 'POST':
++ body = self.fixtures.load(
++ '_backup_client_SUCCESS_PUT.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++ else:
++ raise ValueError("Unknown Method {0}".format(method))
++
++ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOCLIENT(
++ self, method, url, body, headers):
++ # only gets here are implemented
++ # If we get any other method something has gone wrong
++ assert(method == 'GET')
++ body = self.fixtures.load(
++ '_backup_INFO_NOCLIENT.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_DISABLED(
++ self, method, url, body, headers):
++ # only gets here are implemented
++ # If we get any other method something has gone wrong
++ assert(method == 'GET')
++ body = self.fixtures.load(
++ '_backup_INFO_DISABLED.xml')
++ return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
++
++ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOJOB(
++ self, method, url, body, headers):
++ # only gets here are implemented
++ # If we get any other method something has gone wrong
++ assert(method == 'GET')
++ body = self.fixtures.load(
++ '_backup_INFO_NOJOB.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_DEFAULT(
++ self, method, url, body, headers):
++ if method != 'POST':
++ raise InvalidRequestError('Only POST is accepted for this test')
++ request = ET.fromstring(body)
++ service_plan = request.get('servicePlan')
++ if service_plan != DEFAULT_BACKUP_PLAN:
++ raise InvalidRequestError('The default plan %s should have been passed in. Not %s' % (DEFAULT_BACKUP_PLAN, service_plan))
++ body = self.fixtures.load(
++ '_backup_ENABLE.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(
++ self, method, url, body, headers):
++ if method == 'POST':
++ body = self.fixtures.load(
++ '_backup_ENABLE.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++ elif method == 'GET':
++ if url.endswith('disable'):
++ body = self.fixtures.load(
++ '_backup_DISABLE.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++ body = self.fixtures.load(
++ '_backup_INFO.xml')
++ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
++
++ else:
++ raise ValueError("Unknown Method {0}".format(method))
++
++ def _caas_2_3_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOBACKUP(
++ self, method, url, body, headers):
++ assert(method == 'GET')
++ body = self.fixtures.load('server_server_NOBACKUP.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_EXISTS(
++ self, method, url, body, headers):
++ # only POSTs are implemented
++ # If we get any other method something has gone wrong
++ assert(method == 'POST')
++ body = self.fixtures.load(
++ '_backup_EXISTS.xml')
++ return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
++
++ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify(
++ self, method, url, body, headers):
++ request = ET.fromstring(body)
++ service_plan = request.get('servicePlan')
++ if service_plan != 'Essentials':
++ raise InvalidRequestError("Expected Essentials backup plan in request")
++ body = self.fixtures.load('_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_modify_DEFAULT(
++ self, method, url, body, headers):
++ request = ET.fromstring(body)
++ service_plan = request.get('servicePlan')
++ if service_plan != DEFAULT_BACKUP_PLAN:
++ raise InvalidRequestError("Expected % backup plan in test" % DEFAULT_BACKUP_PLAN)
++ body = self.fixtures.load('_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):
++ if url.endswith('disable'):
++ body = self.fixtures.load(
++ ('_remove_backup_client.xml')
++ )
++ elif url.endswith('cancelJob'):
++ body = self.fixtures.load(
++ (''
++ '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml')
++ )
++ else:
++ raise ValueError("Unknown URL: %s" % url)
++ 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_FAIL(
++ self, method, url, body, headers):
++ if url.endswith('disable'):
++ body = self.fixtures.load(
++ ('_remove_backup_client_FAIL.xml')
++ )
++ elif url.endswith('cancelJob'):
++ body = self.fixtures.load(
++ (''
++ '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml')
++ )
++ else:
++ raise ValueError("Unknown URL: %s" % url)
++ 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/68bf8534/libcloud/test/backup/test_dimensiondata_v2_4.py
----------------------------------------------------------------------
diff --cc libcloud/test/backup/test_dimensiondata_v2_4.py
index 0000000,285ac40..4fbed27
mode 000000,100644..100644
--- a/libcloud/test/backup/test_dimensiondata_v2_4.py
+++ b/libcloud/test/backup/test_dimensiondata_v2_4.py
@@@ -1,0 -1,503 +1,503 @@@
+ # Licensed to the Apache Software Foundation (ASF) under one or more
+ # contributor license agreements. See the NOTICE file distributed with
+ # this work for additional information regarding copyright ownership.
+ # The ASF licenses this file to You under the Apache License, Version 2.0
+ # (the "License"); you may not use this file except in compliance with
+ # the License. You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+
+ try:
+ from lxml import etree as ET
+ except ImportError:
+ from xml.etree import ElementTree as ET
+
+ import sys
+ from libcloud.utils.py3 import httplib
+
+ from libcloud.common.dimensiondata import DimensionDataAPIException
+ from libcloud.common.types import InvalidCredsError
+ from libcloud.backup.base import BackupTargetJob
+ from libcloud.backup.drivers.dimensiondata import DimensionDataBackupDriver as DimensionData
+ from libcloud.backup.drivers.dimensiondata import DEFAULT_BACKUP_PLAN
+
+ from libcloud.test import MockHttp, unittest
+ from libcloud.test.backup import TestCaseMixin
+ from libcloud.test.file_fixtures import BackupFileFixtures
+
+ from libcloud.test.secrets import DIMENSIONDATA_PARAMS
+
+
+ class DimensionData_v2_4_Tests(unittest.TestCase, TestCaseMixin):
+
+ def setUp(self):
+ DimensionData.connectionCls.active_api_version = '2.4'
- DimensionData.connectionCls.conn_classes = (None, DimensionDataMockHttp)
++ DimensionData.connectionCls.conn_class = DimensionDataMockHttp
+ DimensionDataMockHttp.type = None
+ self.driver = DimensionData(*DIMENSIONDATA_PARAMS)
+
+ def test_invalid_region(self):
+ with self.assertRaises(ValueError):
+ self.driver = DimensionData(*DIMENSIONDATA_PARAMS, region='blah')
+
+ def test_invalid_creds(self):
+ DimensionDataMockHttp.type = 'UNAUTHORIZED'
+ with self.assertRaises(InvalidCredsError):
+ self.driver.list_targets()
+
+ def test_list_targets(self):
+ targets = self.driver.list_targets()
+ self.assertEqual(len(targets), 2)
+ self.assertEqual(targets[0].id, '5579f3a7-4c32-4cf5-8a7e-b45c36a35c10')
+ self.assertEqual(targets[0].address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+ self.assertEqual(targets[0].extra['servicePlan'], 'Enterprise')
+
+ def test_create_target(self):
+ target = self.driver.create_target(
+ 'name',
+ 'e75ead52-692f-4314-8725-c8a4f4d13a87',
+ extra={'servicePlan': 'Enterprise'})
+ self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
+ self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+ self.assertEqual(target.extra['servicePlan'], 'Enterprise')
+
+ def test_create_target_DEFAULT(self):
+ DimensionDataMockHttp.type = 'DEFAULT'
+ target = self.driver.create_target(
+ 'name',
+ 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+ self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
+ self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
+
+ def test_create_target_EXISTS(self):
+ DimensionDataMockHttp.type = 'EXISTS'
+ with self.assertRaises(DimensionDataAPIException) as context:
+ self.driver.create_target(
+ 'name',
+ 'e75ead52-692f-4314-8725-c8a4f4d13a87',
+ extra={'servicePlan': 'Enterprise'})
+ self.assertEqual(context.exception.code, 'ERROR')
+ self.assertEqual(context.exception.msg, 'Cloud backup for this server is already enabled or being enabled (state: NORMAL).')
+
+ def test_update_target(self):
+ target = self.driver.list_targets()[0]
+ extra = {'servicePlan': 'Essentials'}
+ new_target = self.driver.update_target(target, extra=extra)
+ self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
+
+ def test_update_target_DEFAULT(self):
+ DimensionDataMockHttp.type = 'DEFAULT'
+ target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
+ self.driver.update_target(target)
+
+ def test_update_target_STR(self):
+ target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
+ extra = {'servicePlan': 'Essentials'}
+ new_target = self.driver.update_target(target, extra=extra)
+ self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
+
+ def test_delete_target(self):
+ target = self.driver.list_targets()[0]
+ self.assertTrue(self.driver.delete_target(target))
+
+ def test_ex_add_client_to_target(self):
+ target = self.driver.list_targets()[0]
+ client = self.driver.ex_list_available_client_types(target)[0]
+ storage_policy = self.driver.ex_list_available_storage_policies(target)[0]
+ schedule_policy = self.driver.ex_list_available_schedule_policies(target)[0]
+ self.assertTrue(
+ self.driver.ex_add_client_to_target(target, client, storage_policy,
+ schedule_policy, 'ON_FAILURE', 'nobody@example.com')
+ )
+
+ 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',
+ '12AM - 6AM', 'ON_FAILURE', 'nobody@example.com')
+ )
+
+ def test_ex_get_backup_details_for_target(self):
+ target = self.driver.list_targets()[0]
+ response = self.driver.ex_get_backup_details_for_target(target)
+ self.assertEqual(response.service_plan, 'Enterprise')
+ client = response.clients[0]
+ self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
+ self.assertEqual(client.type.type, 'FA.Linux')
+ self.assertEqual(client.running_job.progress, 5)
+ self.assertTrue(isinstance(client.running_job, BackupTargetJob))
+ self.assertEqual(len(client.alert.notify_list), 2)
+ self.assertTrue(isinstance(client.alert.notify_list, list))
+
+ def test_ex_get_backup_details_for_target_NOBACKUP(self):
+ target = self.driver.list_targets()[0].address
+ DimensionDataMockHttp.type = 'NOBACKUP'
+ response = self.driver.ex_get_backup_details_for_target(target)
+ self.assertTrue(response is None)
+
+ def test_ex_cancel_target_job(self):
+ target = self.driver.list_targets()[0]
+ response = self.driver.ex_get_backup_details_for_target(target)
+ client = response.clients[0]
+ self.assertTrue(isinstance(client.running_job, BackupTargetJob))
+ success = client.running_job.cancel()
+ self.assertTrue(success)
+
+ def test_ex_cancel_target_job_with_extras(self):
+ success = self.driver.cancel_target_job(
+ None,
+ ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
+ ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
+ )
+ self.assertTrue(success)
+
+ def test_ex_cancel_target_job_FAIL(self):
+ DimensionDataMockHttp.type = 'FAIL'
+ with self.assertRaises(DimensionDataAPIException) as context:
+ self.driver.cancel_target_job(
+ None,
+ ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
+ ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
+ )
+ self.assertEqual(context.exception.code, 'ERROR')
+
+ """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')
+ self.assertEqual(response.service_plan, 'Essentials')
+ self.assertEqual(len(response.clients), 0)
+
+ """Test a backup details that has a client, but no alerting or running jobs"""
+ def test_ex_get_backup_details_for_target_NO_JOB_OR_ALERT(self):
+ DimensionDataMockHttp.type = 'NOJOB'
+ response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
+ self.assertEqual(response.service_plan, 'Enterprise')
+ self.assertTrue(isinstance(response.clients, list))
+ self.assertEqual(len(response.clients), 1)
+ client = response.clients[0]
+ self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
+ self.assertEqual(client.type.type, 'FA.Linux')
+ self.assertIsNone(client.running_job)
+ self.assertIsNone(client.alert)
+
+ """Test getting backup info for a server that doesn't exist"""
+ 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.assertEqual(context.exception.code, 'ERROR')
+ 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]
+ answer = self.driver.ex_list_available_client_types(target)
+ self.assertEqual(len(answer), 1)
+ self.assertEqual(answer[0].type, 'FA.Linux')
+ self.assertEqual(answer[0].is_file_system, True)
+ self.assertEqual(answer[0].description, 'Linux File system')
+
+ def test_ex_list_available_storage_policies(self):
+ target = self.driver.list_targets()[0]
+ answer = self.driver.ex_list_available_storage_policies(target)
+ self.assertEqual(len(answer), 1)
+ self.assertEqual(answer[0].name,
+ '30 Day Storage Policy + Secondary Copy')
+ self.assertEqual(answer[0].retention_period, 30)
+ self.assertEqual(answer[0].secondary_location, 'Primary')
+
+ def test_ex_list_available_schedule_policies(self):
+ target = self.driver.list_targets()[0]
+ answer = self.driver.ex_list_available_schedule_policies(target)
+ self.assertEqual(len(answer), 1)
+ 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_FAIL(self):
+ DimensionDataMockHttp.type = 'FAIL'
+ 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):
+ super(InvalidRequestError, self).__init__("Invalid Request - %s" % tag)
+
+
+ class DimensionDataMockHttp(MockHttp):
+
+ fixtures = BackupFileFixtures('dimensiondata')
+
+ def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers):
+ return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED])
+
+ def _oec_0_9_myaccount(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_EXISTS(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_DEFAULT(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_INPROGRESS(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_FAIL(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])
+
+ def _oec_0_9_myaccount_DISABLED(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_NOJOB(self, method, url, body, headers):
+ body = self.fixtures.load('oec_0_9_myaccount.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87(self, method, url, body, headers):
+ body = self.fixtures.load(
+ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT(self, method, url, body, headers):
+ body = self.fixtures.load(
+ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOCLIENT(self, method, url, body, headers):
+ body = self.fixtures.load(
+ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOJOB(self, method, url, body, headers):
+ body = self.fixtures.load(
+ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DISABLED(self, method, url, body, headers):
+ body = self.fixtures.load(
+ 'server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server(self, method, url, body, headers):
+ body = self.fixtures.load(
+ 'server_server.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_type(self, method, url, body, headers):
+ body = self.fixtures.load(
+ '_backup_client_type.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_storagePolicy(
+ self, method, url, body, headers):
+ body = self.fixtures.load(
+ '_backup_client_storagePolicy.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_schedulePolicy(
+ self, method, url, body, headers):
+ body = self.fixtures.load(
+ '_backup_client_schedulePolicy.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(
+ self, method, url, body, headers):
+ if method == 'POST':
+ body = self.fixtures.load(
+ '_backup_client_SUCCESS_PUT.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ else:
+ raise ValueError("Unknown Method {0}".format(method))
+
+ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOCLIENT(
+ self, method, url, body, headers):
+ # only gets here are implemented
+ # If we get any other method something has gone wrong
+ assert(method == 'GET')
+ body = self.fixtures.load(
+ '_backup_INFO_NOCLIENT.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_DISABLED(
+ self, method, url, body, headers):
+ # only gets here are implemented
+ # If we get any other method something has gone wrong
+ assert(method == 'GET')
+ body = self.fixtures.load(
+ '_backup_INFO_DISABLED.xml')
+ return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
+ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOJOB(
+ self, method, url, body, headers):
+ # only gets here are implemented
+ # If we get any other method something has gone wrong
+ assert(method == 'GET')
+ body = self.fixtures.load(
+ '_backup_INFO_NOJOB.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_DEFAULT(
+ self, method, url, body, headers):
+ if method != 'POST':
+ raise InvalidRequestError('Only POST is accepted for this test')
+ request = ET.fromstring(body)
+ service_plan = request.get('servicePlan')
+ if service_plan != DEFAULT_BACKUP_PLAN:
+ raise InvalidRequestError('The default plan %s should have been passed in. Not %s' % (DEFAULT_BACKUP_PLAN, service_plan))
+ body = self.fixtures.load(
+ '_backup_ENABLE.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(
+ self, method, url, body, headers):
+ if method == 'POST':
+ body = self.fixtures.load(
+ '_backup_ENABLE.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ elif method == 'GET':
+ if url.endswith('disable'):
+ body = self.fixtures.load(
+ '_backup_DISABLE.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ body = self.fixtures.load(
+ '_backup_INFO.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ else:
+ raise ValueError("Unknown Method {0}".format(method))
+
+ def _caas_2_4_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOBACKUP(
+ self, method, url, body, headers):
+ assert(method == 'GET')
+ body = self.fixtures.load('server_server_NOBACKUP.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_EXISTS(
+ self, method, url, body, headers):
+ # only POSTs are implemented
+ # If we get any other method something has gone wrong
+ assert(method == 'POST')
+ body = self.fixtures.load(
+ '_backup_EXISTS.xml')
+ return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
+
+ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify(
+ self, method, url, body, headers):
+ request = ET.fromstring(body)
+ service_plan = request.get('servicePlan')
+ if service_plan != 'Essentials':
+ raise InvalidRequestError("Expected Essentials backup plan in request")
+ body = self.fixtures.load('_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_modify_DEFAULT(
+ self, method, url, body, headers):
+ request = ET.fromstring(body)
+ service_plan = request.get('servicePlan')
+ if service_plan != DEFAULT_BACKUP_PLAN:
+ raise InvalidRequestError("Expected % backup plan in test" % DEFAULT_BACKUP_PLAN)
+ body = self.fixtures.load('_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):
+ if url.endswith('disable'):
+ body = self.fixtures.load(
+ ('_remove_backup_client.xml')
+ )
+ elif url.endswith('cancelJob'):
+ body = self.fixtures.load(
+ (''
+ '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml')
+ )
+ else:
+ raise ValueError("Unknown URL: %s" % url)
+ 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_FAIL(
+ self, method, url, body, headers):
+ if url.endswith('disable'):
+ body = self.fixtures.load(
+ ('_remove_backup_client_FAIL.xml')
+ )
+ elif url.endswith('cancelJob'):
+ body = self.fixtures.load(
+ (''
+ '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml')
+ )
+ else:
+ raise ValueError("Unknown URL: %s" % url)
+ 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/68bf8534/libcloud/test/common/test_base_driver.py
----------------------------------------------------------------------
diff --cc libcloud/test/common/test_base_driver.py
index 6e04874,6e04874..f69c982
--- a/libcloud/test/common/test_base_driver.py
+++ b/libcloud/test/common/test_base_driver.py
@@@ -51,12 -51,12 +51,6 @@@ class BaseDriverTestCase(unittest.TestC
result['timeout'] = 13
return result
-- DummyDriver2.connectionCls = Mock()
-- DummyDriver2(key='foo')
-- call_kwargs = DummyDriver2.connectionCls.call_args[1]
-- self.assertEqual(call_kwargs['timeout'], 13)
-- self.assertEqual(call_kwargs['retry_delay'], None)
--
# 4. Value provided via "_ex_connection_class_kwargs" and constructor,
# constructor should win
DummyDriver2.connectionCls = Mock()