You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by qu...@apache.org on 2017/09/27 03:12:47 UTC
[04/14] libcloud git commit: New compute driver for UpCloud
New compute driver for UpCloud
Signed-off-by: Quentin Pradet <qu...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/abbd9bb6
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/abbd9bb6
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/abbd9bb6
Branch: refs/heads/trunk
Commit: abbd9bb63f8b1295bc67fbcf8f271f89ed79e4d6
Parents: 5892fa1
Author: Mika Lackman <mi...@upcloud.com>
Authored: Mon Aug 14 14:03:29 2017 +0300
Committer: Quentin Pradet <qu...@apache.org>
Committed: Wed Sep 27 07:04:33 2017 +0400
----------------------------------------------------------------------
libcloud/common/upcloud.py | 183 +++++++++
libcloud/compute/drivers/upcloud.py | 277 +++++++++++++
libcloud/compute/types.py | 2 +
libcloud/test/common/test_upcloud.py | 212 ++++++++++
.../compute/fixtures/upcloud/api_1_2_plan.json | 38 ++
.../fixtures/upcloud/api_1_2_server.json | 22 +
...er_00893c98-5d5a-4363-b177-88df518a2b60.json | 58 +++
...er_00f8c525-7e62-4108-8115-3958df5b43dc.json | 57 +++
...525-7e62-4108-8115-3958df5b43dc_restart.json | 57 +++
.../upcloud/api_1_2_server_from_cdrom.json | 65 +++
.../upcloud/api_1_2_server_from_template.json | 59 +++
.../fixtures/upcloud/api_1_2_storage_cdrom.json | 411 +++++++++++++++++++
.../upcloud/api_1_2_storage_template.json | 114 +++++
.../compute/fixtures/upcloud/api_1_2_zone.json | 30 ++
.../upcloud/api_1_2_zone_failed_auth.json | 6 +
libcloud/test/compute/test_upcloud.py | 248 +++++++++++
libcloud/test/secrets.py-dist | 1 +
17 files changed, 1840 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/common/upcloud.py
----------------------------------------------------------------------
diff --git a/libcloud/common/upcloud.py b/libcloud/common/upcloud.py
new file mode 100644
index 0000000..432606a
--- /dev/null
+++ b/libcloud/common/upcloud.py
@@ -0,0 +1,183 @@
+# 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 json
+import time
+
+from libcloud.common.exceptions import BaseHTTPError
+
+
+class UpcloudTimeoutException(Exception):
+ pass
+
+
+class UpcloudCreateNodeRequestBody(object):
+ """Body of the create_node request
+
+ Takes the create_node arguments (**kwargs) and constructs the request body
+ """
+ def __init__(self, user_id, name, size, image, location, auth=None):
+ self.body = {
+ 'server': {
+ 'title': name,
+ 'hostname': 'localhost',
+ 'plan': size.id,
+ 'zone': location.id,
+ 'login_user': _LoginUser(user_id, auth).to_dict(),
+ 'storage_devices': _StorageDevice(image, size).to_dict()
+ }
+ }
+
+ def to_json(self):
+ """Serializes the body to json"""
+ return json.dumps(self.body)
+
+
+class UpcloudNodeDestroyer(object):
+ """Destroyes the node.
+ Node must be first stopped and then it can be
+ destroyed"""
+
+ WAIT_AMOUNT = 2
+ SLEEP_COUNT_TO_TIMEOUT = 20
+
+ def __init__(self, upcloud_node_operations, sleep_func=None):
+ self._operations = upcloud_node_operations
+ self._sleep_func = sleep_func or time.sleep
+ self._sleep_count = 0
+
+ def destroy_node(self, node_id):
+ self._stop_called = False
+ self._sleep_count = 0
+ return self._do_destroy_node(node_id)
+
+ def _do_destroy_node(self, node_id):
+ state = self._operations.node_state(node_id)
+ if state == 'stopped':
+ self._operations.destroy_node(node_id)
+ return True
+ elif state == 'error':
+ return False
+ elif state == 'started':
+ if not self._stop_called:
+ self._operations.stop_node(node_id)
+ self._stop_called = True
+ else:
+ # Waiting for started state to change and
+ # not calling stop again
+ self._sleep()
+ return self._do_destroy_node(node_id)
+ elif state == 'maintenance':
+ # Lets wait maintenace state to go away and retry destroy
+ self._sleep()
+ return self._do_destroy_node(node_id)
+ elif state is None: # Server not found any more
+ return True
+
+ def _sleep(self):
+ if self._sleep_count > self.SLEEP_COUNT_TO_TIMEOUT:
+ raise UpcloudTimeoutException("Timeout, could not destroy node")
+ self._sleep_count += 1
+ self._sleep_func(self.WAIT_AMOUNT)
+
+
+class UpcloudNodeOperations(object):
+
+ def __init__(self, connection):
+ self.connection = connection
+
+ def stop_node(self, node_id):
+ body = {
+ 'stop_server': {
+ 'stop_type': 'hard'
+ }
+ }
+ self.connection.request('1.2/server/{0}/stop'.format(node_id),
+ method='POST',
+ data=json.dumps(body))
+
+ def node_state(self, node_id):
+ action = '1.2/server/{0}'.format(node_id)
+ try:
+ response = self.connection.request(action)
+ return response.object['server']['state']
+ except BaseHTTPError as e:
+ if e.code == 404:
+ return None
+ raise
+
+ def destroy_node(self, node_id):
+ self.connection.request('1.2/server/{0}'.format(node_id),
+ method='DELETE')
+
+
+class _LoginUser(object):
+
+ def __init__(self, user_id, auth=None):
+ self.user_id = user_id
+ self.auth = auth
+
+ def to_dict(self):
+ login_user = {'username': self.user_id}
+ if self.auth is not None:
+ login_user['ssh_keys'] = {
+ 'ssh_key': [self.auth.pubkey]
+ }
+ else:
+ login_user['create_password'] = 'yes'
+
+ return login_user
+
+
+class _StorageDevice(object):
+
+ def __init__(self, image, size):
+ self.image = image
+ self.size = size
+
+ def to_dict(self):
+ extra = self.image.extra
+ if extra['type'] == 'template':
+ return self._storage_device_for_template_image()
+ elif extra['type'] == 'cdrom':
+ return self._storage_device_for_cdrom_image()
+
+ def _storage_device_for_template_image(self):
+ storage_devices = {
+ 'storage_device': [{
+ 'action': 'clone',
+ 'title': self.image.name,
+ 'storage': self.image.id
+ }]
+ }
+ return storage_devices
+
+ def _storage_device_for_cdrom_image(self):
+ storage_devices = {
+ 'storage_device': [
+ {
+ 'action': 'create',
+ 'title': self.image.name,
+ 'size': self.size.disk,
+ 'tier': self.size.extra['storage_tier']
+
+ },
+ {
+ 'action': 'attach',
+ 'storage': self.image.id,
+ 'type': 'cdrom'
+ }
+ ]
+ }
+ return storage_devices
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/compute/drivers/upcloud.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/upcloud.py b/libcloud/compute/drivers/upcloud.py
new file mode 100644
index 0000000..b9daf84
--- /dev/null
+++ b/libcloud/compute/drivers/upcloud.py
@@ -0,0 +1,277 @@
+# 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.
+"""
+Upcloud node driver
+"""
+import base64
+import json
+
+from libcloud.utils.py3 import httplib, b
+from libcloud.compute.base import NodeDriver, NodeLocation, NodeSize
+from libcloud.compute.base import NodeImage, Node, NodeState
+from libcloud.compute.types import Provider
+from libcloud.common.base import ConnectionUserAndKey, JsonResponse
+from libcloud.common.types import InvalidCredsError
+from libcloud.common.upcloud import UpcloudCreateNodeRequestBody
+from libcloud.common.upcloud import UpcloudNodeDestroyer
+from libcloud.common.upcloud import UpcloudNodeOperations
+
+SERVER_STATE = {
+ 'started': NodeState.RUNNING,
+ 'stopped': NodeState.STOPPED,
+ 'maintenance': NodeState.RECONFIGURING,
+ 'error': NodeState.ERROR
+}
+
+
+class UpcloudResponse(JsonResponse):
+ """Response class for UpcloudDriver"""
+
+ def success(self):
+ if self.status == httplib.NO_CONTENT:
+ return True
+ return super(UpcloudResponse, self).success()
+
+ def parse_error(self):
+ data = self.parse_body()
+ if self.status == httplib.UNAUTHORIZED:
+ raise InvalidCredsError(value=data['error']['error_message'])
+ return data
+
+
+class UpcloudConnection(ConnectionUserAndKey):
+ """Connection class for UpcloudDriver"""
+ host = 'api.upcloud.com'
+ responseCls = UpcloudResponse
+
+ def add_default_headers(self, headers):
+ """Adds headers that are needed for all requests"""
+ headers['Authorization'] = self._basic_auth()
+ headers['Accept'] = 'application/json'
+ headers['Content-Type'] = 'application/json'
+ return headers
+
+ def _basic_auth(self):
+ """Constructs basic auth header content string"""
+ credentials = b("{0}:{1}".format(self.user_id, self.key))
+ credentials = base64.b64encode(credentials)
+ return 'Basic {0}'.format(credentials.decode('ascii'))
+
+
+class UpcloudDriver(NodeDriver):
+ """Upcloud node driver
+
+ :keyword username: Username required for authentication
+ :type username: ``str``
+
+ :keyword password: Password required for authentication
+ :type password: ``str``
+ """
+ type = Provider.UPCLOUD
+ name = 'Upcloud'
+ website = 'https://www.upcloud.com'
+ connectionCls = UpcloudConnection
+ features = {'create_node': ['ssh_key', 'generates_password']}
+
+ def __init__(self, username, password, **kwargs):
+ super(UpcloudDriver, self).__init__(key=username, secret=password,
+ **kwargs)
+
+ def list_locations(self):
+ """
+ List available locations for deployment
+
+ :rtype: ``list`` of :class:`NodeLocation`
+ """
+ response = self.connection.request('1.2/zone')
+ return self._to_node_locations(response.object['zones']['zone'])
+
+ def list_sizes(self):
+ """
+ List available plans
+
+ :rtype: ``list`` of :class:`NodeSize`
+ """
+ response = self.connection.request('1.2/plan')
+ return self._to_node_sizes(response.object['plans']['plan'])
+
+ def list_images(self):
+ """
+ List available distributions.
+
+ :rtype: ``list`` of :class:`NodeImage`
+ """
+ response = self.connection.request('1.2/storage/template')
+ obj = response.object
+ response = self.connection.request('1.2/storage/cdrom')
+ storage = response.object['storages']['storage']
+ obj['storages']['storage'].extend(storage)
+ return self._to_node_images(obj['storages']['storage'])
+
+ def create_node(self, name, size, image, location, auth=None, **kwargs):
+ """
+ Creates instance to upcloud.
+
+ If auth is not given then password will be generated.
+
+ :param name: String with a name for this new node (required)
+ :type name: ``str``
+
+ :param size: The size of resources allocated to this node.
+ (required)
+ :type size: :class:`.NodeSize`
+
+ :param image: OS Image to boot on node. (required)
+ :type image: :class:`.NodeImage`
+
+ :param location: Which data center to create a node in. If empty,
+ undefined behavior will be selected. (optional)
+ :type location: :class:`.NodeLocation`
+
+ :param auth: Initial authentication information for the node
+ (optional)
+ :type auth: :class:`.NodeAuthSSHKey`
+
+ :return: The newly created node.
+ :rtype: :class:`.Node`
+ """
+ body = UpcloudCreateNodeRequestBody(user_id=self.connection.user_id,
+ name=name, size=size, image=image,
+ location=location, auth=auth)
+ response = self.connection.request('1.2/server',
+ method='POST',
+ data=body.to_json())
+ server = response.object['server']
+ # Upcloud server's are in maintenace state when goind
+ # from state to other, it is safe to assume STARTING state
+ return self._to_node(server, state=NodeState.STARTING)
+
+ def list_nodes(self):
+ """
+ List nodes
+
+ :return: List of node objects
+ :rtype: ``list`` of :class:`Node`
+ """
+ servers = []
+ for nid in self._node_ids():
+ response = self.connection.request('1.2/server/{0}'.format(nid))
+ servers.append(response.object['server'])
+ return self._to_nodes(servers)
+
+ def reboot_node(self, node):
+ """
+ Reboot the given node
+
+ :param node: the node to reboot
+ :type node: :class:`Node`
+
+ :rtype: ``bool``
+ """
+ body = {
+ 'restart_server': {
+ 'stop_type': 'hard'
+ }
+ }
+ self.connection.request('1.2/server/{0}/restart'.format(node.id),
+ method='POST',
+ data=json.dumps(body))
+ return True
+
+ def destroy_node(self, node):
+ """
+ Destroy the given node
+
+ The disk resources, attached to node, will not be removed.
+
+ :param node: the node to destroy
+ :type node: :class:`Node`
+
+ :rtype: ``bool``
+ """
+
+ operations = UpcloudNodeOperations(self.connection)
+ destroyer = UpcloudNodeDestroyer(operations)
+ return destroyer.destroy_node(node.id)
+
+ def _node_ids(self):
+ """Returns list of server uids currently on upcloud"""
+ response = self.connection.request('1.2/server')
+ servers = response.object['servers']['server']
+ return [server['uuid'] for server in servers]
+
+ def _to_nodes(self, servers):
+ return [self._to_node(server) for server in servers]
+
+ def _to_node(self, server, state=None):
+ ip_addresses = server['ip_addresses']['ip_address']
+ public_ips = [ip['address'] for ip in ip_addresses
+ if ip['access'] == 'public']
+ private_ips = [ip['address'] for ip in ip_addresses
+ if ip['access'] == 'private']
+
+ extra = {'vnc_password': server['vnc_password']}
+ if 'password' in server:
+ extra['password'] = server['password']
+ return Node(id=server['uuid'],
+ name=server['title'],
+ state=state or SERVER_STATE[server['state']],
+ public_ips=public_ips,
+ private_ips=private_ips,
+ driver=self,
+ extra=extra)
+
+ def _to_node_locations(self, zones):
+ return [self._construct_node_location(zone) for zone in zones]
+
+ def _construct_node_location(self, zone):
+ return NodeLocation(id=zone['id'],
+ name=zone['description'],
+ country=self._parse_country(zone['id']),
+ driver=self)
+
+ def _parse_country(self, zone_id):
+ """Parses the country information out of zone_id.
+ Zone_id format [country]_[city][number], like fi_hel1"""
+ return zone_id.split('-')[0].upper()
+
+ def _to_node_sizes(self, plans):
+ return [self._construct_node_size(plan) for plan in plans]
+
+ def _construct_node_size(self, plan):
+ extra = self._copy_dict(('core_number', 'storage_tier'), plan)
+ return NodeSize(id=plan['name'], name=plan['name'],
+ ram=plan['memory_amount'],
+ disk=plan['storage_size'],
+ bandwidth=plan['public_traffic_out'],
+ price=None, driver=self,
+ extra=extra)
+
+ def _to_node_images(self, images):
+ return [self._construct_node_image(image) for image in images]
+
+ def _construct_node_image(self, image):
+ extra = self._copy_dict(('access', 'license',
+ 'size', 'state', 'type'), image)
+ return NodeImage(id=image['uuid'],
+ name=image['title'],
+ driver=self,
+ extra=extra)
+
+ def _copy_dict(self, keys, d):
+ extra = {}
+ for key in keys:
+ extra[key] = d[key]
+ return extra
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/compute/types.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py
index deae506..b360c0e 100644
--- a/libcloud/compute/types.py
+++ b/libcloud/compute/types.py
@@ -98,6 +98,7 @@ class Provider(Type):
:cvar RACKSPACE_FIRST_GEN: Rackspace First Gen Cloud Servers
:cvar RIMUHOSTING: RimuHosting.com
:cvar TERREMARK: Terremark
+ :cvar UPCLOUD: Upcloud
:cvar VCL: VCL driver
:cvar VCLOUD: vmware vCloud
:cvar VPSNET: VPS.net
@@ -161,6 +162,7 @@ class Provider(Type):
SKALICLOUD = 'skalicloud'
SOFTLAYER = 'softlayer'
TERREMARK = 'terremark'
+ UPCLOUD = 'upcloud'
VCL = 'vcl'
VCLOUD = 'vcloud'
VOXEL = 'voxel'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/common/test_upcloud.py
----------------------------------------------------------------------
diff --git a/libcloud/test/common/test_upcloud.py b/libcloud/test/common/test_upcloud.py
new file mode 100644
index 0000000..76dd9c1
--- /dev/null
+++ b/libcloud/test/common/test_upcloud.py
@@ -0,0 +1,212 @@
+# 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 json
+
+from mock import Mock, call
+
+from libcloud.common.upcloud import UpcloudCreateNodeRequestBody, UpcloudNodeDestroyer, UpcloudNodeOperations
+from libcloud.common.upcloud import UpcloudTimeoutException
+from libcloud.compute.base import NodeImage, NodeSize, NodeLocation, NodeAuthSSHKey
+from libcloud.test import unittest
+
+
+class TestUpcloudCreateNodeRequestBody(unittest.TestCase):
+
+ def test_creating_node_from_template_image(self):
+ image = NodeImage(id='01000000-0000-4000-8000-000030060200',
+ name='Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ driver='',
+ extra={'type': 'template'})
+ location = NodeLocation(id='fi-hel1', name='Helsinki #1', country='FI', driver='')
+ size = NodeSize(id='1xCPU-1GB', name='1xCPU-1GB', ram=1024, disk=30, bandwidth=2048,
+ extra={'core_number': 1, 'storage_tier': 'maxiops'}, price=None, driver='')
+
+ body = UpcloudCreateNodeRequestBody(user_id='somename', name='ts', image=image, location=location, size=size)
+ json_body = body.to_json()
+ dict_body = json.loads(json_body)
+ expected_body = {
+ 'server': {
+ 'title': 'ts',
+ 'hostname': 'localhost',
+ 'plan': '1xCPU-1GB',
+ 'zone': 'fi-hel1',
+ 'login_user': {'username': 'somename',
+ 'create_password': 'yes'},
+ 'storage_devices': {
+ 'storage_device': [{
+ 'action': 'clone',
+ 'title': 'Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ 'storage': '01000000-0000-4000-8000-000030060200'
+ }]
+ },
+ }
+ }
+ self.assertDictEqual(expected_body, dict_body)
+
+ def test_creating_node_from_cdrom_image(self):
+ image = NodeImage(id='01000000-0000-4000-8000-000030060200',
+ name='Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ driver='',
+ extra={'type': 'cdrom'})
+ location = NodeLocation(id='fi-hel1', name='Helsinki #1', country='FI', driver='')
+ size = NodeSize(id='1xCPU-1GB', name='1xCPU-1GB', ram=1024, disk=30, bandwidth=2048,
+ extra={'core_number': 1, 'storage_tier': 'maxiops'}, price=None, driver='')
+
+ body = UpcloudCreateNodeRequestBody(user_id='somename', name='ts', image=image, location=location, size=size)
+ json_body = body.to_json()
+ dict_body = json.loads(json_body)
+ expected_body = {
+ 'server': {
+ 'title': 'ts',
+ 'hostname': 'localhost',
+ 'plan': '1xCPU-1GB',
+ 'zone': 'fi-hel1',
+ 'login_user': {'username': 'somename',
+ 'create_password': 'yes'},
+ 'storage_devices': {
+ 'storage_device': [
+ {
+ 'action': 'create',
+ 'size': 30,
+ 'tier': 'maxiops',
+ 'title': 'Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ },
+ {
+ 'action': 'attach',
+ 'storage': '01000000-0000-4000-8000-000030060200',
+ 'type': 'cdrom'
+ }
+ ]
+ }
+ }
+ }
+ self.assertDictEqual(expected_body, dict_body)
+
+ def test_creating_node_using_ssh_keys(self):
+ image = NodeImage(id='01000000-0000-4000-8000-000030060200',
+ name='Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ driver='',
+ extra={'type': 'template'})
+ location = NodeLocation(id='fi-hel1', name='Helsinki #1', country='FI', driver='')
+ size = NodeSize(id='1xCPU-1GB', name='1xCPU-1GB', ram=1024, disk=30, bandwidth=2048,
+ extra={'core_number': 1, 'storage_tier': 'maxiops'}, price=None, driver='')
+ auth = NodeAuthSSHKey('sshkey')
+
+ body = UpcloudCreateNodeRequestBody(user_id='somename', name='ts', image=image, location=location, size=size, auth=auth)
+ json_body = body.to_json()
+ dict_body = json.loads(json_body)
+ expected_body = {
+ 'server': {
+ 'title': 'ts',
+ 'hostname': 'localhost',
+ 'plan': '1xCPU-1GB',
+ 'zone': 'fi-hel1',
+ 'login_user': {
+ 'username': 'somename',
+ 'ssh_keys': {
+ 'ssh_key': [
+ 'sshkey'
+ ]
+ },
+ },
+ 'storage_devices': {
+ 'storage_device': [{
+ 'action': 'clone',
+ 'title': 'Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ 'storage': '01000000-0000-4000-8000-000030060200'
+ }]
+ },
+ }
+ }
+ self.assertDictEqual(expected_body, dict_body)
+
+
+class TestUpcloudNodeDestroyer(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_sleep = Mock()
+ self.mock_operations = Mock(spec=UpcloudNodeOperations)
+ self.destroyer = UpcloudNodeDestroyer(self.mock_operations, sleep_func=self.mock_sleep)
+
+ def test_node_already_in_stopped_state(self):
+ self.mock_operations.node_state.side_effect = ['stopped']
+
+ self.assertTrue(self.destroyer.destroy_node(1))
+
+ self.assertTrue(self.mock_operations.stop_node.call_count == 0)
+ self.mock_operations.destroy_node.assert_called_once_with(1)
+
+ def test_node_in_error_state(self):
+ self.mock_operations.node_state.side_effect = ['error']
+
+ self.assertFalse(self.destroyer.destroy_node(1))
+
+ self.assertTrue(self.mock_operations.stop_node.call_count == 0)
+ self.assertTrue(self.mock_operations.destroy_node.call_count == 0)
+
+ def test_node_in_started_state(self):
+ self.mock_operations.node_state.side_effect = ['started', 'stopped']
+
+ self.assertTrue(self.destroyer.destroy_node(1))
+
+ self.mock_operations.stop_node.assert_called_once_with(1)
+ self.mock_operations.destroy_node.assert_called_once_with(1)
+
+ def test_node_in_maintenace_state(self):
+ self.mock_operations.node_state.side_effect = ['maintenance', 'maintenance', None]
+
+ self.assertTrue(self.destroyer.destroy_node(1))
+
+ self.mock_sleep.assert_has_calls([call(self.destroyer.WAIT_AMOUNT), call(self.destroyer.WAIT_AMOUNT)])
+
+ self.assertTrue(self.mock_operations.stop_node.call_count == 0)
+ self.assertTrue(self.mock_operations.destroy_node.call_count == 0)
+
+ def test_node_statys_in_started_state_for_awhile(self):
+ self.mock_operations.node_state.side_effect = ['started', 'started', 'stopped']
+
+ self.assertTrue(self.destroyer.destroy_node(1))
+
+ # Only one all for stop should be done
+ self.mock_operations.stop_node.assert_called_once_with(1)
+ self.mock_sleep.assert_has_calls([call(self.destroyer.WAIT_AMOUNT)])
+ self.mock_operations.destroy_node.assert_called_once_with(1)
+
+ def test_reuse(self):
+ "Verify that internal flag self.destroyer._stop_node is handled properly"
+ self.mock_operations.node_state.side_effect = ['started', 'stopped', 'started', 'stopped']
+ self.assertTrue(self.destroyer.destroy_node(1))
+ self.assertTrue(self.destroyer.destroy_node(1))
+
+ self.assertEquals(self.mock_sleep.call_count, 0)
+ self.assertEquals(self.mock_operations.stop_node.call_count, 2)
+
+ def test_timeout(self):
+ self.mock_operations.node_state.side_effect = ['maintenance'] * 50
+
+ self.assertRaises(UpcloudTimeoutException, self.destroyer.destroy_node, 1)
+
+ def test_timeout_reuse(self):
+ "Verify sleep count is handled properly"
+ self.mock_operations.node_state.side_effect = ['maintenance'] * 50
+ self.assertRaises(UpcloudTimeoutException, self.destroyer.destroy_node, 1)
+
+ self.mock_operations.node_state.side_effect = ['maintenance', None]
+ self.assertTrue(self.destroyer.destroy_node(1))
+
+
+if __name__ == '__main__':
+ sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_plan.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_plan.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_plan.json
new file mode 100644
index 0000000..c59dcf0
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_plan.json
@@ -0,0 +1,38 @@
+{
+ "plans" : {
+ "plan" : [
+ {
+ "core_number" : 1,
+ "memory_amount" : 1024,
+ "name" : "1xCPU-1GB",
+ "public_traffic_out" : 2048,
+ "storage_size" : 30,
+ "storage_tier" : "maxiops"
+ },
+ {
+ "core_number" : 2,
+ "memory_amount" : 2048,
+ "name" : "2xCPU-2GB",
+ "public_traffic_out" : 3072,
+ "storage_size" : 50,
+ "storage_tier" : "maxiops"
+ },
+ {
+ "core_number" : 4,
+ "memory_amount" : 4096,
+ "name" : "4xCPU-4GB",
+ "public_traffic_out" : 4096,
+ "storage_size" : 100,
+ "storage_tier" : "maxiops"
+ },
+ {
+ "core_number" : 6,
+ "memory_amount" : 8192,
+ "name" : "6xCPU-8GB",
+ "public_traffic_out" : 8192,
+ "storage_size" : 200,
+ "storage_tier" : "maxiops"
+ }
+ ]
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_server.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_server.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_server.json
new file mode 100644
index 0000000..e2a7d0a
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_server.json
@@ -0,0 +1,22 @@
+{
+ "servers" : {
+ "server" : [
+ {
+ "core_number" : "1",
+ "hostname" : "localhost",
+ "license" : 0,
+ "memory_amount" : "1024",
+ "plan" : "1xCPU-1GB",
+ "plan_ipv4_bytes" : "12267",
+ "plan_ipv6_bytes" : "4644",
+ "state" : "started",
+ "tags" : {
+ "tag" : []
+ },
+ "title" : "test_server",
+ "uuid" : "00f8c525-7e62-4108-8115-3958df5b43dc",
+ "zone" : "fi-hel1"
+ }
+ ]
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00893c98-5d5a-4363-b177-88df518a2b60.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00893c98-5d5a-4363-b177-88df518a2b60.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00893c98-5d5a-4363-b177-88df518a2b60.json
new file mode 100644
index 0000000..c99fc05
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00893c98-5d5a-4363-b177-88df518a2b60.json
@@ -0,0 +1,58 @@
+{
+ "server" : {
+ "boot_order" : "cdrom,disk",
+ "core_number" : "1",
+ "firewall" : "off",
+ "host" : 4297867907,
+ "hostname" : "localhost",
+ "ip_addresses" : {
+ "ip_address" : [
+ {
+ "access" : "private",
+ "address" : "10.2.1.244",
+ "family" : "IPv4"
+ },
+ {
+ "access" : "public",
+ "address" : "2a04:3541:1000:500:7cae:1dff:fead:5bde",
+ "family" : "IPv6"
+ },
+ {
+ "access" : "public",
+ "address" : "83.136.254.34",
+ "family" : "IPv4",
+ "part_of_plan" : "yes"
+ }
+ ]
+ },
+ "license" : 0,
+ "memory_amount" : "1024",
+ "nic_model" : "virtio",
+ "plan" : "1xCPU-1GB",
+ "plan_ipv4_bytes" : "0",
+ "plan_ipv6_bytes" : "0",
+ "state" : "stopped",
+ "storage_devices" : {
+ "storage_device" : [
+ {
+ "address" : "virtio:0",
+ "part_of_plan" : "yes",
+ "storage" : "01839922-a675-4214-b10e-a4cf7953992f",
+ "storage_size" : 30,
+ "storage_title" : "localhost-disk0",
+ "type" : "disk"
+ }
+ ]
+ },
+ "tags" : {
+ "tag" : []
+ },
+ "timezone" : "UTC",
+ "title" : "test",
+ "uuid" : "00893c98-5d5a-4363-b177-88df518a2b60",
+ "video_model" : "cirrus",
+ "vnc" : "off",
+ "vnc_password" : "Hf7qpJs8",
+ "zone" : "uk-lon1"
+ }
+}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json
new file mode 100644
index 0000000..1278e6e
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json
@@ -0,0 +1,57 @@
+{
+ "server" : {
+ "boot_order" : "disk",
+ "core_number" : "1",
+ "firewall" : "off",
+ "host" : 4964762243,
+ "hostname" : "localhost",
+ "ip_addresses" : {
+ "ip_address" : [
+ {
+ "access" : "private",
+ "address" : "10.1.7.68",
+ "family" : "IPv4"
+ },
+ {
+ "access" : "public",
+ "address" : "2a04:3540:1000:310:7cae:1dff:fead:19dc",
+ "family" : "IPv6"
+ },
+ {
+ "access" : "public",
+ "address" : "94.237.37.249",
+ "family" : "IPv4",
+ "part_of_plan" : "yes"
+ }
+ ]
+ },
+ "license" : 0,
+ "memory_amount" : "1024",
+ "nic_model" : "virtio",
+ "plan" : "1xCPU-1GB",
+ "plan_ipv4_bytes" : "8242",
+ "plan_ipv6_bytes" : "3440",
+ "state" : "started",
+ "storage_devices" : {
+ "storage_device" : [
+ {
+ "address" : "virtio:0",
+ "storage" : "01e5411f-37c9-4b8e-b0bb-4cad5119b3ea",
+ "storage_size" : 10,
+ "storage_title" : "Ubuntu Server 16.04 LTS (Xenial Xerus)",
+ "type" : "disk"
+ }
+ ]
+ },
+ "tags" : {
+ "tag" : []
+ },
+ "timezone" : "UTC",
+ "title" : "test_server",
+ "uuid" : "00f8c525-7e62-4108-8115-3958df5b43dc",
+ "video_model" : "cirrus",
+ "vnc" : "off",
+ "vnc_password" : "r362XMmV",
+ "zone" : "fi-hel1"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc_restart.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc_restart.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc_restart.json
new file mode 100644
index 0000000..af904e4
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc_restart.json
@@ -0,0 +1,57 @@
+{
+ "server" : {
+ "boot_order" : "disk",
+ "core_number" : "1",
+ "firewall" : "off",
+ "host" : 4964762243,
+ "hostname" : "localhost",
+ "ip_addresses" : {
+ "ip_address" : [
+ {
+ "access" : "private",
+ "address" : "10.1.7.68",
+ "family" : "IPv4"
+ },
+ {
+ "access" : "public",
+ "address" : "2a04:3540:1000:310:7cae:1dff:fead:19dc",
+ "family" : "IPv6"
+ },
+ {
+ "access" : "public",
+ "address" : "94.237.37.249",
+ "family" : "IPv4",
+ "part_of_plan" : "yes"
+ }
+ ]
+ },
+ "license" : 0,
+ "memory_amount" : "1024",
+ "nic_model" : "virtio",
+ "plan" : "1xCPU-1GB",
+ "plan_ipv4_bytes" : "218066",
+ "plan_ipv6_bytes" : "6450",
+ "state" : "started",
+ "storage_devices" : {
+ "storage_device" : [
+ {
+ "address" : "virtio:0",
+ "storage" : "01e5411f-37c9-4b8e-b0bb-4cad5119b3ea",
+ "storage_size" : 10,
+ "storage_title" : "Ubuntu Server 16.04 LTS (Xenial Xerus)",
+ "type" : "disk"
+ }
+ ]
+ },
+ "tags" : {
+ "tag" : []
+ },
+ "timezone" : "UTC",
+ "title" : "test_server",
+ "uuid" : "00f8c525-7e62-4108-8115-3958df5b43dc",
+ "video_model" : "cirrus",
+ "vnc" : "off",
+ "vnc_password" : "r362XMmV",
+ "zone" : "fi-hel1"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_cdrom.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_cdrom.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_cdrom.json
new file mode 100644
index 0000000..b82f8c6
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_cdrom.json
@@ -0,0 +1,65 @@
+{
+ "server" : {
+ "boot_order" : "disk",
+ "core_number" : "1",
+ "firewall" : "off",
+ "hostname" : "localhost",
+ "ip_addresses" : {
+ "ip_address" : [
+ {
+ "access" : "private",
+ "address" : "10.1.2.26",
+ "family" : "IPv4"
+ },
+ {
+ "access" : "public",
+ "address" : "2a04:3540:1000:310:7cae:1dff:fead:5683",
+ "family" : "IPv6"
+ },
+ {
+ "access" : "public",
+ "address" : "94.237.37.215",
+ "family" : "IPv4",
+ "part_of_plan" : "yes"
+ }
+ ]
+ },
+ "license" : 0,
+ "memory_amount" : "1024",
+ "nic_model" : "virtio",
+ "plan" : "1xCPU-1GB",
+ "plan_ipv4_bytes" : "0",
+ "plan_ipv6_bytes" : "0",
+ "progress" : "0",
+ "state" : "maintenance",
+ "storage_devices" : {
+ "storage_device" : [
+ {
+ "address" : "virtio:0",
+ "part_of_plan" : "yes",
+ "storage" : "01898fe2-9909-471e-b33c-59d7896b48f5",
+ "storage_size" : 30,
+ "storage_title" : "Ubuntu Server 16.04 LTS (Xenial Xerus), 64-bit",
+ "type" : "disk"
+ },
+ {
+ "address" : "ide:0:0",
+ "storage" : "01000000-0000-4000-8000-000030040101",
+ "storage_size" : 1,
+ "storage_title" : "Ubuntu Server 14.04 LTS",
+ "type" : "cdrom"
+ }
+ ]
+ },
+ "tags" : {
+ "tag" : []
+ },
+ "timezone" : "UTC",
+ "title" : "test_server",
+ "uuid" : "00c68ee6-e6e1-4d5f-a213-4a1e063a3cbd",
+ "video_model" : "cirrus",
+ "vnc" : "off",
+ "vnc_password" : "5C4TVPf8",
+ "zone" : "fi-hel1"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_template.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_template.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_template.json
new file mode 100644
index 0000000..cde80cc
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_server_from_template.json
@@ -0,0 +1,59 @@
+{
+ "server" : {
+ "boot_order" : "disk",
+ "core_number" : "1",
+ "firewall" : "off",
+ "hostname" : "localhost",
+ "ip_addresses" : {
+ "ip_address" : [
+ {
+ "access" : "private",
+ "address" : "10.1.3.163",
+ "family" : "IPv4"
+ },
+ {
+ "access" : "public",
+ "address" : "2a04:3540:1000:310:7cae:1dff:fead:3332",
+ "family" : "IPv6"
+ },
+ {
+ "access" : "public",
+ "address" : "94.237.37.216",
+ "family" : "IPv4",
+ "part_of_plan" : "yes"
+ }
+ ]
+ },
+ "license" : 0,
+ "memory_amount" : "1024",
+ "nic_model" : "virtio",
+ "password" : "777gznbm",
+ "plan" : "1xCPU-1GB",
+ "plan_ipv4_bytes" : "0",
+ "plan_ipv6_bytes" : "0",
+ "progress" : "0",
+ "state" : "maintenance",
+ "storage_devices" : {
+ "storage_device" : [
+ {
+ "address" : "virtio:0",
+ "storage" : "018e2c82-1c16-46b9-8b7d-aeaf8d8309a9",
+ "storage_size" : 10,
+ "storage_title" : "Ubuntu Server 16.04 LTS (Xenial Xerus)",
+ "type" : "disk"
+ }
+ ]
+ },
+ "tags" : {
+ "tag" : []
+ },
+ "timezone" : "UTC",
+ "title" : "test_server",
+ "username" : "mlackman",
+ "uuid" : "00814aac-240f-4f08-9139-9697c9ffc0b7",
+ "video_model" : "cirrus",
+ "vnc" : "off",
+ "vnc_password" : "G4FvMjvg",
+ "zone" : "fi-hel1"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_cdrom.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_cdrom.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_cdrom.json
new file mode 100644
index 0000000..5f45a2b
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_cdrom.json
@@ -0,0 +1,411 @@
+{
+ "storages" : {
+ "storage" : [
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Standard (CD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Standard (CD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010010102"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Standard (CD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010010201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Standard (CD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010010202"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Enterprise (CD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010020101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Enterprise (CD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010020102"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Enterprise (CD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010020201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Enterprise (CD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010020202"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 3,
+ "state" : "online",
+ "title" : "Windows Server 2008 R2 Datacenter",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010030101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Datacenter",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010040101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Windows Server 2003 R2 Datacenter",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010040201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 4,
+ "state" : "online",
+ "title" : "Windows Server 2012",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000010050101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 5,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 6.0.1 (Squeeze) (DVD)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 6.0.1 (Squeeze) (netinst)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020010201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 6.0.1 (Squeeze) (netinst)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020010301"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 6.0.1 (Squeeze) (live)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020010401"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 6.0.1 (Squeeze) (live)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020010501"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 7.8 (Wheezy)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020020201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 8.6.0 (Jessie)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020030101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 9.0.0 (Stretch) Installation CD",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000020040101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 10.04",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 10.04",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030010201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 11.04",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030020101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 11.04",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030020201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 12.04",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030030101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 14.04 LTS",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030040101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Ubuntu Server 16.04 LTS (Xenial Xerus), 64-bit",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000030060101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 4,
+ "state" : "online",
+ "title" : "Fedora 16 (DVD)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000040010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Fedora 16 (live)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000040010201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Fedora 19",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000040020101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 4,
+ "state" : "online",
+ "title" : "CentOS 6.0 (DVD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "CentOS 6.0 (DVD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010102"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 4,
+ "state" : "online",
+ "title" : "CentOS 6.8 (DVD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010103"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 3,
+ "state" : "online",
+ "title" : "CentOS 6.8 (DVD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010104"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 4,
+ "state" : "online",
+ "title" : "CentOS 6.8 (DVD 1)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010105"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "CentOS 6.8 (DVD 2)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010106"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 5,
+ "state" : "online",
+ "title" : "CentOS 7.3-1611 DVD",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000050010301"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 4,
+ "state" : "online",
+ "title" : "Knoppix 6.4.4",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000060010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Arch Linux 2010.05",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000070010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "CoreOS 607.0.0",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000080010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "CoreOS 845.0.0",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000080010201"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "CoreOS Alpha (1032.1.0)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000080010501"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "CoreOS Stable (1068.8.0)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000080020101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "Gentoo Linux Minimal Installation CD (64bit)",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000090010101"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 1,
+ "state" : "online",
+ "title" : "FreeBSD 11.0-RELEASE amd64 Installation CD",
+ "type" : "cdrom",
+ "uuid" : "01000000-0000-4000-8000-000100010101"
+ }
+ ]
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_template.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_template.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_template.json
new file mode 100644
index 0000000..12f42c2
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_template.json
@@ -0,0 +1,114 @@
+{
+ "storages" : {
+ "storage" : [
+ {
+ "access" : "public",
+ "license" : 3.36,
+ "size" : 20,
+ "state" : "online",
+ "title" : "Windows Server 2012 R2 Datacenter",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000010050200"
+ },
+ {
+ "access" : "public",
+ "license" : 0.694,
+ "size" : 20,
+ "state" : "online",
+ "title" : "Windows Server 2012 R2 Standard",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000010050300"
+ },
+ {
+ "access" : "public",
+ "license" : 3.36,
+ "size" : 20,
+ "state" : "online",
+ "title" : "Windows Server 2016 Datacenter",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000010060200"
+ },
+ {
+ "access" : "public",
+ "license" : 0.694,
+ "size" : 20,
+ "state" : "online",
+ "title" : "Windows Server 2016 Standard",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000010060300"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 7.8 (Wheezy)",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000020020100"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Debian GNU/Linux 8.7 (Jessie)",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000020030100"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Ubuntu Server 12.04 LTS (Precise Pangolin)",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000030030200"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Ubuntu Server 14.04 LTS (Trusty Tahr)",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000030040200"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "Ubuntu Server 16.04 LTS (Xenial Xerus)",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000030060200"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "CentOS 6.9",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000050010200"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 2,
+ "state" : "online",
+ "title" : "CentOS 7.0",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000050010300"
+ },
+ {
+ "access" : "public",
+ "license" : 0,
+ "size" : 5,
+ "state" : "online",
+ "title" : "CoreOS Stable 1068.8.0",
+ "type" : "template",
+ "uuid" : "01000000-0000-4000-8000-000080010200"
+ }
+ ]
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_zone.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_zone.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_zone.json
new file mode 100644
index 0000000..19deb8d
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_zone.json
@@ -0,0 +1,30 @@
+{
+ "zones" : {
+ "zone" : [
+ {
+ "description" : "Frankfurt #1",
+ "id" : "de-fra1"
+ },
+ {
+ "description" : "Helsinki #1",
+ "id" : "fi-hel1"
+ },
+ {
+ "description" : "Amsterdam #1",
+ "id" : "nl-ams1"
+ },
+ {
+ "description" : "Singapore #1",
+ "id" : "sg-sin1"
+ },
+ {
+ "description" : "London #1",
+ "id" : "uk-lon1"
+ },
+ {
+ "description" : "Chicago #1",
+ "id" : "us-chi1"
+ }
+ ]
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/fixtures/upcloud/api_1_2_zone_failed_auth.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_zone_failed_auth.json b/libcloud/test/compute/fixtures/upcloud/api_1_2_zone_failed_auth.json
new file mode 100644
index 0000000..356841c
--- /dev/null
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_zone_failed_auth.json
@@ -0,0 +1,6 @@
+{
+ "error" : {
+ "error_code" : "AUTHENTICATION_FAILED",
+ "error_message" : "Authentication failed using the given username and password."
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/compute/test_upcloud.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_upcloud.py b/libcloud/test/compute/test_upcloud.py
new file mode 100644
index 0000000..41d02a0
--- /dev/null
+++ b/libcloud/test/compute/test_upcloud.py
@@ -0,0 +1,248 @@
+# 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 __future__ import with_statement
+import sys
+import re
+import json
+import base64
+
+from libcloud.utils.py3 import httplib, ensure_string
+from libcloud.compute.drivers.upcloud import UpcloudDriver
+from libcloud.common.types import InvalidCredsError
+from libcloud.compute.drivers.upcloud import UpcloudResponse
+from libcloud.compute.types import NodeState
+from libcloud.compute.base import NodeImage, NodeSize, NodeLocation, NodeAuthSSHKey, Node
+from libcloud.test import LibcloudTestCase, unittest, MockHttp
+from libcloud.test.file_fixtures import ComputeFileFixtures
+from libcloud.test.secrets import UPCLOUD_PARAMS
+
+
+class UpcloudPersistResponse(UpcloudResponse):
+
+ def parse_body(self):
+ import os
+ path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir, 'compute', 'fixtures', 'upcloud'))
+ filename = 'api' + self.request.path_url.replace('/', '_').replace('.', '_') + '.json'
+ filename = os.path.join(path, filename)
+ if not os.path.exists(filename):
+ with open(filename, 'w+') as f:
+ f.write(self.body)
+ return super(UpcloudPersistResponse, self).parse_body()
+
+
+class UpcloudAuthenticationTests(LibcloudTestCase):
+
+ def setUp(self):
+ UpcloudDriver.connectionCls.conn_class = UpcloudMockHttp
+ self.driver = UpcloudDriver("nosuchuser", "nopwd")
+
+ def test_authentication_fails(self):
+ with self.assertRaises(InvalidCredsError):
+ self.driver.list_locations()
+
+
+class UpcloudDriverTests(LibcloudTestCase):
+
+ def setUp(self):
+ UpcloudDriver.connectionCls.conn_class = UpcloudMockHttp
+ # UpcloudDriver.connectionCls.responseCls = UpcloudPersistResponse
+ self.driver = UpcloudDriver(*UPCLOUD_PARAMS)
+
+ def test_features(self):
+ features = self.driver.features['create_node']
+ self.assertIn('ssh_key', features)
+ self.assertIn('generates_password', features)
+
+ def test_list_locations(self):
+ locations = self.driver.list_locations()
+ self.assertTrue(len(locations) >= 1)
+ expected_node_location = NodeLocation(id='fi-hel1',
+ name='Helsinki #1',
+ country='FI',
+ driver=self.driver)
+ self.assert_object(expected_node_location, objects=locations)
+
+ def test_list_sizes(self):
+ sizes = self.driver.list_sizes()
+ self.assertTrue(len(sizes) >= 1)
+ expected_node_size = NodeSize(id='1xCPU-1GB',
+ name='1xCPU-1GB',
+ ram=1024,
+ disk=30,
+ bandwidth=2048,
+ price=None,
+ driver=self.driver,
+ extra={'core_number': 1,
+ 'storage_tier': 'maxiops'})
+ self.assert_object(expected_node_size, objects=sizes)
+
+ def test_list_images(self):
+ images = self.driver.list_images()
+ self.assertTrue(len(images) >= 1)
+ expected_node_image = NodeImage(id='01000000-0000-4000-8000-000010010101',
+ name='Windows Server 2003 R2 Standard (CD 1)',
+ driver=self.driver,
+ extra={'access': 'public',
+ 'licence': 0,
+ 'size': 1,
+ 'state': 'online',
+ 'type': 'cdrom'})
+ self.assert_object(expected_node_image, objects=images)
+
+ def test_create_node_from_template(self):
+ image = NodeImage(id='01000000-0000-4000-8000-000030060200',
+ name='Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ extra={'type': 'template'},
+ driver=self.driver)
+ location = NodeLocation(id='fi-hel1', name='Helsinki #1', country='FI', driver=self.driver)
+ size = NodeSize(id='1xCPU-1GB', name='1xCPU-1GB', ram=1024, disk=30, bandwidth=2048,
+ extra={'core_number': 1, 'storage_tier': 'maxiops'}, price=None, driver=self.driver)
+ node = self.driver.create_node(name='test_server', size=size, image=image, location=location)
+
+ self.assertTrue(re.match('^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$', node.id))
+ self.assertEquals(node.name, 'test_server')
+ self.assertEquals(node.state, NodeState.STARTING)
+ self.assertTrue(len(node.public_ips) > 0)
+ self.assertTrue(len(node.private_ips) > 0)
+ self.assertEquals(node.driver, self.driver)
+ self.assertTrue(len(node.extra['password']) > 0)
+ self.assertTrue(len(node.extra['vnc_password']) > 0)
+
+ def test_create_node_with_ssh_keys(self):
+ image = NodeImage(id='01000000-0000-4000-8000-000030060200',
+ name='Ubuntu Server 16.04 LTS (Xenial Xerus)',
+ extra={'type': 'template'},
+ driver=self.driver)
+ location = NodeLocation(id='fi-hel1', name='Helsinki #1', country='FI', driver=self.driver)
+ size = NodeSize(id='1xCPU-1GB', name='1xCPU-1GB', ram=1024, disk=30, bandwidth=2048,
+ extra={'core_number': 1, 'storage_tier': 'maxiops'}, price=None, driver=self.driver)
+
+ auth = NodeAuthSSHKey('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCUUFfYA+T+BzoM7IIR' +
+ 'VXNndDjYvIROMjfyRBhhHf6RZd1IkAwcWSGISePh2tIiqu8gJalYYHg2w' +
+ 'i3ofMJfi6VYeyBFWrIDhMK0v+ziBbBUtlJNnP6MBOR/13avkk+76TVrcG' +
+ 'xu49RaptYNzZ21XluvIlaqqdjAhoh0J+o7OZTKD7N1UTPL7CIX+ITaA+g' +
+ '3FR5ITClk8KmIbp3vT6fUPD7pNUrGBZTpcPcHq8rodQ8igWIVdSkb9iky' +
+ 'ew4y6wvsubQ3Ykn26XeKxrk1vA6ZKMHt7ijCYmfL0LcDfctNymy/vc6hs' +
+ 'WxCRS5OqNQ6nxdXpv9A+TD0sJuf5jaoH7MSpU1 mika.lackman@gmail.com')
+ node = self.driver.create_node(name='test_server', size=size, image=image, location=location, auth=auth)
+ self.assertTrue(re.match('^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$', node.id))
+ self.assertEquals(node.name, 'test_server')
+ self.assertEquals(node.state, NodeState.STARTING)
+ self.assertTrue(len(node.public_ips) > 0)
+ self.assertTrue(len(node.private_ips) > 0)
+ self.assertEquals(node.driver, self.driver)
+
+ def test_list_nodes(self):
+ nodes = self.driver.list_nodes()
+
+ self.assertTrue(len(nodes) >= 1)
+ node = nodes[0]
+ self.assertEquals(node.name, 'test_server')
+ self.assertEquals(node.state, NodeState.RUNNING)
+ self.assertTrue(len(node.public_ips) > 0)
+ self.assertTrue(len(node.private_ips) > 0)
+ self.assertEquals(node.driver, self.driver)
+
+ def test_reboot_node(self):
+ nodes = self.driver.list_nodes()
+ success = self.driver.reboot_node(nodes[0])
+ self.assertTrue(success)
+
+ def test_destroy_node(self):
+ if UpcloudDriver.connectionCls.conn_class == UpcloudMockHttp:
+ nodes = [Node(id='00893c98_5d5a_4363_b177_88df518a2b60', name='', state='',
+ public_ips=[], private_ips=[], driver=self.driver)]
+ else:
+ nodes = self.driver.list_nodes()
+ success = self.driver.destroy_node(nodes[0])
+ self.assertTrue(success)
+
+ def assert_object(self, expected_object, objects):
+ same_data = any([self.objects_equals(expected_object, obj) for obj in objects])
+ self.assertTrue(same_data, "Objects does not match")
+
+ def objects_equals(self, expected_obj, obj):
+ for name in vars(expected_obj):
+ expected_data = getattr(expected_obj, name)
+ actual_data = getattr(obj, name)
+ same_data = self.data_equals(expected_data, actual_data)
+ if not same_data:
+ break
+ return same_data
+
+ def data_equals(self, expected_data, actual_data):
+ if isinstance(expected_data, dict):
+ return self.dicts_equals(expected_data, actual_data)
+ else:
+ return expected_data == actual_data
+
+ def dicts_equals(self, d1, d2):
+ """Assumes dicts to contain only hashable types"""
+ return set(d1.values()) == set(d2.values())
+
+
+class UpcloudMockHttp(MockHttp):
+ fixtures = ComputeFileFixtures('upcloud')
+
+ def _1_2_zone(self, method, url, body, headers):
+ auth = headers['Authorization'].split(' ')[1]
+ username, password = ensure_string(base64.b64decode(auth)).split(':')
+ if username == 'nosuchuser' and password == 'nopwd':
+ body = self.fixtures.load('api_1_2_zone_failed_auth.json')
+ status_code = httplib.UNAUTHORIZED
+ else:
+ body = self.fixtures.load('api_1_2_zone.json')
+ status_code = httplib.OK
+ return (status_code, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_plan(self, method, url, body, headers):
+ body = self.fixtures.load('api_1_2_plan.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_storage_cdrom(self, method, url, body, headers):
+ body = self.fixtures.load('api_1_2_storage_cdrom.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_storage_template(self, method, url, body, headers):
+ body = self.fixtures.load('api_1_2_storage_template.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_server(self, method, url, body, headers):
+ if method == 'POST':
+ dbody = json.loads(body)
+ storages = dbody['server']['storage_devices']['storage_device']
+ if any(['type' in storage and storage['type'] == 'cdrom' for storage in storages]):
+ body = self.fixtures.load('api_1_2_server_from_cdrom.json')
+ else:
+ body = self.fixtures.load('api_1_2_server_from_template.json')
+ else:
+ body = self.fixtures.load('api_1_2_server.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_server_00f8c525_7e62_4108_8115_3958df5b43dc(self, method, url, body, headers):
+ body = self.fixtures.load('api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_server_00f8c525_7e62_4108_8115_3958df5b43dc_restart(self, method, url, body, headers):
+ body = self.fixtures.load('api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc_restart.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _1_2_server_00893c98_5d5a_4363_b177_88df518a2b60(self, method, url, body, headers):
+ body = self.fixtures.load('api_1_2_server_00893c98-5d5a-4363-b177-88df518a2b60.json')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+if __name__ == '__main__':
+ sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/abbd9bb6/libcloud/test/secrets.py-dist
----------------------------------------------------------------------
diff --git a/libcloud/test/secrets.py-dist b/libcloud/test/secrets.py-dist
index 1b30bf2..8eaa0c6 100644
--- a/libcloud/test/secrets.py-dist
+++ b/libcloud/test/secrets.py-dist
@@ -57,6 +57,7 @@ VULTR_PARAMS = ('key')
PACKET_PARAMS = ('api_key')
ECS_PARAMS = ('access_key', 'access_secret')
CLOUDSCALE_PARAMS = ('token',)
+UPCLOUD_PARAMS = ('user', 'secret')
# Storage
STORAGE_S3_PARAMS = ('key', 'secret')