diff --git libcloud/compute/drivers/opsource.py libcloud/compute/drivers/opsource.py new file mode 100644 index 0000000..df0471f --- /dev/null +++ libcloud/compute/drivers/opsource.py @@ -0,0 +1,508 @@ +# 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. +""" +Opsource Driver +""" +import base64 +import socket +from xml.etree import ElementTree as ET +from xml.parsers.expat import ExpatError + +from libcloud.common.base import ConnectionUserAndKey, Response +from libcloud.common.types import LibcloudError, InvalidCredsError, MalformedResponseError +from libcloud.compute.types import NodeState, Provider +from libcloud.compute.base import NodeDriver, Node, NodeAuthPassword +from libcloud.compute.base import NodeSize, NodeImage, NodeLocation + +# Roadmap / TODO: +# +# 0.1 - Basic functionality: create, delete, start, stop, reboot - servers +# (base OS images only, no customer images suported yet) +# x implement list_nodes() +# x implement create_node() (only support Base OS images, no customer images yet) +# x implement reboot() +# x implement destroy_node() +# x implement list_sizes() +# x implement list_images() (only support Base OS images, no customer images yet) +# x implement list_locations() +# x implement ex_* extension functions for opsource-specific features +# x ex_graceful_shutdown +# x ex_start_node +# x ex_power_off +# x ex_list_networks (needed for create_node()) +# x refactor: switch to using fixxpath() from the vcloud driver for dealing with xml namespace tags +# x refactor: move some functionality from OpsourceConnection.request() method into new .request_with_orgId() method +# x add OpsourceStatus object support to: +# x _to_node() +# x _to_network() +# x implement test cases +# +# 0.2 - Support customer images (snapshots) and server modification functions +# - support customer-created images: +# - list deployed customer images (in list_images() ?) +# - list pending customer images (in list_images() ?) +# - delete customer images +# - modify customer images +# - add "pending-servers" in list_nodes() +# - implement various ex_* extension functions for opsource-specific features +# - ex_modify_server() +# - ex_add_storage_to_server() +# - ex_snapshot_server() (create's customer image) +# +# 0.3 - support Network API +# 0.4 - Support VIP/Load-balancing API +# 0.5 - support Files Account API +# 0.6 - support Reports API +# 1.0 - Opsource 0.9 API feature complete, tested + +# setup a few variables to represent all of the opsource cloud namespaces +NAMESPACE_BASE = "http://oec.api.opsource.net/schemas" +ORGANIZATION_NS = NAMESPACE_BASE + "/organization" +SERVER_NS = NAMESPACE_BASE + "/server" +NETWORK_NS = NAMESPACE_BASE + "/network" +DIRECTORY_NS = NAMESPACE_BASE + "/directory" +RESET_NS = NAMESPACE_BASE + "/reset" +VIP_NS = NAMESPACE_BASE + "/vip" +IMAGEIMPORTEXPORT_NS = NAMESPACE_BASE + "/imageimportexport" +DATACENTER_NS = NAMESPACE_BASE + "/datacenter" +SUPPORT_NS = NAMESPACE_BASE + "/support" +GENERAL_NS = NAMESPACE_BASE + "/general" +IPPLAN_NS = NAMESPACE_BASE + "/ipplan" +WHITELABEL_NS = NAMESPACE_BASE + "/whitelabel" + +def fixxpath(root, xpath): + """ElementTree wants namespaces in its xpaths, so here we add them.""" + namespace, root_tag = root.tag[1:].split("}", 1) + fixed_xpath = "/".join(["{%s}%s" % (namespace, e) + for e in xpath.split("/")]) + return fixed_xpath + +class OpsourceResponse(Response): + + def parse_body(self): + try: + body = ET.XML(self.body) + except: + raise MalformedResponseError("Failed to parse XML", body=self.body, driver=OpsourceNodeDriver) + return body + + def parse_error(self): + if self.status == 401: + raise InvalidCredsError(self.body) + + if self.status == 403: + raise InvalidCredsError(self.body) + + try: + body = ET.XML(self.body) + except: + raise MalformedResponseError("Failed to parse XML", body=self.body, driver=OpsourceNodeDriver) + + if self.status == 400: + code = body.findtext(fixxpath(body, "resultCode")) + message = body.findtext(fixxpath(body, "resultDetail")) + raise OpsourceAPIException(code, message, driver=OpsourceNodeDriver) + + return self.body + +class OpsourceAPIException(LibcloudError): + def __init__(self, code, msg, driver): + self.code = code + self.msg = msg + self.driver = driver + + def __str__(self): + return "%s: %s" % (self.code, self.msg) + + def __repr__(self): + return "" % (self.code, self.msg) + +class OpsourceConnection(ConnectionUserAndKey): + """ + Connection class for the Opsource driver + """ + + host = 'api.opsourcecloud.net' + api_path = '/oec' + api_version = '0.9' + _orgId = None + responseCls = OpsourceResponse + + def add_default_headers(self, headers): + headers['Authorization'] = ('Basic %s' + % (base64.b64encode('%s:%s' % (self.user_id, self.key)))) + return headers + + def request(self, action, params=None, data='', headers=None, method='GET'): + action = "%s/%s/%s" % (self.api_path, self.api_version, action) + + return super(OpsourceConnection, 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) + + return super(OpsourceConnection, self).request( + action=action, + params=params, data=data, + method=method, headers=headers + ) + + def get_resource_path(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, self._get_orgId())) + + def _get_orgId(self): + """ + send the /myaccount API request to opsource cloud and parse the 'orgId' from the + XML response object. We need the orgId to use most of the other API functions + """ + if self._orgId == None: + body = self.request('myaccount').object + self._orgId = body.findtext(fixxpath(body, "orgId")) + return self._orgId + +class OpsourceStatus(object): + """ + Opsource API pending operation status class + action, requestTime, username, numberOfSteps, updateTime, + step.name, step.number, step.percentComplete, failureReason, + """ + def __init__(self, action=None, requestTime=None, userName=None, + numberOfSteps=None, updateTime=None, step_name=None, + step_number=None, step_percentComplete=None, failureReason=None): + self.action = action + self.requestTime = requestTime + self.userName = userName + self.numberOfSteps = numberOfSteps + self.updateTime = updateTime + self.step_name = step_name + self.step_number = step_number + self.step_percentComplete = step_percentComplete + self.failureReason = failureReason + + def __repr__(self): + return (('') + % (self.id, self.name, self.description, self.location, + self.privateNet, self.multicast)) + + +class OpsourceNodeDriver(NodeDriver): + """ + Opsource node driver + """ + + connectionCls = OpsourceConnection + + type = Provider.OPSOURCE + name = 'Opsource' + + features = {"create_node": ["password"]} + + 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)) + return nodes + + def list_sizes(self, location=None): + return [ NodeSize(id=1, + name="default", + ram=0, + disk=0, + bandwidth=0, + price=0, + driver=self.connection.driver) ] + + def list_images(self, location=None): + """return a list of available images + Currently only returns the default 'base OS images' provided by opsource. + Customer images (snapshots) are not yet supported. + """ + return self._to_base_images(self.connection.request('base/image').object) + + def list_locations(self): + """list locations (datacenters) available for instantiating servers and + networks. + """ + return self._to_locations(self.connection.request_with_orgId('datacenter').object) + + def create_node(self, **kwargs): + """Create a new opsource node + + Standard keyword arguments from L{NodeDriver.create_node}: + @keyword name: String with a name for this new node (required) + @type name: str + + @keyword image: OS Image to boot on node. (required) + @type image: L{NodeImage} + + @keyword auth: Initial authentication information for the node (required) + @type auth: L{NodeAuthPassword} + + Non-standard keyword arguments: + @keyword ex_description: description for this node (required) + @type ex_description: C{str} + + @keyword ex_network: Network to create the node within (required) + @type ex_network: L{OpsourceNetwork} + + @keyword ex_isStarted: Start server after creation? default true (required) + @type ex_isStarted: C{bool} + + @return: The newly created L{Node}. NOTE: Opsource does not provide a way to + determine the ID of the server that was just created, so the returned + L{Node} is not guaranteed to be the same one that was created. This + is only the case when multiple nodes with the same name exist. + """ + name = kwargs['name'] + image = kwargs['image'] + + # XXX: Node sizes can be adjusted after a node is created, but cannot be + # set at create time because size is part of the image definition. + size = NodeSize(id=0, name='', ram=0, disk=None, bandwidth=None, + price=0, driver=self.connection.driver) + + password = None + if kwargs.has_key('auth'): + auth = kwargs['auth'] + if isinstance(auth, NodeAuthPassword): + password = auth.password + else: + raise ValueError('auth must be of NodeAuthPassword type') + + ex_description = kwargs['ex_description'] + ex_isStarted = kwargs['ex_isStarted'] + ex_network = kwargs['ex_network'] + vlanResourcePath = "%s/%s" % (self.connection.get_resource_path(), ex_network.id) + + imageResourcePath = None + if image.extra.has_key('resourcePath'): + imageResourcePath = image.extra['resourcePath'] + else: + imageResourcePath = "%s/%s" % (self.connection.get_resource_path(), image.id) + + server_elm = ET.Element('Server', {'xmlns': SERVER_NS}) + ET.SubElement(server_elm, "name").text = name + ET.SubElement(server_elm, "description").text = ex_description + ET.SubElement(server_elm, "vlanResourcePath").text = vlanResourcePath + ET.SubElement(server_elm, "imageResourcePath").text = imageResourcePath + ET.SubElement(server_elm, "administratorPassword").text = password + ET.SubElement(server_elm, "isStarted").text = str(ex_isStarted) + + data = self.connection.request_with_orgId('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 + # because opsource allows multiple nodes to have the same name + return filter(lambda x: x.name == name, self.list_nodes())[-1] + + def reboot_node(self, node): + """reboots the node""" + body = self.connection.request_with_orgId('server/%s?restart' % node.id).object + result = body.findtext(fixxpath(body, "result")) + return result == 'SUCCESS' + + def destroy_node(self, node): + """Destroys the node""" + body = self.connection.request_with_orgId('server/%s?delete' % node.id).object + result = body.findtext(fixxpath(body, "result")) + return result == 'SUCCESS' + + def ex_start_node(self, node): + """Powers on an existing deployed server""" + body = self.connection.request_with_orgId('server/%s?start' % node.id).object + result = body.findtext(fixxpath(body, "result")) + return result == 'SUCCESS' + + def ex_shutdown_graceful(self, node): + """This function will attempt to "gracefully" stop a server by initiating a + shutdown sequence within the guest operating system. A successful response + on this function means the system has successfully passed the + request into the operating system. + """ + body = self.connection.request_with_orgId('server/%s?shutdown' % node.id).object + result = body.findtext(fixxpath(body, "result")) + return result == 'SUCCESS' + + def ex_power_off(self, node): + """This function will abruptly power-off a server. Unlike ex_shutdown_graceful, + success ensures the node will stop but some OS and application configurations may + be adversely affected by the equivalent of pulling the power plug out of the + machine. + """ + body = self.connection.request_with_orgId('server/%s?poweroff' % node.id).object + result = body.findtext(fixxpath(body, "result")) + return result == 'SUCCESS' + + def ex_list_networks(self): + """List networks deployed across all data center locations for your + organization. The response includes the location of each network. + + Returns a list of OpsourceNetwork objects + """ + return self._to_networks(self.connection.request_with_orgId('networkWithLocation').object) + + def ex_get_location_by_id(self, id): + location = None + if id is not None: + location = filter(lambda x: x.id == id, self.list_locations())[0] + return location + + def _to_networks(self, object): + node_elements = object.findall(fixxpath(object, "network")) + return [ self._to_network(el) for el in node_elements ] + + def _to_network(self, element): + multicast = False + if element.findtext(fixxpath(element, "multicast")) == 'true': + multicast = True + + status = self._to_status(element.find(fixxpath(element, "status"))) + + location_id = element.findtext(fixxpath(element, "location")) + location = self.ex_get_location_by_id(location_id) + + return OpsourceNetwork(id=element.findtext(fixxpath(element, "id")), + name=element.findtext(fixxpath(element, "name")), + description=element.findtext(fixxpath(element, "description")), + location=location, + privateNet=element.findtext(fixxpath(element, "privateNet")), + multicast=multicast, + status=status) + + def _to_locations(self, object): + node_elements = object.findall(fixxpath(object, "datacenter")) + return [ self._to_location(el) for el in node_elements ] + + def _to_location(self, element): + l = NodeLocation(id=element.findtext(fixxpath(element, "location")), + name=element.findtext(fixxpath(element, "displayName")), + country=element.findtext(fixxpath(element, "country")), + driver=self) + return l + + def _to_nodes(self, object): + node_elements = object.findall(fixxpath(object, "DeployedServer")) + node_elements.extend(object.findall(fixxpath(object, "PendingDeployServer"))) + return [ self._to_node(el) for el in node_elements ] + + def _to_node(self, element): + if element.findtext(fixxpath(element, "isStarted")) == 'true': + state = NodeState.RUNNING + else: + state = NodeState.TERMINATED + + status = self._to_status(element.find(fixxpath(element, "status"))) + + extra = { + 'description': element.findtext(fixxpath(element, "description")), + 'sourceImageId': element.findtext(fixxpath(element,"sourceImageId")), + 'networkId': element.findtext(fixxpath(element, "networkId")), + 'networkId': element.findtext(fixxpath(element, "networkId")), + 'machineName': element.findtext(fixxpath(element, "machineName")), + 'deployedTime': element.findtext(fixxpath(element, "deployedTime")), + 'cpuCount': element.findtext(fixxpath(element, "machineSpecification/cpuCount")), + 'memoryMb': element.findtext(fixxpath(element, "machineSpecification/memoryMb")), + 'osStorageGb': element.findtext(fixxpath(element, "machineSpecification/osStorageGb")), + 'additionalLocalStorageGb': element.findtext(fixxpath(element, "machineSpecification/additionalLocalStorageGb")), + 'OS_type': element.findtext(fixxpath(element, "machineSpecification/operatingSystem/type")), + 'OS_displayName': element.findtext(fixxpath(element, "machineSpecification/operatingSystem/displayName")), + 'status': status, + } + + n = Node(id=element.findtext(fixxpath(element, "id")), + name=element.findtext(fixxpath(element, "name")), + state=state, + public_ip="unknown", + private_ip=element.findtext(fixxpath(element, "privateIpAddress")), + driver=self.connection.driver, + extra=extra) + return n + + def _to_base_images(self, object): + node_elements = object.findall(fixxpath(object, "ServerImage")) + return [ self._to_base_image(el) for el in node_elements ] + + def _to_base_image(self, element): + ## place ## + ## probably need multiple _to_image() functions that parse differently + ## than + location_id = element.findtext(fixxpath(element, "location")) + location = self.ex_get_location_by_id(location_id) + + extra = { + 'description': element.findtext(fixxpath(element, "description")), + 'OS_type': element.findtext(fixxpath(element, "operatingSystem/type")), + 'OS_displayName': element.findtext(fixxpath(element, "operatingSystem/displayName")), + 'cpuCount': element.findtext(fixxpath(element, "cpuCount")), + 'resourcePath': element.findtext(fixxpath(element, "resourcePath")), + 'memory': element.findtext(fixxpath(element, "memory")), + 'osStorage': element.findtext(fixxpath(element, "osStorage")), + 'additionalStorage': element.findtext(fixxpath(element, "additionalStorage")), + 'created': element.findtext(fixxpath(element, "created")), + 'location': location, + } + + i = NodeImage(id=str(element.findtext(fixxpath(element, "id"))), + name=str(element.findtext(fixxpath(element, "name"))), + extra=extra, + driver=self.connection.driver) + return i + + def _to_status(self, element): + if element == None: + return OpsourceStatus() + s = OpsourceStatus(action=element.findtext(fixxpath(element, "action")), + requestTime=element.findtext(fixxpath(element, "requestTime")), + userName=element.findtext(fixxpath(element, "userName")), + numberOfSteps=element.findtext(fixxpath(element, "numberOfSteps")), + step_name=element.findtext(fixxpath(element, "step/name")), + step_number=element.findtext(fixxpath(element, "step/number")), + step_percentComplete=element.findtext(fixxpath(element, "step/percentComplete")), + failureReason=element.findtext(fixxpath(element, "failureReason"))) + return s diff --git libcloud/compute/providers.py libcloud/compute/providers.py index 47b0c21..0668a5c 100644 --- libcloud/compute/providers.py +++ libcloud/compute/providers.py @@ -79,6 +79,8 @@ DRIVERS = { ('libcloud.compute.drivers.bluebox', 'BlueboxNodeDriver'), Provider.GANDI: ('libcloud.compute.drivers.gandi', 'GandiNodeDriver'), + Provider.OPSOURCE: + ('libcloud.compute.drivers.opsource', 'OpsourceNodeDriver'), } def get_driver(provider): diff --git libcloud/compute/types.py libcloud/compute/types.py index 43adaa2..4c08b04 100644 --- libcloud/compute/types.py +++ libcloud/compute/types.py @@ -53,6 +53,7 @@ class Provider(object): @cvar CLOUDSIGMA: CloudSigma @cvar NIMBUS: Nimbus @cvar BLUEBOX: Bluebox + @cvar OPSOURCE: Opsource Cloud """ DUMMY = 0 EC2 = 1 # deprecated name @@ -86,6 +87,7 @@ class Provider(object): NIMBUS = 27 BLUEBOX = 28 GANDI = 29 + OPSOURCE = 30 class NodeState(object): """ diff --git libcloud/drivers/__init__.py libcloud/drivers/__init__.py index b95d6be..5958bd5 100644 --- libcloud/drivers/__init__.py +++ libcloud/drivers/__init__.py @@ -35,4 +35,5 @@ __all__ = [ 'vcloud', 'voxel', 'vpsnet', + 'opsource', ] diff --git libcloud/drivers/opsource.py libcloud/drivers/opsource.py new file mode 100644 index 0000000..9bf8855 --- /dev/null +++ libcloud/drivers/opsource.py @@ -0,0 +1,19 @@ +# 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. + +from libcloud.utils import deprecated_warning +from libcloud.compute.drivers.opsource import * + +deprecated_warning(__name__) diff --git test/compute/fixtures/opsource/_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server.xml test/compute/fixtures/opsource/_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server.xml new file mode 100644 index 0000000..191c7ae --- /dev/null +++ test/compute/fixtures/opsource/_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server.xml @@ -0,0 +1,6 @@ + + Deploy Server + SUCCESS + Server "Deploy" issued + REASON_0 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_datacenter.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_datacenter.xml new file mode 100644 index 0000000..4870ee1 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_datacenter.xml @@ -0,0 +1,12 @@ + + + + NA1 + US - East + Ashburn + Virginia + US + https://opsource-na1.cloud-vpn.net/ + true + + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml new file mode 100644 index 0000000..ca27554 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml @@ -0,0 +1,11 @@ + + + + 53b4c05b-341e-4ac3-b688-bdd74e53ca9b + test-net1 + test-net1 description + NA1 + 10.162.1.0 + false + + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete.xml new file mode 100644 index 0000000..90686b9 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete.xml @@ -0,0 +1,7 @@ + + + Delete Server + SUCCESS + Server "Delete" issued + REASON_0 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete_INPROGRESS.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete_INPROGRESS.xml new file mode 100644 index 0000000..df55852 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete_INPROGRESS.xml @@ -0,0 +1,7 @@ + + + Delete Server + ERROR + Operation in progress on Server with Id 11 + REASON_392 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff.xml new file mode 100644 index 0000000..03c0f3d --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff.xml @@ -0,0 +1,7 @@ + + + Power Off Server + SUCCESS + Server "Power Off" issued + REASON_0 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff_INPROGRESS.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff_INPROGRESS.xml new file mode 100644 index 0000000..7b9a9d7 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff_INPROGRESS.xml @@ -0,0 +1,7 @@ + + + Power Off Server + ERROR + Operation in progress on Server with Id 11 + REASON_392 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart.xml new file mode 100644 index 0000000..0638feb --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart.xml @@ -0,0 +1,6 @@ + + Restart Server + SUCCESS + Server "Restart" issued + REASON_0 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart_INPROGRESS.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart_INPROGRESS.xml new file mode 100644 index 0000000..ab9e31f --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart_INPROGRESS.xml @@ -0,0 +1,7 @@ + + + Restart Server + ERROR + Operation in progress on Server with Id 11 + REASON_392 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown.xml new file mode 100644 index 0000000..b0937c8 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown.xml @@ -0,0 +1,6 @@ + + Graceful Shutdown Server + SUCCESS + Server "Graceful Shutdown" issued + REASON_0 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown_INPROGRESS.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown_INPROGRESS.xml new file mode 100644 index 0000000..eb3cbb5 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown_INPROGRESS.xml @@ -0,0 +1,7 @@ + + + Graceful Shutdown Server + ERROR + Operation in progress on Server with Id 11 + REASON_392 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start.xml new file mode 100644 index 0000000..274e05f --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start.xml @@ -0,0 +1,7 @@ + + + Start Server + SUCCESS + Server "Start" issued + REASON_0 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start_INPROGRESS.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start_INPROGRESS.xml new file mode 100644 index 0000000..6d1714f --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start_INPROGRESS.xml @@ -0,0 +1,7 @@ + + + Start Server + ERROR + Operation in progress on Server with Id 11 + REASON_392 + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployed.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployed.xml new file mode 100644 index 0000000..ae48208 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployed.xml @@ -0,0 +1,45 @@ + + + + abadbc7e-9e10-46ca-9d4a-194bcc6b6c16 + testnode01 + this is testnode01 description + + 2 + 2048 + 10 + 20 + + UNIX + REDHAT5/64 + + + 44ed8b72-ebea-11df-bdc1-001517c46384 + 53b4c05b-341e-4ac3-b688-bdd78e43ca9e + 10.162.1.1 + 10-162-1-1 + true + 2011-03-02T17:16:09.882Z + + + dbadbc8e-9e10-56ca-5d4a-155bcc5b5c15 + testnode02 + this is testnode02 description + + 4 + 4096 + 10 + 20 + + UNIX + REDHAT5/64 + + + 44ed8b72-ebea-11df-bdc1-001517c46384 + 53b4c05b-341e-4ac3-b688-bdd78e43ca9e + 10.162.1.2 + 10-162-1-2 + true + 2011-03-02T17:16:10.882Z + + diff --git test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_pendingDeploy.xml test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_pendingDeploy.xml new file mode 100644 index 0000000..f24073f --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_pendingDeploy.xml @@ -0,0 +1,26 @@ + + + + e75ead52-692f-4314-8725-c8a4f4d13a87 + test2 + test2 node + + 1 + 2048 + 10 + 0 + + UNIX + REDHAT5/64 + + + 52ed8b72-ebea-11df-bdc1-001517c46384 + 52f4c05b-341e-4ac3-b688-bdd78e43ca9e + 10.162.151.11 + + DEPLOY_SERVER + 2011-03-20T22:32:23.000Z + copia + + + diff --git test/compute/fixtures/opsource/oec_0_9_base_image.xml test/compute/fixtures/opsource/oec_0_9_base_image.xml new file mode 100644 index 0000000..3be14f0 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_base_image.xml @@ -0,0 +1,339 @@ + + + + 52ed8b72-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed8b72-ebea-11df-bdc1-001517c46384 + RedHat 5.5 64-bit 1 CPU + RedHat 5.5 Enterprise (Tikanga), 64-bit + + UNIX + REDHAT5/64 + + NA1 + 1 + 2048 + 10 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed8dca-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed8dca-ebea-11df-bdc1-001517c46384 + RedHat 5.5 64-bit 2 CPU + RedHat 5.5 Enterprise (Tikanga), 64-bit + + UNIX + REDHAT5/64 + + NA1 + 2 + 4096 + 10 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed8ed8-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed8ed8-ebea-11df-bdc1-001517c46384 + RedHat 5.5 64-bit 4 CPU + RedHat 5.5 Enterprise (Tikanga), 64-bit + + UNIX + REDHAT5/64 + + NA1 + 4 + 6144 + 10 + 0 + 1970-01-01T00:00:02.010Z + + + 6fc040ae-3605-11e0-bfb5-001517c46384 + /oec/base/image/6fc040ae-3605-11e0-bfb5-001517c46384 + RedHat 5.5 32-bit 1 CPU + RedHat 5.5 Enterprise (Tikanga), 32-bit + + UNIX + REDHAT5/32 + + NA1 + 1 + 2048 + 10 + 0 + 2011-02-11T17:36:19.000Z + + + 52ed92d4-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed92d4-ebea-11df-bdc1-001517c46384 + Ubuntu 8.04.4 2 CPU + Ubuntu 8.04.4 LTS, 64-bit + + UNIX + UBUNTU8/64 + + NA1 + 2 + 4096 + 10 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed876c-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed876c-ebea-11df-bdc1-001517c46384 + Win2008 Ent 64-bit R2 2 CPU + Windows 2008 Enterprise R2 64-bit + + WINDOWS + WIN2008R2E/64 + + NA1 + 2 + 4096 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed8a5a-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed8a5a-ebea-11df-bdc1-001517c46384 + Win2008 Ent 64-bit R2 4 CPU + Windows 2008 Enterprise R2 64-bit + + WINDOWS + WIN2008R2E/64 + + NA1 + 4 + 8192 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed865e-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed865e-ebea-11df-bdc1-001517c46384 + Win2008 Std 64-bit R2 2 CPU + Windows 2008 Standard R2 64-bit + + WINDOWS + WIN2008R2S/64 + + NA1 + 2 + 4096 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed7b96-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed7b96-ebea-11df-bdc1-001517c46384 + Win2008 Std 32-bit 1 CPU + Windows 2008 Standard SP2 32-bit + + WINDOWS + WIN2008S/32 + + NA1 + 1 + 2048 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed7cb8-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed7cb8-ebea-11df-bdc1-001517c46384 + Win2008 Std 32-bit 2 CPU + Windows 2008 Standard SP2 32-bit + + WINDOWS + WIN2008S/32 + + NA1 + 2 + 4096 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed7da8-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed7da8-ebea-11df-bdc1-001517c46384 + Win2008 Std 32-bit 4 CPU + Windows 2008 Standard SP2 32-bit + + WINDOWS + WIN2008S/32 + + NA1 + 4 + 4096 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed7ea2-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed7ea2-ebea-11df-bdc1-001517c46384 + Win2008 Ent 32-bit 2 CPU + Windows 2008 Enterprise SP2 32-bit + + WINDOWS + WIN2008E/32 + + NA1 + 2 + 4096 + 50 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed8fd2-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed8fd2-ebea-11df-bdc1-001517c46384 + Red Hat 4.8 32-bit 1 CPU + Red Hat ES 4.8 (Nahant), 32-bit + + UNIX + REDHAT4/32 + + NA1 + 1 + 2048 + 10 + 0 + 1970-01-01T00:00:02.010Z + + + 52ed90cc-ebea-11df-bdc1-001517c46384 + /oec/base/image/52ed90cc-ebea-11df-bdc1-001517c46384 + CentOS 5.5 32-bit 1 CPU + CentOS release 5.5, 32-bit + + UNIX + CENTOS5/32 + + NA1 + 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 test/compute/fixtures/opsource/oec_0_9_myaccount.xml test/compute/fixtures/opsource/oec_0_9_myaccount.xml new file mode 100644 index 0000000..4f3b132 --- /dev/null +++ test/compute/fixtures/opsource/oec_0_9_myaccount.xml @@ -0,0 +1,26 @@ + + + testuser + Test User + Test + User + test@example.com + 8a8f6abc-2745-4d8a-9cbc-8dabe5a7d0e4 + + + create image + + + reports + + + server + + + primary administrator + + + network + + + diff --git test/compute/test_opsource.py test/compute/test_opsource.py new file mode 100644 index 0000000..1669e68 --- /dev/null +++ test/compute/test_opsource.py @@ -0,0 +1,223 @@ +# 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. +import sys +import unittest +import httplib + +from libcloud.common.types import InvalidCredsError +from libcloud.compute.drivers.opsource import OpsourceNodeDriver as Opsource +from libcloud.compute.drivers.opsource import OpsourceAPIException, OpsourceNetwork +from libcloud.compute.base import Node, NodeImage, NodeSize, NodeAuthPassword, NodeLocation + +from test import MockHttp +from test.compute import TestCaseMixin +from test.file_fixtures import ComputeFileFixtures + +from test.secrets import OPSOURCE_USER, OPSOURCE_PASS + +class OpsourceTests(unittest.TestCase, TestCaseMixin): + + def setUp(self): + Opsource.connectionCls.conn_classes = (None, OpsourceMockHttp) + OpsourceMockHttp.type = None + self.driver = Opsource(OPSOURCE_USER, OPSOURCE_PASS) + + def test_invalid_creds(self): + OpsourceMockHttp.type = 'UNAUTHORIZED' + try: + self.driver.list_nodes() + self.assertTrue(False) # Above command should have thrown an InvalidCredsException + except InvalidCredsError: + self.assertTrue(True) + + def test_list_sizes_response(self): + OpsourceMockHttp.type = None + ret = self.driver.list_sizes() + self.assertEqual(len(ret), 1) + size = ret[0] + self.assertEqual(size.name, 'default') + + def test_reboot_node_response(self): + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + ret = node.reboot() + self.assertTrue(ret is True) + + def test_reboot_node_response_INPROGRESS(self): + OpsourceMockHttp.type = 'INPROGRESS' + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + try: + ret = node.reboot() + self.assertTrue(False) # above command should have thrown OpsourceAPIException + except OpsourceAPIException: + self.assertTrue(True) + + def test_destroy_node_response(self): + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + ret = node.destroy() + self.assertTrue(ret is True) + + def test_destroy_node_response_INPROGRESS(self): + OpsourceMockHttp.type = 'INPROGRESS' + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + try: + ret = node.destroy() + self.assertTrue(False) # above command should have thrown OpsourceAPIException + except OpsourceAPIException: + self.assertTrue(True) + + def test_create_node_response(self): + rootPw = NodeAuthPassword('pass123') + image = self.driver.list_images()[0] + location = self.driver.list_locations()[0] + network = self.driver.ex_list_networks()[0] + node = self.driver.create_node(name='test2', image=image, auth=rootPw, + ex_description='test2 node', ex_network=network, + ex_isStarted=False) + self.assertEqual(node.id, 'e75ead52-692f-4314-8725-c8a4f4d13a87') + self.assertEqual(node.extra['status'].action, 'DEPLOY_SERVER') + + def test_ex_shutdown_graceful(self): + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + ret = self.driver.ex_shutdown_graceful(node) + self.assertTrue(ret is True) + + def test_ex_shutdown_graceful_INPROGRESS(self): + OpsourceMockHttp.type = 'INPROGRESS' + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + try: + ret = self.driver.ex_shutdown_graceful(node) + self.assertTrue(False) # above command should have thrown OpsourceAPIException + except OpsourceAPIException: + self.assertTrue(True) + + def test_ex_start_node(self): + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + ret = self.driver.ex_start_node(node) + self.assertTrue(ret is True) + + def test_ex_start_node_INPROGRESS(self): + OpsourceMockHttp.type = 'INPROGRESS' + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + try: + ret = self.driver.ex_start_node(node) + self.assertTrue(False) # above command should have thrown OpsourceAPIException + except OpsourceAPIException: + self.assertTrue(True) + + def test_ex_power_off(self): + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + ret = self.driver.ex_power_off(node) + self.assertTrue(ret is True) + + def test_ex_power_off_INPROGRESS(self): + OpsourceMockHttp.type = 'INPROGRESS' + node = Node(id='11', name=None, state=None, + public_ip=None, private_ip=None, driver=self.driver) + try: + ret = self.driver.ex_power_off(node) + self.assertTrue(False) # above command should have thrown OpsourceAPIException + except OpsourceAPIException: + self.assertTrue(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)) + +class OpsourceMockHttp(MockHttp): + + fixtures = ComputeFileFixtures('opsource') + + 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_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_base_image(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_base_image.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployed(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_deployed.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_pendingDeploy(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_pendingDeploy.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_datacenter(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_datacenter.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11(self, method, url, body, headers): + body = None + action = url.split('?')[-1] + + if action == 'restart': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart.xml') + elif action == 'shutdown': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown.xml') + elif action == 'delete': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete.xml') + elif action == 'start': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start.xml') + elif action == 'poweroff': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff.xml') + + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_INPROGRESS(self, method, url, body, headers): + body = None + action = url.split('?')[-1] + + if action == 'restart': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_restart_INPROGRESS.xml') + elif action == 'shutdown': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_shutdown_INPROGRESS.xml') + elif action == 'delete': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_delete_INPROGRESS.xml') + elif action == 'start': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_start_INPROGRESS.xml') + elif action == 'poweroff': + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_11_poweroff_INPROGRESS.xml') + + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server(self, method, url, body, headers): + body = self.fixtures.load('_oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation(self, method, url, body, headers): + body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_networkWithLocation.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + +if __name__ == '__main__': + sys.exit(unittest.main()) diff --git test/secrets.py-dist test/secrets.py-dist index e245298..cb8bf12 100644 --- test/secrets.py-dist +++ test/secrets.py-dist @@ -63,3 +63,6 @@ GANDI_USER = '' OPENNEBULA_USER = '' OPENNEBULA_KEY = '' + +OPSOURCE_USER='' +OPSOURCE_PASS=''