diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py index 0a9525541f..8f0f71d9b0 100644 --- a/libcloud/compute/drivers/dimensiondata.py +++ b/libcloud/compute/drivers/dimensiondata.py @@ -52,6 +52,9 @@ IPPLAN_NS = NAMESPACE_BASE + "/ipplan" WHITELABEL_NS = NAMESPACE_BASE + "/whitelabel" +# API 2.0 Namespaces and URNs +TYPES_URN = "urn:didata.com:api:cloud:types" + # API end-points API_ENDPOINTS = { 'dd-na': { @@ -104,9 +107,10 @@ def parse_error(self): body = self.parse_body() + # TODO: The path is not fixed as server. if self.status == httplib.BAD_REQUEST: - code = findtext(body, 'resultCode', SERVER_NS) - message = findtext(body, 'resultDetail', SERVER_NS) + code = findtext(body, 'responseCode', SERVER_NS) + message = findtext(body, 'message', SERVER_NS) raise DimensionDataAPIException(code, message, driver=DimensionDataNodeDriver) @@ -133,8 +137,11 @@ class DimensionDataConnection(ConnectionUserAndKey): Connection class for the DimensionData driver """ - api_path = '/oec' - api_version = '0.9' + api_path_version_1 = '/oec' + api_path_version_2 = '/caas' + api_version_1 = '0.9' + api_version_2 = '2.0' + _orgId = None responseCls = DimensionDataResponse @@ -159,31 +166,60 @@ def add_default_headers(self, headers): self.key))).decode('utf-8')) return headers - def request(self, action, params=None, data='', - headers=None, method='GET'): - action = "%s/%s/%s" % (self.api_path, self.api_version, action) + def request_api_1(self, action, params=None, data='', + headers=None, method='GET'): + action = "%s/%s/%s" % (self.api_path_version_1, + self.api_version_1, action) + + return super(DimensionDataConnection, self).request( + action=action, + params=params, data=data, + method=method, headers=headers) + + def request_api_2(self, path, action, params=None, data='', + headers=None, method='GET'): + action = "%s/%s/%s/%s" % (self.api_path_version_2, + self.api_version_2, path, action) + + return super(DimensionDataConnection, self).request( + action=action, + params=params, data=data, + method=method, headers=headers) + + def request_with_orgId_api_1(self, action, params=None, data='', + headers=None, method='GET'): + action = "%s/%s" % (self.get_resource_path_api_1(), action) return super(DimensionDataConnection, self).request( action=action, params=params, data=data, method=method, headers=headers) - def request_with_orgId(self, action, params=None, data='', - headers=None, method='GET'): - action = "%s/%s" % (self.get_resource_path(), action) + def request_with_orgId_api_2(self, action, params=None, data='', + headers=None, method='GET'): + action = "%s/%s" % (self.get_resource_path_api_2(), action) return super(DimensionDataConnection, self).request( action=action, params=params, data=data, method=method, headers=headers) - def get_resource_path(self): + def get_resource_path_api_1(self): + """ + This method returns a resource path which is necessary for referencing + resources that require a full path instead of just an ID, such as + networks, and customer snapshots. + """ + return ("%s/%s/%s" % (self.api_path_version_1, self.api_version_1, + self._get_orgId())) + + def get_resource_path_api_2(self): """ This method returns a resource path which is necessary for referencing resources that require a full path instead of just an ID, such as networks, and customer snapshots. """ - return ("%s/%s/%s" % (self.api_path, self.api_version, + return ("%s/%s/%s" % (self.api_path_version_2, self.api_version_2, self._get_orgId())) def _get_orgId(self): @@ -193,7 +229,7 @@ def _get_orgId(self): of the other API functions """ if self._orgId is None: - body = self.request('myaccount').object + body = self.request_api_1('myaccount').object self._orgId = findtext(body, 'orgId', DIRECTORY_NS) return self._orgId @@ -251,6 +287,44 @@ def __repr__(self): self.private_net, self.multicast)) +class DimensionDataNetworkDomain(object): + """ + DimensionData network domain with location. + """ + + def __init__(self, id, name, description, location, status): + self.id = str(id) + self.name = name + self.description = description + self.location = location + self.status = status + + def __repr__(self): + return (('') + % (self.id, self.name, self.description, self.location, + self.status)) + + +class DimensionDataVlan(object): + """ + DimensionData VLAN. + """ + + def __init__(self, id, name, description, location, status): + self.id = str(id) + self.name = name + self.location = location + self.description = description + self.status = status + + def __repr__(self): + return (('') + % (self.id, self.name, self.description, + self.location, self.status)) + + class DimensionDataNodeDriver(NodeDriver): """ DimensionData node driver. @@ -332,15 +406,17 @@ def create_node(self, name, image, auth, ex_description, if not isinstance(ex_network, DimensionDataNetwork): raise ValueError('ex_network must be of DimensionDataNetwork type') - vlanResourcePath = "%s/%s" % (self.connection.get_resource_path(), - ex_network.id) + vlanResourcePath = "%s/%s" % ( + self.connection.get_resource_path_api_1(), + ex_network.id) imageResourcePath = None if 'resourcePath' in image.extra: imageResourcePath = image.extra['resourcePath'] else: - imageResourcePath = "%s/%s" % (self.connection.get_resource_path(), - image.id) + imageResourcePath = "%s/%s" % ( + self.connection.get_resource_path_api_1(), + image.id) server_elm = ET.Element('Server', {'xmlns': SERVER_NS}) ET.SubElement(server_elm, "name").text = name @@ -350,9 +426,10 @@ def create_node(self, name, image, auth, ex_description, ET.SubElement(server_elm, "administratorPassword").text = password ET.SubElement(server_elm, "isStarted").text = str(ex_is_started) - self.connection.request_with_orgId('server', - method='POST', - data=ET.tostring(server_elm)).object + self.connection.request_with_orgId_api_1( + 'server', + method='POST', + data=ET.tostring(server_elm)).object # XXX: return the last node in the list that has a matching name. this # is likely but not guaranteed to be the node we just created @@ -366,23 +443,29 @@ def create_node(self, name, image, auth, ex_description, return node def destroy_node(self, node): - body = self.connection.request_with_orgId( - 'server/%s?delete' % (node.id)).object - - result = findtext(body, 'result', GENERAL_NS) - return result == 'SUCCESS' + request_elm = ET.Element('deleteServer', + {'xmlns': TYPES_URN, 'id': node.id}) + body = self.connection.request_with_orgId_api_2( + 'server/deleteServer', + method='POST', + data=ET.tostring(request_elm)).object + result = findtext(body, 'responseCode', TYPES_URN) + return result == 'IN_PROGRESS' def reboot_node(self, node): - body = self.connection.request_with_orgId( - 'server/%s?restart' % (node.id)).object - result = findtext(body, 'result', GENERAL_NS) - return result == 'SUCCESS' + request_elm = ET.Element('rebootServer', + {'xmlns': TYPES_URN, 'id': node.id}) + body = self.connection.request_with_orgId_api_2( + 'server/rebootServer', + method='POST', + data=ET.tostring(request_elm)).object + result = findtext(body, 'responseCode', TYPES_URN) + return result == 'IN_PROGRESS' def list_nodes(self): nodes = self._to_nodes( - self.connection.request_with_orgId('server/deployed').object) - nodes.extend(self._to_nodes( - self.connection.request_with_orgId('server/pendingDeploy').object)) + self.connection.request_with_orgId_api_2('server/server').object) + return nodes def list_images(self, location=None): @@ -394,9 +477,16 @@ def list_images(self, location=None): @inherits: :class:`NodeDriver.list_images` """ return self._to_base_images( - self.connection.request('base/image').object) + self.connection.request_api_1('base/image').object) def list_sizes(self, location=None): + """ + return a list of available sizes + Currently, the size of the node is dictated by the chosen OS base + image, they cannot be set explicitly. + + @inherits: :class:`NodeDriver.list_sizes` + """ return [ NodeSize(id=1, name="default", @@ -415,7 +505,8 @@ def list_locations(self): @inherits: :class:`NodeDriver.list_locations` """ return self._to_locations( - self.connection.request_with_orgId('datacenter').object) + self.connection + .request_with_orgId_api_2('infrastructure/datacenter').object) def list_networks(self, location=None): """ @@ -430,7 +521,8 @@ def list_networks(self, location=None): :rtype: ``list`` of :class:`DimensionDataNetwork` """ return self._to_networks( - self.connection.request_with_orgId('networkWithLocation').object) + self.connection + .request_with_orgId_api_1('networkWithLocation').object) def _to_base_images(self, object): images = [] @@ -476,10 +568,14 @@ def ex_start_node(self, node): :rtype: ``bool`` """ - body = self.connection.request_with_orgId( - 'server/%s?start' % node.id).object - result = findtext(body, 'result', GENERAL_NS) - return result == 'SUCCESS' + request_elm = ET.Element('startServer', + {'xmlns': TYPES_URN, 'id': node.id}) + body = self.connection.request_with_orgId_api_2( + 'server/startServer', + method='POST', + data=ET.tostring(request_elm)).object + result = findtext(body, 'responseCode', TYPES_URN) + return result == 'IN_PROGRESS' def ex_shutdown_graceful(self, node): """ @@ -493,10 +589,14 @@ def ex_shutdown_graceful(self, node): :rtype: ``bool`` """ - body = self.connection.request_with_orgId( - 'server/%s?shutdown' % (node.id)).object - result = findtext(body, 'result', GENERAL_NS) - return result == 'SUCCESS' + request_elm = ET.Element('shutdownServer', + {'xmlns': TYPES_URN, 'id': node.id}) + body = self.connection.request_with_orgId_api_2( + 'server/shutdownServer', + method='POST', + data=ET.tostring(request_elm)).object + result = findtext(body, 'responseCode', TYPES_URN) + return result == 'IN_PROGRESS' def ex_power_off(self, node): """ @@ -510,10 +610,35 @@ def ex_power_off(self, node): :rtype: ``bool`` """ - body = self.connection.request_with_orgId( - 'server/%s?poweroff' % node.id).object - result = findtext(body, 'result', GENERAL_NS) - return result == 'SUCCESS' + request_elm = ET.Element('powerOffServer', + {'xmlns': TYPES_URN, 'id': node.id}) + body = self.connection.request_with_orgId_api_2( + 'server/powerOffServer', + method='POST', + data=ET.tostring(request_elm)).object + result = findtext(body, 'responseCode', TYPES_URN) + return result == 'IN_PROGRESS' + + def ex_reset(self, node): + """ + This function will abruptly reset a server. Unlike + reboot_node, success ensures the node will restart but some OS + and application configurations may be adversely affected by the + equivalent of pulling the power plug out of the machine. + + :param node: Node which should be used + :type node: :class:`Node` + + :rtype: ``bool`` + """ + request_elm = ET.Element('resetServer', + {'xmlns': TYPES_URN, 'id': node.id}) + body = self.connection.request_with_orgId_api_2( + 'server/resetServer', + method='POST', + data=ET.tostring(request_elm)).object + result = findtext(body, 'responseCode', TYPES_URN) + return result == 'IN_PROGRESS' def ex_list_networks(self): """ @@ -523,10 +648,33 @@ def ex_list_networks(self): :return: a list of DimensionDataNetwork objects :rtype: ``list`` of :class:`DimensionDataNetwork` """ - response = self.connection.request_with_orgId('networkWithLocation') \ - .object + response = self.connection \ + .request_with_orgId_api_1('networkWithLocation').object return self._to_networks(response) + def ex_list_network_domains(self): + """ + List networks deployed across all data center locations for your + organization. The response includes the location of each network. + + :return: a list of DimensionDataNetwork objects + :rtype: ``list`` of :class:`DimensionDataNetwork` + """ + response = self.connection \ + .request_with_orgId_api_2('network/networkDomain').object + return self._to_network_domains(response) + + def ex_list_vlans(self): + """ + List VLANs available in a given networkDomain + + :return: a list of DimensionDataVlan objects + :rtype: ``list`` of :class:`DimensionDataVlan` + """ + response = self.connection.request_with_orgId_api_2('network/vlan') \ + .object + return self._to_vlans(response) + def ex_get_location_by_id(self, id): """ Get location by ID. @@ -570,66 +718,113 @@ def _to_network(self, element): multicast=multicast, status=status) + def _to_network_domains(self, object): + network_domains = [] + for element in findall(object, 'networkDomain', TYPES_URN): + network_domains.append(self._to_network_domain(element)) + + return network_domains + + def _to_network_domain(self, element): + status = self._to_status(element.find(fixxpath('state', TYPES_URN))) + + location_id = element.get('datacenter') + location = self.ex_get_location_by_id(location_id) + + return DimensionDataNetworkDomain( + id=element.get('id'), + name=findtext(element, 'name', TYPES_URN), + description=findtext(element, 'description', + TYPES_URN), + location=location, + status=status) + + def _to_vlans(self, object): + vlans = [] + for element in findall(object, 'vlan', TYPES_URN): + vlans.append(self._to_vlan(element)) + + return vlans + + def _to_vlan(self, element): + status = self._to_status(element.find(fixxpath('state', TYPES_URN))) + + location_id = element.get('location') + location = self.ex_get_location_by_id(location_id) + + return DimensionDataVlan( + id=element.get('id'), + name=findtext(element, 'name', TYPES_URN), + description=findtext(element, 'description', + TYPES_URN), + location=location, + status=status) + def _to_locations(self, object): locations = [] - for element in object.findall(fixxpath('datacenter', DATACENTER_NS)): + for element in object.findall(fixxpath('datacenter', TYPES_URN)): locations.append(self._to_location(element)) return locations def _to_location(self, element): - l = NodeLocation(id=findtext(element, 'location', DATACENTER_NS), - name=findtext(element, 'displayName', DATACENTER_NS), - country=findtext(element, 'country', DATACENTER_NS), + l = NodeLocation(id=element.get('id'), + name=findtext(element, 'displayName', TYPES_URN), + country=findtext(element, 'country', TYPES_URN), driver=self) return l def _to_nodes(self, object): - node_elements = object.findall(fixxpath('DeployedServer', SERVER_NS)) - node_elements.extend(object.findall( - fixxpath('PendingDeployServer', SERVER_NS))) + node_elements = object.findall(fixxpath('Server', TYPES_URN)) + return [self._to_node(el) for el in node_elements] def _to_node(self, element): - if findtext(element, 'isStarted', SERVER_NS) == 'true': + if findtext(element, 'started', TYPES_URN) == 'true': state = NodeState.RUNNING else: state = NodeState.TERMINATED - status = self._to_status(element.find(fixxpath('status', SERVER_NS))) + status = self._to_status(element.find(fixxpath('progress', TYPES_URN))) extra = { - 'description': findtext(element, 'description', SERVER_NS), - 'sourceImageId': findtext(element, 'sourceImageId', SERVER_NS), - 'networkId': findtext(element, 'networkId', SERVER_NS), - 'machineName': findtext(element, 'machineName', SERVER_NS), - 'deployedTime': findtext(element, 'deployedTime', SERVER_NS), - 'cpuCount': findtext(element, 'machineSpecification/cpuCount', - SERVER_NS), - 'memoryMb': findtext(element, 'machineSpecification/memoryMb', - SERVER_NS), - 'osStorageGb': findtext(element, - 'machineSpecification/osStorageGb', - SERVER_NS), - 'additionalLocalStorageGb': findtext( - element, 'machineSpecification/additionalLocalStorageGb', - SERVER_NS), - 'OS_type': findtext(element, - 'machineSpecification/operatingSystem/type', - SERVER_NS), - 'OS_displayName': findtext( - element, 'machineSpecification/operatingSystem/displayName', - SERVER_NS), - 'status': status, + 'description': findtext(element, 'description', TYPES_URN), + 'sourceImageId': findtext(element, 'sourceImageId', TYPES_URN), + 'networkId': findtext(element, 'networkId', TYPES_URN), + 'networkDomainId': element.find(fixxpath('networkInfo', TYPES_URN)) + .get('networkDomainId'), + 'datacenterId': element.get('datacenterId'), + 'deployedTime': findtext(element, 'createTime', TYPES_URN), + 'cpuCount': int(findtext( + element, + 'cpuCount', + TYPES_URN)), + 'memoryMb': int(findtext( + element, + 'memoryGb', + TYPES_URN)) * 1024, + 'OS_id': element.find(fixxpath( + 'operatingSystem', + TYPES_URN)).get('id'), + 'OS_type': element.find(fixxpath( + 'operatingSystem', + TYPES_URN)).get('family'), + 'OS_displayName': element.find(fixxpath( + 'operatingSystem', + TYPES_URN)).get('displayName'), + 'status': status } - public_ip = findtext(element, 'publicIpAddress', SERVER_NS) + public_ip = findtext(element, 'publicIpAddress', TYPES_URN) + + private_ip = findtext(element, 'networkInfo/primaryNic/privateIpv4', + TYPES_URN) - n = Node(id=findtext(element, 'id', SERVER_NS), - name=findtext(element, 'name', SERVER_NS), + n = Node(id=element.get('id'), + name=findtext(element, 'name', TYPES_URN), state=state, public_ips=[public_ip] if public_ip is not None else [], - private_ips=findtext(element, 'privateIpAddress', SERVER_NS), + private_ips=[private_ip] if private_ip is not None else [], driver=self.connection.driver, extra=extra) return n @@ -637,33 +832,33 @@ def _to_node(self, element): def _to_status(self, element): if element is None: return DimensionDataStatus() - s = DimensionDataStatus(action=findtext(element, 'action', SERVER_NS), + s = DimensionDataStatus(action=findtext(element, 'action', TYPES_URN), request_time=findtext( element, 'requestTime', - SERVER_NS), + TYPES_URN), user_name=findtext( element, 'userName', - SERVER_NS), + TYPES_URN), number_of_steps=findtext( element, 'numberOfSteps', - SERVER_NS), + TYPES_URN), step_name=findtext( element, 'step/name', - SERVER_NS), + TYPES_URN), step_number=findtext( element, 'step_number', - SERVER_NS), + TYPES_URN), step_percent_complete=findtext( element, 'step/percentComplete', - SERVER_NS), + TYPES_URN), failure_reason=findtext( element, 'failureReason', - SERVER_NS)) + TYPES_URN)) return s diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_infrastructure_datacenter.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_infrastructure_datacenter.xml new file mode 100644 index 0000000000..66ae1db418 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_infrastructure_datacenter.xml @@ -0,0 +1,44 @@ + + + +US - West +Santa Clara +California +US +https://na10.cloud-vpn.net +ftps-na.cloud-vpn.net + + + + + + + + + +Archive Speed +ARCH +Archive Disk Speed +Replaced with HPF and ECN + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_networkDomain.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_networkDomain.xml new file mode 100644 index 0000000000..22680d107e --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_networkDomain.xml @@ -0,0 +1,13 @@ + + + +Production Network Domain + +ESSENTIALS +165.180.8.8 +2015-02-12T18:18:14.000Z +NORMAL + + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_vlan.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_vlan.xml new file mode 100644 index 0000000000..b489284ca3 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_vlan.xml @@ -0,0 +1,17 @@ + + + + +Production VLAN +For hosting our Production Cloud Servers + +10.0.3.1 + +2607:f480:1111:1153:0:0:0:1 +2015-02-13T10:56:44.000Z +NORMAL + + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer.xml new file mode 100644 index 0000000000..c8e70dad6e --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer.xml @@ -0,0 +1,9 @@ + + +DELETE_SERVER +IN_PROGRESS +Request to Delete Server (Id:d577a691-e116-4913-a440- +022d2729fc84) has been accepted and is being processed + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer_RESOURCEBUSY.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer_RESOURCEBUSY.xml new file mode 100644 index 0000000000..2df9e07c3b --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer_RESOURCEBUSY.xml @@ -0,0 +1,8 @@ + + +DELETE_SERVER +RESOURCE_BUSY +Server is already busy + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer.xml new file mode 100644 index 0000000000..549ea79832 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer.xml @@ -0,0 +1,8 @@ + + +POWER_OFF_SERVER +IN_PROGRESS +Request to power off Server 'Production Server' has been accepted +and is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer_INPROGRESS.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer_INPROGRESS.xml new file mode 100644 index 0000000000..8ddcca918e --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer_INPROGRESS.xml @@ -0,0 +1,8 @@ + + +POWER_OFF_SERVER +RESOURCE_BUSY +Request to power off Server 'Production Server' has been accepted +and is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer.xml new file mode 100644 index 0000000000..38f098a08c --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer.xml @@ -0,0 +1,8 @@ + + +REBOOT_SERVER +IN_PROGRESS +Request to reboot Server 'Production Server' has been accepted +and is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer_RESOURCEBUSY.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer_RESOURCEBUSY.xml new file mode 100644 index 0000000000..3defba48a2 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer_RESOURCEBUSY.xml @@ -0,0 +1,7 @@ + + +REBOOT_SERVER +RESOURCE_BUSY +Request to reboot Server 'Production Server' did not work, server is busy. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_resetServer.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_resetServer.xml new file mode 100644 index 0000000000..ece1a8bb41 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_resetServer.xml @@ -0,0 +1,8 @@ + + +RESET_SERVER +IN_PROGRESS +Request to reset Server 'Production Server' has been accepted and +is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml new file mode 100644 index 0000000000..64c691a92f --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml @@ -0,0 +1,64 @@ + + + +test2 +Test Description + +1 +2 + + + + +02250336-de2b-4e99-ab96-78511b7f8f4b +2015-02-17T10:59:18.000Z +true +true +NORMAL + +DEPLOY_SERVER +2015-03-06T18:05:33.000Z +myuser + + + + + + +Production Web Server 2 +This server hosts our production web +applications. + +1 +2 + + + + +02250336-de2b-4e99-ab96-78511b7f8f4b +2015-02-17T10:59:18.000Z +true +true +PENDING_CHANGE + +SHUTDOWN_SERVER +2015-03-06T18:05:33.000Z +myuser + + + + + + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer.xml new file mode 100644 index 0000000000..937cb0d563 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer.xml @@ -0,0 +1,8 @@ + + +SHUTDOWN_SERVER +IN_PROGRESS +Request to shutdown Server 'Production Server' has been accepted +and is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer_INPROGRESS.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer_INPROGRESS.xml new file mode 100644 index 0000000000..386489cdc9 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer_INPROGRESS.xml @@ -0,0 +1,8 @@ + + +SHUTDOWN_SERVER +RESOURCE_BUSY +Request to shutdown Server 'Production Server' has been accepted +and is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer.xml new file mode 100644 index 0000000000..4fb4589bb5 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer.xml @@ -0,0 +1,8 @@ + + +START_SERVER +IN_PROGRESS +Request to start Server 'Production Server' has been accepted and +is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer_INPROGRESS.xml b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer_INPROGRESS.xml new file mode 100644 index 0000000000..f5c30708b6 --- /dev/null +++ b/libcloud/test/compute/fixtures/dimensiondata/caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer_INPROGRESS.xml @@ -0,0 +1,8 @@ + + +START_SERVER +RESOURCE_BUSY +Request to start Server 'Production Server' has been accepted and +is being processed. + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml b/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml index ca27554d8e..88364b69cd 100644 --- a/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml +++ b/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml @@ -4,7 +4,7 @@ 53b4c05b-341e-4ac3-b688-bdd74e53ca9b test-net1 test-net1 description - NA1 + NA10 10.162.1.0 false diff --git a/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_base_image.xml b/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_base_image.xml index 3be14f069f..d758343885 100644 --- a/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_base_image.xml +++ b/libcloud/test/compute/fixtures/dimensiondata/oec_0_9_base_image.xml @@ -9,7 +9,7 @@ UNIX REDHAT5/64 - NA1 + NA10 1 2048 10 @@ -25,7 +25,7 @@ UNIX REDHAT5/64 - NA1 + NA10 2 4096 10 @@ -41,7 +41,7 @@ UNIX REDHAT5/64 - NA1 + NA10 4 6144 10 @@ -57,7 +57,7 @@ UNIX REDHAT5/32 - NA1 + NA10 1 2048 10 @@ -73,7 +73,7 @@ UNIX UBUNTU8/64 - NA1 + NA10 2 4096 10 @@ -89,7 +89,7 @@ WINDOWS WIN2008R2E/64 - NA1 + NA10 2 4096 50 @@ -105,7 +105,7 @@ WINDOWS WIN2008R2E/64 - NA1 + NA10 4 8192 50 @@ -121,7 +121,7 @@ WINDOWS WIN2008R2S/64 - NA1 + NA10 2 4096 50 @@ -137,7 +137,7 @@ WINDOWS WIN2008S/32 - NA1 + NA10 1 2048 50 @@ -153,7 +153,7 @@ WINDOWS WIN2008S/32 - NA1 + NA10 2 4096 50 @@ -169,7 +169,7 @@ WINDOWS WIN2008S/32 - NA1 + NA10 4 4096 50 @@ -185,7 +185,7 @@ WINDOWS WIN2008E/32 - NA1 + NA10 2 4096 50 @@ -201,7 +201,7 @@ UNIX REDHAT4/32 - NA1 + NA10 1 2048 10 @@ -217,123 +217,11 @@ UNIX CENTOS5/32 - NA1 + NA10 1 2048 10 0 1970-01-01T00:00:02.010Z - - 52ed91da-ebea-11df-bdc1-001517c46384 - /oec/base/image/52ed91da-ebea-11df-bdc1-001517c46384 - CentOS 5.5 64-bit 1 CPU - CentOS release 5.5, 64-bit - - UNIX - CENTOS5/64 - - NA1 - 1 - 2048 - 10 - 0 - 1970-01-01T00:00:02.010Z - - - 52ed766e-ebea-11df-bdc1-001517c46384 - /oec/base/image/52ed766e-ebea-11df-bdc1-001517c46384 - Win2003 Ent 32-bit 1 CPU - Windows 2003 Enterprise SP2 32-bit - - WINDOWS - WIN2003E/32 - - NA1 - 1 - 2048 - 16 - 0 - 1970-01-01T00:00:02.010Z - - - 52ed7876-ebea-11df-bdc1-001517c46384 - /oec/base/image/52ed7876-ebea-11df-bdc1-001517c46384 - Win2003 Ent 32-bit 2 CPU - Windows 2003 Enterprise SP2 32-bit - - WINDOWS - WIN2003E/32 - - NA1 - 2 - 4096 - 16 - 0 - 1970-01-01T00:00:02.010Z - - - 52ed7984-ebea-11df-bdc1-001517c46384 - /oec/base/image/52ed7984-ebea-11df-bdc1-001517c46384 - Win2003 Ent 32-bit 4 CPU - Windows 2003 Enterprise SP2 32-bit - - WINDOWS - WIN2003E/32 - - NA1 - 4 - 4096 - 16 - 0 - 1970-01-01T00:00:02.010Z - - - 52ed7a88-ebea-11df-bdc1-001517c46384 - /oec/base/image/52ed7a88-ebea-11df-bdc1-001517c46384 - Win2003 Std 64-bit 2 CPU - Windows 2003 Standard x64 SP2, 64-bit - - WINDOWS - WIN2003S/64 - - NA1 - 2 - 4096 - 16 - 0 - 1970-01-01T00:00:02.010Z - - - 0c231ef0-2a42-11e0-bfb5-001517c46384 - /oec/base/image/0c231ef0-2a42-11e0-bfb5-001517c46384 - RedHat 64-bit 2 CPU with MySQL - RedHat 5.5 Enterprise with MySQL 5.5 installed - - UNIX - REDHAT5/64 - - NA1 - 2 - 8192 - 10 - 0 - 2011-01-27T18:19:58.000Z - - - 2fb5261a-2a42-11e0-bfb5-001517c46384 - /oec/base/image/2fb5261a-2a42-11e0-bfb5-001517c46384 - RedHat 64-bit 2 CPU with PostgreSQL - RedHat 5.5 Enterprise with PostgreSQL 9.0 installed - - UNIX - REDHAT5/64 - - NA1 - 2 - 8192 - 10 - 0 - 2011-01-27T18:20:57.000Z - diff --git a/libcloud/test/compute/test_dimensiondata.py b/libcloud/test/compute/test_dimensiondata.py index 47a8e16ffd..c37ab44d2f 100644 --- a/libcloud/test/compute/test_dimensiondata.py +++ b/libcloud/test/compute/test_dimensiondata.py @@ -44,6 +44,20 @@ def test_invalid_creds(self): except InvalidCredsError: pass + def test_list_locations_response(self): + DimensionDataMockHttp.type = None + ret = self.driver.list_locations() + self.assertEqual(len(ret), 1) + first_node = ret[0] + self.assertEqual(first_node.id, 'NA10') + self.assertEqual(first_node.name, 'US - West') + self.assertEqual(first_node.country, 'US') + + def test_list_nodes_response(self): + DimensionDataMockHttp.type = None + ret = self.driver.list_nodes() + self.assertEqual(len(ret), 2) + def test_list_sizes_response(self): DimensionDataMockHttp.type = None ret = self.driver.list_sizes() @@ -74,7 +88,7 @@ def test_destroy_node_response(self): ret = node.destroy() self.assertTrue(ret is True) - def test_destroy_node_response_INPROGRESS(self): + def test_destroy_node_response_RESOURCE_BUSY(self): DimensionDataMockHttp.type = 'INPROGRESS' node = Node(id='11', name=None, state=None, public_ips=None, private_ips=None, driver=self.driver) @@ -146,16 +160,25 @@ def test_ex_power_off_INPROGRESS(self): except DimensionDataAPIException: pass + def test_ex_reset(self): + node = Node(id='11', name=None, state=None, + public_ips=None, private_ips=None, driver=self.driver) + ret = self.driver.ex_reset(node) + self.assertTrue(ret is True) + def test_ex_list_networks(self): nets = self.driver.ex_list_networks() self.assertEqual(nets[0].name, 'test-net1') self.assertTrue(isinstance(nets[0].location, NodeLocation)) - def test_node_public_ip(self): - nodes = self.driver.list_nodes() - node = [n for n in nodes if n.id == - 'abadbc7e-9e10-46ca-9d4a-194bcc6b6c16'][0] - self.assertEqual(node.public_ips[0], '200.16.132.7') + def test_ex_list_network_domains(self): + nets = self.driver.ex_list_network_domains() + self.assertEqual(nets[0].name, 'Production Network Domain') + self.assertTrue(isinstance(nets[0].location, NodeLocation)) + + def test_ex_list_vlans(self): + vlans = self.driver.ex_list_vlans() + self.assertEqual(vlans[0].name, "Production VLAN") class DimensionDataMockHttp(MockHttp): @@ -246,6 +269,85 @@ def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation(self, meth 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deleteServer_RESOURCEBUSY.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_rebootServer_RESOURCEBUSY.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_infrastructure_datacenter(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_infrastructure_datacenter.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_startServer_INPROGRESS.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_shutdownServer_INPROGRESS.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_resetServer(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_resetServer.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer_INPROGRESS(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_powerOffServer_INPROGRESS.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_networkDomain(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_networkDomain.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_vlan(self, method, url, body, headers): + body = self.fixtures.load( + 'caas_2_0_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_network_vlan.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) if __name__ == '__main__': sys.exit(unittest.main())