You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2014/09/14 13:25:12 UTC
[2/3] git commit: [Libcloud-589] Add a new compute driver for
ProfitBricks provider.
[Libcloud-589] Add a new compute driver for ProfitBricks provider.
Closes #352
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/34cff301
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/34cff301
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/34cff301
Branch: refs/heads/trunk
Commit: 34cff3012c097b1f37b821bea60eb2c761d4fe15
Parents: 6869ebc
Author: Matt Baldwin <ba...@gmail.com>
Authored: Tue Aug 26 13:59:37 2014 -0700
Committer: Tomaz Muraus <to...@apache.org>
Committed: Sun Sep 14 13:20:13 2014 +0200
----------------------------------------------------------------------
CHANGES.rst | 4 +
libcloud/compute/drivers/profitbricks.py | 1485 ++++++++++++++++++
libcloud/compute/providers.py | 2 +
libcloud/compute/types.py | 2 +
.../fixtures/profitbricks/attach_volume.xml | 12 +
.../fixtures/profitbricks/create_node.xml | 13 +
.../fixtures/profitbricks/create_volume.xml | 13 +
.../fixtures/profitbricks/destroy_node.xml | 12 +
.../fixtures/profitbricks/destroy_volume.xml | 12 +
.../fixtures/profitbricks/detach_volume.xml | 12 +
.../profitbricks/ex_clear_datacenter.xml | 12 +
.../profitbricks/ex_create_datacenter.xml | 13 +
.../ex_create_network_interface.xml | 13 +
.../profitbricks/ex_describe_datacenter.xml | 15 +
.../ex_describe_network_interface.xml | 26 +
.../fixtures/profitbricks/ex_describe_node.xml | 77 +
.../profitbricks/ex_describe_volume.xml | 22 +
.../profitbricks/ex_destroy_datacenter.xml | 10 +
.../ex_destroy_network_interface.xml | 12 +
.../profitbricks/ex_list_datacenters.xml | 19 +
.../profitbricks/ex_list_network_interfaces.xml | 75 +
.../fixtures/profitbricks/ex_start_node.xml | 10 +
.../fixtures/profitbricks/ex_stop_node.xml | 10 +
.../profitbricks/ex_update_datacenter.xml | 12 +
.../ex_update_network_interface.xml | 12 +
.../fixtures/profitbricks/ex_update_node.xml | 12 +
.../fixtures/profitbricks/ex_update_volume.xml | 12 +
.../fixtures/profitbricks/list_images.xml | 43 +
.../fixtures/profitbricks/list_nodes.xml | 172 ++
.../fixtures/profitbricks/list_volumes.xml | 66 +
.../fixtures/profitbricks/reboot_node.xml | 10 +
libcloud/test/compute/test_profitbricks.py | 509 ++++++
libcloud/test/secrets.py-dist | 1 +
33 files changed, 2730 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 1495ea7..e9ac0fd 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -98,6 +98,10 @@ Compute
Reported by Xavier Barbosa.
[Tomaz Muraus, Xavier Barbosa]
+- Add new driver for ProfitBricks provider.
+ (LIBCLOUD-589, GITHUB-352)
+ [Matt Baldwin]
+
Storage
~~~~~~~
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/compute/drivers/profitbricks.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/profitbricks.py b/libcloud/compute/drivers/profitbricks.py
new file mode 100644
index 0000000..0d4a5ea
--- /dev/null
+++ b/libcloud/compute/drivers/profitbricks.py
@@ -0,0 +1,1485 @@
+# 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.
+"""ProfitBricks Compute driver
+"""
+import base64
+
+import copy
+import time
+
+try:
+ from lxml import etree as ET
+except ImportError:
+ from xml.etree import ElementTree as ET
+
+from libcloud.utils.networking import is_private_subnet
+from libcloud.utils.py3 import b
+from libcloud.compute.providers import Provider
+from libcloud.common.base import ConnectionUserAndKey, XmlResponse
+from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize
+from libcloud.compute.base import NodeImage, StorageVolume
+from libcloud.compute.base import UuidMixin
+from libcloud.compute.types import NodeState
+from libcloud.common.types import LibcloudError, MalformedResponseError
+
+__all__ = [
+ 'API_VERSION',
+ 'API_HOST',
+ 'ProfitBricksNodeDriver',
+ 'Datacenter',
+ 'ProfitBricksNetworkInterface',
+ 'ProfitBricksAvailabilityZone'
+]
+
+API_HOST = 'api.profitbricks.com'
+API_VERSION = '/1.3/'
+
+
+class ProfitBricksResponse(XmlResponse):
+ """
+ ProfitBricks response parsing.
+ """
+ def parse_error(self):
+ try:
+ body = ET.XML(self.body)
+ except:
+ raise MalformedResponseError('Failed to parse XML',
+ body=self.body,
+ driver=ProfitBricksNodeDriver)
+
+ for e in body.findall('.//detail'):
+ if ET.iselement(e[0].find('httpCode')):
+ http_code = e[0].find('httpCode').text
+ else:
+ http_code = None
+ if ET.iselement(e[0].find('faultCode')):
+ fault_code = e[0].find('faultCode').text
+ else:
+ fault_code = None
+ if ET.iselement(e[0].find('message')):
+ message = e[0].find('message').text
+ else:
+ message = None
+
+ return LibcloudError('HTTP Code: %s, Fault Code: %s, Message: %s' %
+ (http_code, fault_code, message), driver=self)
+
+
+class ProfitBricksConnection(ConnectionUserAndKey):
+ """
+ Represents a single connection to the ProfitBricks endpoint.
+ """
+ host = API_HOST
+ api_prefix = API_VERSION
+ responseCls = ProfitBricksResponse
+
+ def add_default_headers(self, headers):
+ headers['Content-Type'] = 'text/xml'
+ headers['Authorization'] = 'Basic %s' % (base64.b64encode(
+ b('%s:%s' % (self.user_id, self.key))).decode('utf-8'))
+
+ return headers
+
+ def encode_data(self, data):
+ soap_env = ET.Element('soapenv:Envelope', {
+ 'xmlns:soapenv': 'http://schemas.xmlsoap.org/soap/envelope/',
+ 'xmlns:ws': 'http://ws.api.profitbricks.com/'
+ })
+ ET.SubElement(soap_env, 'soapenv:Header')
+ soap_body = ET.SubElement(soap_env, 'soapenv:Body')
+ soap_req_body = ET.SubElement(soap_body, 'ws:%s' % (data['action']))
+
+ if 'request' in data.keys():
+ soap_req_body = ET.SubElement(soap_req_body, 'request')
+ for key, value in data.items():
+ if key not in ['action', 'request']:
+ child = ET.SubElement(soap_req_body, key)
+ child.text = value
+ else:
+ for key, value in data.items():
+ if key != 'action':
+ child = ET.SubElement(soap_req_body, key)
+ child.text = value
+
+ soap_post = ET.tostring(soap_env)
+
+ return soap_post
+
+ def request(self, action, params=None, data=None, headers=None,
+ method='POST', raw=False):
+ action = self.api_prefix + action
+
+ return super(ProfitBricksConnection, self).request(action=action,
+ params=params,
+ data=data,
+ headers=headers,
+ method=method,
+ raw=raw)
+
+
+class Datacenter(UuidMixin):
+ """
+ Class which stores information about ProfitBricks datacenter
+ instances.
+
+ :param id: The datacenter ID.
+ :type id: ``str``
+
+ :param name: The datacenter name.
+ :type name: ``str``
+
+ :param version: Datacenter version.
+ :type version: ``str``
+
+
+ Note: This class is ProfitBricks specific.
+ """
+ def __init__(self, id, name, version, driver, extra=None):
+ self.id = str(id)
+ self.name = name
+ self.version = version
+ self.driver = driver
+ self.extra = extra or {}
+ UuidMixin.__init__(self)
+
+ def __repr__(self):
+ return ((
+ '<Datacenter: id=%s, name=%s, version=%s, driver=%s> ...>')
+ % (self.id, self.name, self.version,
+ self.driver.name))
+
+
+class ProfitBricksNetworkInterface(object):
+ """
+ Class which stores information about ProfitBricks network
+ interfaces.
+
+ :param id: The network interface ID.
+ :type id: ``str``
+
+ :param name: The network interface name.
+ :type name: ``str``
+
+ :param state: The network interface name.
+ :type state: ``int``
+
+ Note: This class is ProfitBricks specific.
+ """
+ def __init__(self, id, name, state, extra=None):
+ self.id = id
+ self.name = name
+ self.state = state
+ self.extra = extra or {}
+
+ def __repr__(self):
+ return (('<ProfitBricksNetworkInterface: id=%s, name=%s>')
+ % (self.id, self.name))
+
+
+class ProfitBricksAvailabilityZone(object):
+ """
+ Extension class which stores information about a ProfitBricks
+ availability zone.
+
+ Note: This class is ProfitBricks specific.
+ """
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return (('<ProfitBricksAvailabilityZone: name=%s>')
+ % (self.name))
+
+
+class ProfitBricksNodeDriver(NodeDriver):
+ """
+ Base ProfitBricks node driver.
+ """
+ connectionCls = ProfitBricksConnection
+ name = 'ProfitBricks'
+ website = 'http://www.profitbricks.com'
+ type = Provider.PROFIT_BRICKS
+
+ PROVISIONING_STATE = {
+ 'INACTIVE': NodeState.PENDING,
+ 'INPROCESS': NodeState.PENDING,
+ 'AVAILABLE': NodeState.RUNNING,
+ 'DELETED': NodeState.TERMINATED,
+ }
+
+ NODE_STATE_MAP = {
+ 'NOSTATE': NodeState.UNKNOWN,
+ 'RUNNING': NodeState.RUNNING,
+ 'BLOCKED': NodeState.STOPPED,
+ 'PAUSE': NodeState.STOPPED,
+ 'SHUTDOWN': NodeState.PENDING,
+ 'SHUTOFF': NodeState.STOPPED,
+ 'CRASHED': NodeState.STOPPED,
+ }
+
+ REGIONS = {
+ '1': {'region': 'us/las', 'country': 'USA'},
+ '2': {'region': 'de/fra', 'country': 'DEU'},
+ '3': {'region': 'de/fkb', 'country': 'DEU'},
+ }
+
+ AVAILABILITY_ZONE = {
+ '1': {'name': 'AUTO'},
+ '2': {'name': 'ZONE_1'},
+ '3': {'name': 'ZONE_2'},
+ }
+
+ """
+ ProfitBricks is unique in that they allow the user to define all aspects
+ of the instance size, i.e. disk size, core size, and memory size.
+
+ These are instance types that match up with what other providers support.
+
+ You can configure disk size, core size, and memory size using the ex_
+ parameters on the create_node method.
+ """
+
+ PROFIT_BRICKS_GENERIC_SIZES = {
+ '1': {
+ 'id': '1',
+ 'name': 'Micro',
+ 'ram': 1024,
+ 'disk': 50,
+ 'cores': 1
+ },
+ '2': {
+ 'id': '2',
+ 'name': 'Small Instance',
+ 'ram': 2048,
+ 'disk': 50,
+ 'cores': 1
+ },
+ '3': {
+ 'id': '3',
+ 'name': 'Medium Instance',
+ 'ram': 4096,
+ 'disk': 50,
+ 'cores': 2
+ },
+ '4': {
+ 'id': '4',
+ 'name': 'Large Instance',
+ 'ram': 7168,
+ 'disk': 50,
+ 'cores': 4
+ },
+ '5': {
+ 'id': '5',
+ 'name': 'ExtraLarge Instance',
+ 'ram': 14336,
+ 'disk': 50,
+ 'cores': 8
+ },
+ '6': {
+ 'id': '6',
+ 'name': 'Memory Intensive Instance Medium',
+ 'ram': 28672,
+ 'disk': 50,
+ 'cores': 4
+ },
+ '7': {
+ 'id': '7',
+ 'name': 'Memory Intensive Instance Large',
+ 'ram': 57344,
+ 'disk': 50,
+ 'cores': 8
+ }
+ }
+
+ """
+ Core Functions
+ """
+
+ def list_sizes(self):
+ """
+ Lists all sizes
+
+ :rtype: ``list`` of :class:`NodeSize`
+ """
+ sizes = []
+
+ for key, values in self.PROFIT_BRICKS_GENERIC_SIZES.items():
+ node_size = self._to_node_size(values)
+ sizes.append(node_size)
+
+ return sizes
+
+ def list_images(self):
+ """
+ List all images.
+
+ :rtype: ``list`` of :class:`NodeImage`
+ """
+
+ action = 'getAllImages'
+ body = {'action': action}
+
+ return self._to_images(self.connection.request(action=action,
+ data=body, method='POST').object)
+
+ def list_locations(self):
+ """
+ List all locations.
+ """
+ locations = []
+
+ for key, values in self.REGIONS.items():
+ location = self._to_location(values)
+ locations.append(location)
+
+ return locations
+
+ def list_nodes(self):
+ """
+ List all nodes.
+
+ :rtype: ``list`` of :class:`Node`
+ """
+ action = 'getAllServers'
+ body = {'action': action}
+
+ return self._to_nodes(self.connection.request(action=action,
+ data=body, method='POST').object)
+
+ def reboot_node(self, node):
+ """
+ Reboots the node.
+
+ :rtype: ``bool``
+ """
+ action = 'resetServer'
+ body = {'action': action,
+ 'serverId': node.id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def create_node(self, name, image, size=None, volume=None,
+ ex_datacenter=None, ex_internet_access=True,
+ ex_availability_zone=None, ex_ram=None,
+ ex_cores=None, ex_disk=None, **kwargs):
+ """
+ Creates a node.
+
+ image is optional as long as you pass ram, cores, and disk
+ to the method. ProfitBricks allows you to adjust compute
+ resources at a much more granular level.
+
+ :param volume: If the volume already exists then pass this in.
+ :type volume: :class:`StorageVolume`
+
+ :param ex_datacenter: If you've already created the DC then pass
+ it in.
+ :type ex_datacenter: :class:`Datacenter`
+
+ :param ex_internet_access: Configure public Internet access.
+ :type ex_internet_access: : ``bool``
+
+ :param ex_availability_zone: The availability zone.
+ :type ex_availability_zone: class: `ProfitBricksAvailabilityZone`
+
+ :param ex_ram: The amount of ram required.
+ :type ex_ram: : ``int``
+
+ :param ex_cores: The number of cores required.
+ :type ex_cores: : ``int``
+
+ :param ex_disk: The amount of disk required.
+ :type ex_disk: : ``int``
+
+ :return: Instance of class ``Node``
+ :rtype: :class:`Node`
+ """
+ if not ex_datacenter:
+ '''
+ We generate a name from the server name passed into the function.
+ '''
+
+ 'Creating a Datacenter for the node since one was not provided.'
+ new_datacenter = self._create_new_datacenter_for_node(name=name)
+ datacenter_id = new_datacenter.id
+
+ 'Waiting for the Datacenter create operation to finish.'
+ self._wait_for_datacenter_state(datacenter=new_datacenter)
+ else:
+ datacenter_id = ex_datacenter.id
+ new_datacenter = None
+
+ if not size:
+ if not ex_ram:
+ raise ValueError('You need to either pass a '
+ 'NodeSize or specify ex_ram as '
+ 'an extra parameter.')
+ if not ex_cores:
+ raise ValueError('You need to either pass a '
+ 'NodeSize or specify ex_cores as '
+ 'an extra parameter.')
+
+ if not volume:
+ if not size:
+ if not ex_disk:
+ raise ValueError('You need to either pass a '
+ 'StorageVolume, a NodeSize, or specify '
+ 'ex_disk as an extra parameter.')
+
+ '''
+ You can override the suggested sizes by passing in unique
+ values for ram, cores, and disk allowing you to size it
+ for your specific use.
+ '''
+
+ if not ex_disk:
+ ex_disk = size.disk
+
+ if not ex_ram:
+ ex_ram = size.ram
+
+ if not ex_cores:
+ ex_cores = size.extra['cores']
+
+ '''
+ A pasword is automatically generated if it is
+ not provided. This is then sent via email to
+ the admin contact on record.
+ '''
+
+ if 'auth' in kwargs:
+ auth = self._get_and_check_auth(kwargs["auth"])
+ password = auth.password
+ else:
+ password = None
+
+ '''
+ Create a StorageVolume that can be attached to the
+ server when it is created.
+ '''
+ if not volume:
+ volume = self._create_node_volume(ex_disk=ex_disk,
+ image=image,
+ password=password,
+ name=name,
+ ex_datacenter=ex_datacenter,
+ new_datacenter=new_datacenter)
+
+ storage_id = volume.id
+
+ 'Waiting on the storage volume to be created before provisioning '
+ 'the instance.'
+ self._wait_for_storage_volume_state(volume)
+ else:
+ if ex_datacenter:
+ datacenter_id = ex_datacenter.id
+ else:
+ datacenter_id = volume.extra['datacenter_id']
+
+ storage_id = volume.id
+
+ action = 'createServer'
+ body = {'action': action,
+ 'request': 'true',
+ 'serverName': name,
+ 'cores': str(ex_cores),
+ 'ram': str(ex_ram),
+ 'bootFromStorageId': storage_id,
+ 'internetAccess': str(ex_internet_access).lower(),
+ 'dataCenterId': datacenter_id
+ }
+
+ if ex_availability_zone:
+ body['availabilityZone'] = ex_availability_zone.name
+
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ nodes = self._to_nodes(data)
+ return nodes[0]
+
+ def destroy_node(self, node, ex_remove_attached_disks=False):
+ """
+ Destroys a node.
+
+ :param node: The node you wish to destroy.
+ :type volume: :class:`Node`
+
+ :param ex_remove_attached_disks: True to destory all attached volumes.
+ :type ex_remove_attached_disks: : ``bool``
+
+ :rtype: : ``bool``
+ """
+ action = 'deleteServer'
+ body = {'action': action,
+ 'serverId': node.id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ """
+ Volume Functions
+ """
+
+ def list_volumes(self):
+ """
+ Lists all voumes.
+ """
+ action = 'getAllStorages'
+ body = {'action': action}
+
+ return self._to_volumes(self.connection.request(action=action,
+ data=body,
+ method='POST').object)
+
+ def attach_volume(self, node, volume, device=None, ex_bus_type=None):
+ """
+ Attaches a volume.
+
+ :param volume: The volume you're attaching.
+ :type volume: :class:`StorageVolume`
+
+ :param node: The node to which you're attaching the volume.
+ :type node: :class:`Node`
+
+ :param device: The device number order.
+ :type device: : ``int``
+
+ :param ex_bus_type: Bus type. Either IDE or VIRTIO (default).
+ :type ex_bus_type: ``str``
+
+ :return: Instance of class ``StorageVolume``
+ :rtype: :class:`StorageVolume`
+ """
+ action = 'connectStorageToServer'
+ body = {'action': action,
+ 'request': 'true',
+ 'storageId': volume.id,
+ 'serverId': node.id,
+ 'busType': ex_bus_type,
+ 'deviceNumber': str(device)
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+ return volume
+
+ def create_volume(self, size, name=None,
+ ex_datacenter=None, ex_image=None, ex_password=None):
+ """
+ Creates a volume.
+
+ :param ex_datacenter: The datacenter you're placing
+ the storage in. (req)
+ :type ex_datacenter: :class:`Datacenter`
+
+ :param ex_image: The OS image for the volume.
+ :type ex_image: :class:`NodeImage`
+
+ :param ex_password: Optional password for root.
+ :type ex_password: : ``str``
+
+ :return: Instance of class ``StorageVolume``
+ :rtype: :class:`StorageVolume`
+ """
+ action = 'createStorage'
+ body = {'action': action,
+ 'request': 'true',
+ 'size': str(size),
+ 'storageName': name,
+ 'mountImageId': ex_image.id
+ }
+
+ if ex_datacenter:
+ body['dataCenterId'] = ex_datacenter.id
+
+ if ex_password:
+ body['profitBricksImagePassword'] = ex_password
+
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ volumes = self._to_volumes(data)
+ return volumes[0]
+
+ def detach_volume(self, volume):
+ """
+ Detaches a volume.
+
+ :param volume: The volume you're detaching.
+ :type volume: :class:`StorageVolume`
+
+ :rtype: :``bool``
+ """
+ node_id = volume.extra['server_id']
+
+ action = 'disconnectStorageFromServer'
+ body = {'action': action,
+ 'storageId': volume.id,
+ 'serverId': node_id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def destroy_volume(self, volume):
+ """
+ Destroys a volume.
+
+ :param volume: The volume you're attaching.
+ :type volume: :class:`StorageVolume`
+
+ :rtype: : ``bool``
+ """
+ action = 'deleteStorage'
+ body = {'action': action,
+ 'storageId': volume.id}
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_update_volume(self, volume, storage_name=None, size=None):
+ """
+ Updates a volume.
+
+ :param volume: The volume you're attaching..
+ :type volume: :class:`StorageVolume`
+
+ :param storage_name: The name of the volume.
+ :type storage_name: : ``str``
+
+ :param size: The desired size.
+ :type size: ``int``
+
+ :rtype: : ``bool``
+ """
+ action = 'updateStorage'
+ body = {'action': action,
+ 'request': 'true',
+ 'storageId': volume.id
+ }
+
+ if storage_name:
+ body['storageName'] = storage_name
+ if size:
+ body['size'] = str(size)
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_describe_volume(self, volume_id):
+ """
+ Describes a volume.
+
+ :param volume_id: The ID of the volume you're describing.
+ :type volume_id: :class:`StorageVolume`
+
+ :return: Instance of class ``StorageVolume``
+ :rtype: :class:`StorageVolume`
+ """
+ action = 'getStorage'
+ body = {'action': action,
+ 'storageId': volume_id
+ }
+
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ volumes = self._to_volumes(data)
+ return volumes[0]
+
+ """
+ Extension Functions
+ """
+
+ ''' Server Extension Functions
+ '''
+ def ex_stop_node(self, node):
+ """
+ Stops a node.
+
+ This also dealloctes the public IP space.
+
+ :param node: The node you wish to halt.
+ :type node: :class:`Node`
+
+ :rtype: : ``bool``
+ """
+ action = 'stopServer'
+ body = {'action': action,
+ 'serverId': node.id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_start_node(self, node):
+ """
+ Starts a volume.
+
+ :param node: The node you wish to start.
+ :type node: :class:`Node`
+
+ :rtype: : ``bool``
+ """
+ action = 'startServer'
+ body = {'action': action,
+ 'serverId': node.id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_list_availability_zones(self):
+ """
+ Returns a list of availability zones.
+ """
+
+ availability_zones = []
+
+ for key, values in self.AVAILABILITY_ZONE.items():
+ name = copy.deepcopy(values)["name"]
+
+ availability_zone = ProfitBricksAvailabilityZone(
+ name=name
+ )
+ availability_zones.append(availability_zone)
+
+ return availability_zones
+
+ def ex_describe_node(self, node):
+ """
+ Describes a node.
+
+ :param node: The node you wish to describe.
+ :type node: :class:`Node`
+
+ :return: Instance of class ``Node``
+ :rtype: :class:`Node`
+ """
+ action = 'getServer'
+ body = {'action': action,
+ 'serverId': node.id
+ }
+
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ nodes = self._to_nodes(data)
+ return nodes[0]
+
+ def ex_update_node(self, node, name=None, cores=None,
+ ram=None, availability_zone=None):
+ """
+ Updates a node.
+
+ :param cores: The number of CPUs the node should have.
+ :type device: : ``int``
+
+ :param ram: The amount of ram the machine should have.
+ :type ram: : ``int``
+
+ :param ex_availability_zone: Update the availability zone.
+ :type ex_availability_zone: :class:`ProfitBricksAvailabilityZone`
+
+ :rtype: : ``bool``
+ """
+ action = 'updateServer'
+
+ body = {'action': action,
+ 'request': 'true',
+ 'serverId': node.id
+ }
+
+ if name:
+ body['serverName'] = name
+
+ if cores:
+ body['cores'] = str(cores)
+
+ if ram:
+ body['ram'] = str(ram)
+
+ if availability_zone:
+ body['availabilityZone'] = availability_zone.name
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ '''
+ Datacenter Extension Functions
+ '''
+
+ def ex_create_datacenter(self, name, location):
+ """
+ Creates a datacenter.
+
+ ProfitBricks has a concept of datacenters.
+ These represent buckets into which you
+ can place various compute resources.
+
+ :param name: The DC name.
+ :type name: : ``str``
+
+ :param location: The DC region.
+ :type location: : ``str``
+
+ :return: Instance of class ``Datacenter``
+ :rtype: :class:`Datacenter`
+ """
+ action = 'createDataCenter'
+
+ body = {'action': action,
+ 'request': 'true',
+ 'dataCenterName': name,
+ 'location': location.lower()
+ }
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ datacenters = self._to_datacenters(data)
+ return datacenters[0]
+
+ def ex_destroy_datacenter(self, datacenter):
+ """
+ Destroys a datacenter.
+
+ :param datacenter: The DC you're destroying.
+ :type datacenter: :class:`Datacenter`
+
+ :rtype: : ``bool``
+ """
+ action = 'deleteDataCenter'
+ body = {'action': action,
+ 'dataCenterId': datacenter.id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_describe_datacenter(self, datacenter_id):
+ """
+ Describes a datacenter.
+
+ :param datacenter_id: The DC you are describing.
+ :type datacenter_id: ``str``
+
+ :return: Instance of class ``Datacenter``
+ :rtype: :class:`Datacenter`
+ """
+
+ action = 'getDataCenter'
+ body = {'action': action,
+ 'dataCenterId': datacenter_id
+ }
+
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ datacenters = self._to_datacenters(data)
+ return datacenters[0]
+
+ def ex_list_datacenters(self):
+ """
+ Lists all datacenters.
+
+ :return: ``list`` of class ``Datacenter``
+ :rtype: :class:`Datacenter`
+ """
+ action = 'getAllDataCenters'
+ body = {'action': action}
+
+ return self._to_datacenters(self.connection.request(
+ action=action,
+ data=body,
+ method='POST').object)
+
+ def ex_rename_datacenter(self, datacenter, name):
+ """
+ Update a datacenter.
+
+ :param datacenter: The DC you are renaming.
+ :type datacenter: :class:`Datacenter`
+
+ :param name: The DC name.
+ :type name: : ``str``
+
+ :rtype: : ``bool``
+ """
+ action = 'updateDataCenter'
+ body = {'action': action,
+ 'request': 'true',
+ 'dataCenterId': datacenter.id,
+ 'dataCenterName': name
+ }
+
+ self.connection.request(action=action,
+ data=body,
+ method='POST').object
+
+ return True
+
+ def ex_clear_datacenter(self, datacenter):
+ """
+ Clear a datacenter.
+
+ This removes all objects in a DC.
+
+ :param datacenter: The DC you're clearing.
+ :type datacenter: :class:`Datacenter`
+
+ :rtype: : ``bool``
+ """
+ action = 'clearDataCenter'
+ body = {'action': action,
+ 'dataCenterId': datacenter.id
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ '''
+ Network Interface Extension Functions
+ '''
+
+ def ex_list_network_interfaces(self):
+ """
+ Lists all network interfaces.
+
+ :return: ``list`` of class ``ProfitBricksNetworkInterface``
+ :rtype: :class:`ProfitBricksNetworkInterface`
+ """
+ action = 'getAllNic'
+ body = {'action': action}
+
+ return self._to_interfaces(
+ self.connection.request(action=action,
+ data=body,
+ method='POST').object)
+
+ def ex_describe_network_interface(self, network_interface):
+ """
+ Describes a network interface.
+
+ :param network_interface: The NIC you wish to describe.
+ :type network_interface: :class:`ProfitBricksNetworkInterface`
+
+ :return: Instance of class ``ProfitBricksNetworkInterface``
+ :rtype: :class:`ProfitBricksNetworkInterface`
+ """
+ action = 'getNic'
+ body = {'action': action,
+ 'nicId': network_interface.id
+ }
+
+ return self._to_interface(
+ self.connection.request(
+ action=action,
+ data=body,
+ method='POST').object.findall('.//return')[0])
+
+ def ex_create_network_interface(self, node,
+ lan_id=None, ip=None, nic_name=None,
+ dhcp_active=True):
+ """
+ Creates a network interface.
+
+ :param lan_id: The ID for the LAN.
+ :type lan_id: : ``int``
+
+ :param ip: The IP address for the NIC.
+ :type ip: ``str``
+
+ :param nic_name: The name of the NIC, e.g. PUBLIC.
+ :type nic_name: ``str``
+
+ :param dhcp_active: Set to false to disable.
+ :type dhcp_active: ``bool``
+
+ :return: Instance of class ``ProfitBricksNetworkInterface``
+ :rtype: :class:`ProfitBricksNetworkInterface`
+ """
+ action = 'createNic'
+ body = {'action': action,
+ 'request': 'true',
+ 'serverId': node.id,
+ 'dhcpActive': str(dhcp_active)
+ }
+
+ if lan_id:
+ body['lanId'] = str(lan_id)
+ else:
+ body['lanId'] = str(1)
+
+ if ip:
+ body['ip'] = ip
+
+ if nic_name:
+ body['nicName'] = nic_name
+
+ data = self.connection.request(action=action,
+ data=body,
+ method='POST').object
+ interfaces = self._to_interfaces(data)
+ return interfaces[0]
+
+ def ex_update_network_interface(self, network_interface, name=None,
+ lan_id=None, ip=None,
+ dhcp_active=None):
+ """
+ Updates a network interface.
+
+ :param lan_id: The ID for the LAN.
+ :type lan_id: : ``int``
+
+ :param ip: The IP address for the NIC.
+ :type ip: ``str``
+
+ :param name: The name of the NIC, e.g. PUBLIC.
+ :type name: ``str``
+
+ :param dhcp_active: Set to false to disable.
+ :type dhcp_active: ``bool``
+
+ :rtype: : ``bool``
+ """
+ action = 'updateNic'
+ body = {'action': action,
+ 'request': 'true',
+ 'nicId': network_interface.id
+ }
+
+ if name:
+ body['nicName'] = name
+
+ if lan_id:
+ body['lanId'] = str(lan_id)
+
+ if ip:
+ body['ip'] = ip
+
+ if dhcp_active is not None:
+ body['dhcpActive'] = str(dhcp_active).lower()
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_destroy_network_interface(self, network_interface):
+ """
+ Destroy a network interface.
+
+ :param network_interface: The NIC you wish to describe.
+ :type network_interface: :class:`ProfitBricksNetworkInterface`
+
+ :rtype: : ``bool``
+ """
+
+ action = 'deleteNic'
+ body = {'action': action,
+ 'nicId': network_interface.id}
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ def ex_set_inet_access(self, datacenter,
+ network_interface, internet_access=True):
+
+ action = 'setInternetAccess'
+
+ body = {'action': action,
+ 'dataCenterId': datacenter.id,
+ 'lanId': network_interface.extra['lan_id'],
+ 'internetAccess': str(internet_access).lower()
+ }
+
+ self.connection.request(action=action,
+ data=body, method='POST').object
+
+ return True
+
+ """
+ Private Functions
+ """
+
+ def _to_datacenters(self, object):
+ return [self._to_datacenter(
+ datacenter) for datacenter in object.findall('.//return')]
+
+ def _to_datacenter(self, datacenter):
+ datacenter_id = datacenter.find('dataCenterId').text
+ if ET.iselement(datacenter.find('dataCenterName')):
+ datacenter_name = datacenter.find('dataCenterName').text
+ else:
+ datacenter_name = None
+ version = datacenter.find('dataCenterVersion').text
+ if ET.iselement(datacenter.find('provisioningState')):
+ provisioning_state = datacenter.find('provisioningState').text
+ else:
+ provisioning_state = None
+ if ET.iselement(datacenter.find('location')):
+ location = datacenter.find('location').text
+ else:
+ location = None
+
+ provisioning_state = self.PROVISIONING_STATE.get(provisioning_state,
+ NodeState.UNKNOWN)
+
+ return Datacenter(id=datacenter_id,
+ name=datacenter_name,
+ version=version,
+ driver=self.connection.driver,
+ extra={'provisioning_state': provisioning_state,
+ 'location': location})
+
+ def _to_images(self, object):
+ return [self._to_image(image) for image in object.findall('.//return')]
+
+ def _to_image(self, image):
+ image_id = image.find('imageId').text
+ image_name = image.find('imageName').text
+ image_size = image.find('imageSize').text
+ image_type = image.find('imageType').text
+ os_type = image.find('osType').text
+ public = image.find('public').text
+ writeable = image.find('writeable').text
+
+ if ET.iselement(image.find('cpuHotpluggable')):
+ cpu_hotpluggable = image.find('cpuHotpluggable').text
+ else:
+ cpu_hotpluggable = None
+
+ if ET.iselement(image.find('memoryHotpluggable')):
+ memory_hotpluggable = image.find('memoryHotpluggable').text
+ else:
+ memory_hotpluggable = None
+
+ if ET.iselement(image.find('location')):
+ if image.find('region'):
+ image_region = image.find('region').text
+ else:
+ image_region = None
+ else:
+ image_region = None
+
+ return NodeImage(id=image_id,
+ name=image_name,
+ driver=self.connection.driver,
+ extra={'image_size': image_size,
+ 'image_type': image_type,
+ 'cpu_hotpluggable': cpu_hotpluggable,
+ 'memory_hotpluggable': memory_hotpluggable,
+ 'os_type': os_type,
+ 'public': public,
+ 'location': image_region,
+ 'writeable': writeable})
+
+ def _to_nodes(self, object):
+ return [self._to_node(n) for n in object.findall('.//return')]
+
+ def _to_node(self, node):
+ """
+ Convert the request into a node Node
+ """
+ ATTRIBUTE_NAME_MAP = {
+ 'dataCenterId': 'datacenter_id',
+ 'dataCenterVersion': 'datacenter_version',
+ 'serverId': 'node_id',
+ 'serverName': 'node_name',
+ 'cores': 'cores',
+ 'ram': 'ram',
+ 'internetAccess': 'internet_access',
+ 'provisioningState': 'provisioning_state',
+ 'virtualMachineState': 'virtual_machine_state',
+ 'creationTime': 'creation_time',
+ 'lastModificationTime': 'last_modification_time',
+ 'osType': 'os_type',
+ 'availabilityZone': 'availability_zone',
+ 'cpuHotPlug': 'cpu_hotpluggable',
+ 'ramHotPlug': 'memory_hotpluggable',
+ 'nicHotPlug': 'nic_hotpluggable',
+ 'discVirtioHotPlug': 'disc_virtio_hotplug',
+ 'discVirtioHotUnPlug': 'disc_virtio_hotunplug'
+ }
+
+ extra = {}
+ for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items():
+ elem = node.find(attribute_name)
+
+ if ET.iselement(elem):
+ value = elem.text
+ else:
+ value = None
+
+ extra[extra_name] = value
+
+ public_ips = []
+ private_ips = []
+
+ if ET.iselement(node.find('nics')):
+ for nic in node.findall('.//nics'):
+ n_elements = list(nic.findall('.//ips'))
+ if len(n_elements) > 0:
+ ip = n_elements[0].text
+ if is_private_subnet(ip):
+ private_ips.append(ip)
+ else:
+ public_ips.append(ip)
+
+ extra['provisioning_state'] = self.PROVISIONING_STATE.get(
+ extra['provisioning_state'], NodeState.UNKNOWN)
+
+ node_id = extra['node_id']
+ node_name = extra['node_name']
+ state = self.NODE_STATE_MAP.get(extra['virtual_machine_state'],
+ NodeState.UNKNOWN)
+
+ return Node(
+ id=node_id,
+ name=node_name,
+ state=state,
+ public_ips=public_ips,
+ private_ips=private_ips,
+ driver=self.connection.driver,
+ extra=extra)
+
+ def _to_volumes(self, object):
+ return [self._to_volume(
+ volume) for volume in object.findall('.//return')]
+
+ def _to_volume(self, volume, node=None):
+ ATTRIBUTE_NAME_MAP = {
+ 'dataCenterId': 'datacenter_id',
+ 'storageId': 'storage_id',
+ 'storageName': 'storage_name',
+ 'serverIds': 'server_id',
+ 'creationTime': 'creation_time',
+ 'lastModificationTime': 'last_modification_time',
+ 'provisioningState': 'provisioning_state',
+ 'size': 'size',
+ }
+
+ extra = {}
+ for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items():
+ elem = volume.find(attribute_name)
+
+ if ET.iselement(elem):
+ value = elem.text
+ else:
+ value = None
+
+ extra[extra_name] = value
+
+ if ET.iselement(volume.find('mountImage')):
+ image_id = volume.find('mountImage')[0].text
+ image_name = volume.find('mountImage')[1].text
+ else:
+ image_id = None
+ image_name = None
+
+ extra['image_id'] = image_id
+ extra['image_name'] = image_name
+ extra['size'] = int(extra['size']) if extra['size'] else 0
+ extra['provisioning_state'] = \
+ self.PROVISIONING_STATE.get(extra['provisioning_state'],
+ NodeState.UNKNOWN)
+
+ storage_id = extra['storage_id']
+ storage_name = extra['storage_name']
+ size = extra['size']
+
+ return StorageVolume(
+ id=storage_id,
+ name=storage_name,
+ size=size,
+ driver=self.connection.driver,
+ extra=extra)
+
+ def _to_interfaces(self, object):
+ return [self._to_interface(
+ interface) for interface in object.findall('.//return')]
+
+ def _to_interface(self, interface):
+ ATTRIBUTE_NAME_MAP = {
+ 'nicId': 'nic_id',
+ 'nicName': 'nic_name',
+ 'serverId': 'server_id',
+ 'lanId': 'lan_id',
+ 'internetAccess': 'internet_access',
+ 'macAddress': 'mac_address',
+ 'dhcpActive': 'dhcp_active',
+ 'gatewayIp': 'gateway_ip',
+ 'provisioningState': 'provisioning_state',
+ 'dataCenterId': 'datacenter_id',
+ 'dataCenterVersion': 'datacenter_version'
+ }
+
+ extra = {}
+ for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items():
+ elem = interface.find(attribute_name)
+
+ if ET.iselement(elem):
+ value = elem.text
+ else:
+ value = None
+
+ extra[extra_name] = value
+
+ ips = []
+
+ if ET.iselement(interface.find('ips')):
+ for ip in interface.findall('.//ips'):
+ ips.append(ip.text)
+
+ extra['ips'] = ips
+
+ nic_id = extra['nic_id']
+ nic_name = extra['nic_name']
+ state = self.PROVISIONING_STATE.get(extra['provisioning_state'],
+ NodeState.UNKNOWN)
+
+ return ProfitBricksNetworkInterface(
+ id=nic_id,
+ name=nic_name,
+ state=state,
+ extra=extra)
+
+ def _to_location(self, data):
+
+ return NodeLocation(id=data["region"],
+ name=data["region"],
+ country=data["country"],
+ driver=self.connection.driver)
+
+ def _to_node_size(self, data):
+ """
+ Convert the PROFIT_BRICKS_GENERIC_SIZES into NodeSize
+ """
+ return NodeSize(id=data["id"],
+ name=data["name"],
+ ram=data["ram"],
+ disk=data["disk"],
+ bandwidth=None,
+ price=None,
+ driver=self.connection.driver,
+ extra={
+ 'cores': data["cores"]})
+
+ def _wait_for_datacenter_state(self, datacenter, state=NodeState.RUNNING,
+ timeout=300, interval=5):
+ """
+ Private function that waits the datacenter to transition into the
+ specified state.
+
+ :return: Datacenter object on success.
+ :rtype: :class:`.Datacenter`
+ """
+ wait_time = 0
+ datacenter = self.ex_describe_datacenter(datacenter_id=datacenter.id)
+
+ while (datacenter.extra['provisioning_state'] != state):
+ datacenter = \
+ self.ex_describe_datacenter(datacenter_id=datacenter.id)
+ if datacenter.extra['provisioning_state'] == state:
+ break
+
+ if wait_time >= timeout:
+ raise Exception('Datacenter didn\'t transition to %s state '
+ 'in %s seconds' % (state, timeout))
+
+ wait_time += interval
+ time.sleep(interval)
+
+ return datacenter
+
+ def _create_new_datacenter_for_node(self, name):
+ """
+ Creates a Datacenter for a node.
+ """
+ dc_name = name + '-DC'
+
+ return self.ex_create_datacenter(name=dc_name, location='us/las')
+
+ def _wait_for_storage_volume_state(self, volume, state=NodeState.RUNNING,
+ timeout=300, interval=5):
+ """
+ Wait for volume to transition into the specified state.
+
+ :return: Volume object on success.
+ :rtype: :class:`Volume`
+ """
+ wait_time = 0
+ volume = self.ex_describe_volume(volume_id=volume.id)
+
+ while (volume.extra['provisioning_state'] != state):
+ volume = self.ex_describe_volume(volume_id=volume.id)
+ if volume.extra['provisioning_state'] == state:
+ break
+
+ if wait_time >= timeout:
+ raise Exception('Volume didn\'t transition to %s state '
+ 'in %s seconds' % (state, timeout))
+
+ wait_time += interval
+ time.sleep(interval)
+
+ return volume
+
+ def _create_node_volume(self, ex_disk, image, password,
+ name, ex_datacenter=None, new_datacenter=None):
+
+ volume_name = name + '-volume'
+
+ if ex_datacenter:
+ volume = self.create_volume(size=ex_disk,
+ ex_datacenter=ex_datacenter,
+ ex_image=image,
+ ex_password=password,
+ name=volume_name)
+ else:
+ volume = self.create_volume(size=ex_disk,
+ ex_datacenter=new_datacenter,
+ ex_image=image,
+ ex_password=password,
+ name=volume_name)
+
+ return volume
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/compute/providers.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/providers.py b/libcloud/compute/providers.py
index 5c19f61..b449e78 100644
--- a/libcloud/compute/providers.py
+++ b/libcloud/compute/providers.py
@@ -149,6 +149,8 @@ DRIVERS = {
('libcloud.compute.drivers.ec2', 'OutscaleINCNodeDriver'),
Provider.VSPHERE:
('libcloud.compute.drivers.vsphere', 'VSphereNodeDriver'),
+ Provider.PROFIT_BRICKS:
+ ('libcloud.compute.drivers.profitbricks', 'ProfitBricksNodeDriver'),
# Deprecated
Provider.CLOUDSIGMA_US:
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/compute/types.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py
index c7c7616..f07bc48 100644
--- a/libcloud/compute/types.py
+++ b/libcloud/compute/types.py
@@ -76,6 +76,7 @@ class Provider(object):
:cvar IKOULA: Ikoula driver.
:cvar OUTSCALE_SAS: Outscale SAS driver.
:cvar OUTSCALE_INC: Outscale INC driver.
+ :cvar PROFIT_BRICKS: ProfitBricks driver.
"""
DUMMY = 'dummy'
EC2 = 'ec2_us_east'
@@ -122,6 +123,7 @@ class Provider(object):
OUTSCALE_SAS = 'outscale_sas'
OUTSCALE_INC = 'outscale_inc'
VSPHERE = 'vsphere'
+ PROFIT_BRICKS = 'profitbricks'
# OpenStack based providers
HPCLOUD = 'hpcloud'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/attach_volume.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/attach_volume.xml b/libcloud/test/compute/fixtures/profitbricks/attach_volume.xml
new file mode 100644
index 0000000..8b2b1f5
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/attach_volume.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:connectStorageToServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3613039</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>4</dataCenterVersion>
+ </return>
+ </ns2:connectStorageToServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/create_node.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/create_node.xml b/libcloud/test/compute/fixtures/profitbricks/create_node.xml
new file mode 100644
index 0000000..ad515ba
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/create_node.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:createServerReturn xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3768523</requestId>
+ <dataCenterId>3aefc31b-57e9-4af6-8348-af961ac00f74</dataCenterId>
+ <dataCenterVersion>3</dataCenterVersion>
+ <serverId>7b18b85f-cc93-4c2d-abcc-5ce732d35750</serverId>
+ </return>
+ </ns2:createServerReturn>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/create_volume.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/create_volume.xml b/libcloud/test/compute/fixtures/profitbricks/create_volume.xml
new file mode 100644
index 0000000..326879a
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/create_volume.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:createStorageReturn xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3532463</requestId>
+ <dataCenterId>06eac419-c2b3-4761-aeb9-10efdd2cf292</dataCenterId>
+ <dataCenterVersion>3</dataCenterVersion>
+ <storageId>f54aeea3-667a-4460-8cf0-80909509df0c</storageId>
+ </return>
+ </ns2:createStorageReturn>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/destroy_node.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/destroy_node.xml b/libcloud/test/compute/fixtures/profitbricks/destroy_node.xml
new file mode 100644
index 0000000..1dacfaf
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/destroy_node.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:deleteServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3498434</requestId>
+ <dataCenterId>782247bf-f12d-4f08-8050-302c02c4b2e0</dataCenterId>
+ <dataCenterVersion>2</dataCenterVersion>
+ </return>
+ </ns2:deleteServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/destroy_volume.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/destroy_volume.xml b/libcloud/test/compute/fixtures/profitbricks/destroy_volume.xml
new file mode 100644
index 0000000..0591e1c
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/destroy_volume.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:deleteStorageResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3616447</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>13</dataCenterVersion>
+ </return>
+ </ns2:deleteStorageResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/detach_volume.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/detach_volume.xml b/libcloud/test/compute/fixtures/profitbricks/detach_volume.xml
new file mode 100644
index 0000000..fafc327
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/detach_volume.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:disconnectStorageFromServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3614242</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>6</dataCenterVersion>
+ </return>
+ </ns2:disconnectStorageFromServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_clear_datacenter.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_clear_datacenter.xml b/libcloud/test/compute/fixtures/profitbricks/ex_clear_datacenter.xml
new file mode 100644
index 0000000..5259d45
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_clear_datacenter.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:clearDataCenterResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3339052</requestId>
+ <dataCenterId>8669a69f-2274-4520-b51e-dbdf3986a476</dataCenterId>
+ <dataCenterVersion>2</dataCenterVersion>
+ </return>
+ </ns2:clearDataCenterResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_create_datacenter.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_create_datacenter.xml b/libcloud/test/compute/fixtures/profitbricks/ex_create_datacenter.xml
new file mode 100644
index 0000000..f4238d8
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_create_datacenter.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:createDataCenterResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3711001</requestId>
+ <dataCenterId>0c793dd1-d4cd-4141-86f3-8b1a24b2d604</dataCenterId>
+ <dataCenterVersion>1</dataCenterVersion>
+ <location>us/las</location>
+ </return>
+ </ns2:createDataCenterResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_create_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_create_network_interface.xml b/libcloud/test/compute/fixtures/profitbricks/ex_create_network_interface.xml
new file mode 100644
index 0000000..830d41d
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_create_network_interface.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:createNicReturn xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3633314</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>27</dataCenterVersion>
+ <nicId>951e1b49-5f1b-4b2b-b7d9-263dba6e2ddd</nicId>
+ </return>
+ </ns2:createNicReturn>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_describe_datacenter.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_describe_datacenter.xml b/libcloud/test/compute/fixtures/profitbricks/ex_describe_datacenter.xml
new file mode 100644
index 0000000..cb4a1a0
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_describe_datacenter.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getDataCenterResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3719240</requestId>
+ <dataCenterId>a3e6f83a-8982-4d6a-aebc-60baf5755ede</dataCenterId>
+ <dataCenterVersion>1</dataCenterVersion>
+ <dataCenterName>StackPointCloud</dataCenterName>
+ <provisioningState>AVAILABLE</provisioningState>
+ <location>us/las</location>
+ </return>
+ </ns2:getDataCenterResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_describe_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_describe_network_interface.xml b/libcloud/test/compute/fixtures/profitbricks/ex_describe_network_interface.xml
new file mode 100644
index 0000000..e2235b9
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_describe_network_interface.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getNicResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3707226</requestId>
+ <dataCenterId>a3a2e730-0dc3-47e6-bac6-4c056d5e2aee</dataCenterId>
+ <dataCenterVersion>6</dataCenterVersion>
+ <nicId>f1c7a244-2fa6-44ee-8fb6-871f337683a3</nicId>
+ <lanId>1</lanId>
+ <internetAccess>false</internetAccess>
+ <serverId>c09f4f31-336c-4ad2-9ec7-591778513408</serverId>
+ <ips>10.10.38.12</ips>
+ <macAddress>02:01:96:d7:60:e0</macAddress>
+ <firewall>
+ <active>false</active>
+ <firewallId>01490a19-2b20-43cc-86a4-ff0b0460f076</firewallId>
+ <nicId>f1c7a244-2fa6-44ee-8fb6-871f337683a3</nicId>
+ <provisioningState>AVAILABLE</provisioningState>
+ </firewall>
+ <dhcpActive>true</dhcpActive>
+ <provisioningState>AVAILABLE</provisioningState>
+ </return>
+ </ns2:getNicResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_describe_node.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_describe_node.xml b/libcloud/test/compute/fixtures/profitbricks/ex_describe_node.xml
new file mode 100644
index 0000000..5567e8f
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_describe_node.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3706813</requestId>
+ <dataCenterId>a3a2e730-0dc3-47e6-bac6-4c056d5e2aee</dataCenterId>
+ <dataCenterVersion>6</dataCenterVersion>
+ <serverId>c09f4f31-336c-4ad2-9ec7-591778513408</serverId>
+ <serverName>server001</serverName>
+ <cores>1</cores>
+ <ram>1024</ram>
+ <internetAccess>true</internetAccess>
+ <ips>10.10.38.12</ips>
+ <ips>162.254.26.14</ips>
+ <connectedStorages>
+ <bootDevice>true</bootDevice>
+ <busType>VIRTIO</busType>
+ <deviceNumber>1</deviceNumber>
+ <size>50</size>
+ <storageId>addb19d8-e664-43c1-bd2d-ad9210edc610</storageId>
+ <storageName>storage001</storageName>
+ </connectedStorages>
+ <nics>
+ <dataCenterId>a3a2e730-0dc3-47e6-bac6-4c056d5e2aee</dataCenterId>
+ <dataCenterVersion>6</dataCenterVersion>
+ <nicId>f1c7a244-2fa6-44ee-8fb6-871f337683a3</nicId>
+ <lanId>1</lanId>
+ <internetAccess>false</internetAccess>
+ <serverId>c09f4f31-336c-4ad2-9ec7-591778513408</serverId>
+ <ips>10.10.38.12</ips>
+ <macAddress>02:01:96:d7:60:e0</macAddress>
+ <firewall>
+ <active>false</active>
+ <firewallId>01490a19-2b20-43cc-86a4-ff0b0460f076</firewallId>
+ <nicId>f1c7a244-2fa6-44ee-8fb6-871f337683a3</nicId>
+ <provisioningState>AVAILABLE</provisioningState>
+ </firewall>
+ <dhcpActive>true</dhcpActive>
+ <provisioningState>AVAILABLE</provisioningState>
+ </nics>
+ <nics>
+ <dataCenterId>a3a2e730-0dc3-47e6-bac6-4c056d5e2aee</dataCenterId>
+ <dataCenterVersion>6</dataCenterVersion>
+ <nicId>e6263870-cd70-42e4-956a-00f3bbec70e3</nicId>
+ <nicName>PUBLIC</nicName>
+ <lanId>3</lanId>
+ <internetAccess>true</internetAccess>
+ <serverId>c09f4f31-336c-4ad2-9ec7-591778513408</serverId>
+ <ips>162.254.26.14</ips>
+ <macAddress>02:01:9c:53:c3:50</macAddress>
+ <firewall>
+ <active>false</active>
+ <firewallId>c0fa291e-38c2-48a6-bd15-b66ba54ac18a</firewallId>
+ <nicId>e6263870-cd70-42e4-956a-00f3bbec70e3</nicId>
+ <provisioningState>AVAILABLE</provisioningState>
+ </firewall>
+ <dhcpActive>false</dhcpActive>
+ <gatewayIp>162.254.26.1</gatewayIp>
+ <provisioningState>AVAILABLE</provisioningState>
+ </nics>
+ <provisioningState>AVAILABLE</provisioningState>
+ <virtualMachineState>RUNNING</virtualMachineState>
+ <creationTime>2014-07-16T18:53:05.109Z</creationTime>
+ <lastModificationTime>2014-07-16T19:57:51.577Z</lastModificationTime>
+ <osType>LINUX</osType>
+ <availabilityZone>AUTO</availabilityZone>
+ <cpuHotPlug>true</cpuHotPlug>
+ <ramHotPlug>true</ramHotPlug>
+ <nicHotPlug>true</nicHotPlug>
+ <nicHotUnPlug>true</nicHotUnPlug>
+ <discVirtioHotPlug>true</discVirtioHotPlug>
+ <discVirtioHotUnPlug>true</discVirtioHotUnPlug>
+ </return>
+ </ns2:getServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_describe_volume.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_describe_volume.xml b/libcloud/test/compute/fixtures/profitbricks/ex_describe_volume.xml
new file mode 100644
index 0000000..050b4b5
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_describe_volume.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getStorageResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3767716</requestId>
+ <dataCenterId>905f1346-d199-425d-a035-7dc28f6819cd</dataCenterId>
+ <dataCenterVersion>2</dataCenterVersion>
+ <storageId>00d0b9e7-e016-456f-85a0-517aa9a34bf5</storageId>
+ <size>50</size>
+ <storageName>StackPointCloud-Volume</storageName>
+ <mountImage>
+ <imageId>cd59b162-0289-11e4-9f63-52540066fee9</imageId>
+ <imageName>Debian-7-server-2014-07-01</imageName>
+ </mountImage>
+ <provisioningState>AVAILABLE</provisioningState>
+ <creationTime>2014-07-21T17:37:45.958Z</creationTime>
+ <lastModificationTime>2014-07-21T17:37:45.958Z</lastModificationTime>
+ </return>
+ </ns2:getStorageResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_destroy_datacenter.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_destroy_datacenter.xml b/libcloud/test/compute/fixtures/profitbricks/ex_destroy_datacenter.xml
new file mode 100644
index 0000000..4d36bdb
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_destroy_datacenter.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:deleteDataCenterResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3339313</requestId>
+ </return>
+ </ns2:deleteDataCenterResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_destroy_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_destroy_network_interface.xml b/libcloud/test/compute/fixtures/profitbricks/ex_destroy_network_interface.xml
new file mode 100644
index 0000000..6584f06
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_destroy_network_interface.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:deleteNicResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3634902</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>31</dataCenterVersion>
+ </return>
+ </ns2:deleteNicResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_list_datacenters.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_list_datacenters.xml b/libcloud/test/compute/fixtures/profitbricks/ex_list_datacenters.xml
new file mode 100644
index 0000000..dfb9245
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_list_datacenters.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getAllDataCentersResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <dataCenterId>a3e6f83a-8982-4d6a-aebc-60baf5755ede</dataCenterId>
+ <dataCenterName>StackPointCloud</dataCenterName>
+ <dataCenterVersion>1</dataCenterVersion>
+ <provisioningState>AVAILABLE</provisioningState>
+ </return>
+ <return>
+ <dataCenterId>c68f77b8-7ecb-40e9-8b41-79415dffc0f1</dataCenterId>
+ <dataCenterName>XYZ</dataCenterName>
+ <dataCenterVersion>2</dataCenterVersion>
+ <provisioningState>AVAILABLE</provisioningState>
+ </return>
+ </ns2:getAllDataCentersResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_list_network_interfaces.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_list_network_interfaces.xml b/libcloud/test/compute/fixtures/profitbricks/ex_list_network_interfaces.xml
new file mode 100644
index 0000000..7164f22
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_list_network_interfaces.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getAllNicResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>26</dataCenterVersion>
+ <nicId>6b38a4f3-b851-4614-9e3a-5ddff4727727</nicId>
+ <nicName>StackPointCloud</nicName>
+ <lanId>3</lanId>
+ <internetAccess>false</internetAccess>
+ <serverId>234f0cf9-1efc-4ade-b829-036456584116</serverId>
+ <ips>10.14.96.11</ips>
+ <ips>162.254.26.14</ips>
+ <ips>162.254.26.15</ips>
+ <macAddress>02:01:40:47:90:04</macAddress>
+ <firewall>
+ <active>false</active>
+ <firewallId>e93f74b2-d969-4b7d-8fad-3931b85dbc4d</firewallId>
+ <firewallRules>
+ <firewallRuleId>d6f7e726-c13d-464c-b454-cae726dac75d</firewallRuleId>
+ <protocol>ANY</protocol>
+ <sourceIp>1.2.3.4</sourceIp>
+ <targetIp>1.2.3.4</targetIp>
+ </firewallRules>
+ <firewallRules>
+ <firewallRuleId>87773a01-b7e2-481e-8a44-d5ffac830292</firewallRuleId>
+ <protocol>ICMP</protocol>
+ </firewallRules>
+ <nicId>6b38a4f3-b851-4614-9e3a-5ddff4727727</nicId>
+ <provisioningState>AVAILABLE</provisioningState>
+ </firewall>
+ <dhcpActive>true</dhcpActive>
+ <provisioningState>AVAILABLE</provisioningState>
+ </return>
+ <return>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>26</dataCenterVersion>
+ <nicId>47e3b2ce-7846-41cc-8404-1475190e89a3</nicId>
+ <lanId>3</lanId>
+ <internetAccess>false</internetAccess>
+ <serverId>7fb5d34c-77c2-4452-b7b2-274fa0f46327</serverId>
+ <ips>10.14.96.12</ips>
+ <macAddress>02:01:fe:1c:81:73</macAddress>
+ <firewall>
+ <active>false</active>
+ <firewallId>d3d1d8b9-0dd5-4866-8429-a1817be7b6e9</firewallId>
+ <nicId>47e3b2ce-7846-41cc-8404-1475190e89a3</nicId>
+ <provisioningState>AVAILABLE</provisioningState>
+ </firewall>
+ <dhcpActive>true</dhcpActive>
+ <provisioningState>AVAILABLE</provisioningState>
+ </return>
+ <return>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>26</dataCenterVersion>
+ <nicId>cbd00ace-d210-43e8-8cb7-097ad6b33e82</nicId>
+ <lanId>1</lanId>
+ <internetAccess>true</internetAccess>
+ <serverId>234f0cf9-1efc-4ade-b829-036456584116</serverId>
+ <ips>208.94.38.110</ips>
+ <macAddress>02:01:e9:20:cd:81</macAddress>
+ <firewall>
+ <active>false</active>
+ <firewallId>a1a2e1da-7672-4a5c-af62-6c37edaffd26</firewallId>
+ <nicId>cbd00ace-d210-43e8-8cb7-097ad6b33e82</nicId>
+ <provisioningState>INPROCESS</provisioningState>
+ </firewall>
+ <dhcpActive>true</dhcpActive>
+ <gatewayIp>208.94.38.1</gatewayIp>
+ <provisioningState>AVAILABLE</provisioningState>
+ </return>
+ </ns2:getAllNicResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_start_node.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_start_node.xml b/libcloud/test/compute/fixtures/profitbricks/ex_start_node.xml
new file mode 100644
index 0000000..f83d6a4
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_start_node.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:stopServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3494585</requestId>
+ </return>
+ </ns2:stopServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_stop_node.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_stop_node.xml b/libcloud/test/compute/fixtures/profitbricks/ex_stop_node.xml
new file mode 100644
index 0000000..f83d6a4
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_stop_node.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:stopServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3494585</requestId>
+ </return>
+ </ns2:stopServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_update_datacenter.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_update_datacenter.xml b/libcloud/test/compute/fixtures/profitbricks/ex_update_datacenter.xml
new file mode 100644
index 0000000..270aa41
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_update_datacenter.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:updateDataCenterResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3325148</requestId>
+ <dataCenterId>d96dfafc-9a8c-4c0e-8a0c-857a15db572d</dataCenterId>
+ <dataCenterVersion>3</dataCenterVersion>
+ </return>
+ </ns2:updateDataCenterResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_update_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_update_network_interface.xml b/libcloud/test/compute/fixtures/profitbricks/ex_update_network_interface.xml
new file mode 100644
index 0000000..f9f8e0d
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_update_network_interface.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:updateNicResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3665310</requestId>
+ <dataCenterId>aab9454d-c442-4d06-9dd7-7c6121ae5ca2</dataCenterId>
+ <dataCenterVersion>3</dataCenterVersion>
+ </return>
+ </ns2:updateNicResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_update_node.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_update_node.xml b/libcloud/test/compute/fixtures/profitbricks/ex_update_node.xml
new file mode 100644
index 0000000..03693a7
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_update_node.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:updateServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3623299</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>18</dataCenterVersion>
+ </return>
+ </ns2:updateServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/ex_update_volume.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/ex_update_volume.xml b/libcloud/test/compute/fixtures/profitbricks/ex_update_volume.xml
new file mode 100644
index 0000000..03693a7
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/ex_update_volume.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:updateServerResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <requestId>3623299</requestId>
+ <dataCenterId>c2df1871-6aac-458e-ad1a-ef3f530cb7aa</dataCenterId>
+ <dataCenterVersion>18</dataCenterVersion>
+ </return>
+ </ns2:updateServerResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/34cff301/libcloud/test/compute/fixtures/profitbricks/list_images.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/profitbricks/list_images.xml b/libcloud/test/compute/fixtures/profitbricks/list_images.xml
new file mode 100644
index 0000000..3c8ac7b
--- /dev/null
+++ b/libcloud/test/compute/fixtures/profitbricks/list_images.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+ <S:Body>
+ <ns2:getAllImagesResponse xmlns:ns2="http://ws.api.profitbricks.com/">
+ <return>
+ <cpuHotpluggable>false</cpuHotpluggable>
+ <imageId>03b6c3e7-f2ad-11e3-a036-52540066fee9</imageId>
+ <imageName>windows-2012-r2-server-2014-06</imageName>
+ <imageSize>11264</imageSize>
+ <imageType>HDD</imageType>
+ <memoryHotpluggable>false</memoryHotpluggable>
+ <osType>WINDOWS</osType>
+ <public>true</public>
+ <region>NORTH_AMERICA</region>
+ <writeable>true</writeable>
+ </return>
+ <return>
+ <cpuHotpluggable>true</cpuHotpluggable>
+ <imageId>cd59b162-0289-11e4-9f63-52540066fee9</imageId>
+ <imageName>Debian-7-server-2014-07-01</imageName>
+ <imageSize>2048</imageSize>
+ <imageType>HDD</imageType>
+ <memoryHotpluggable>true</memoryHotpluggable>
+ <osType>LINUX</osType>
+ <public>true</public>
+ <region>NORTH_AMERICA</region>
+ <writeable>true</writeable>
+ </return>
+ <return>
+ <cpuHotpluggable>true</cpuHotpluggable>
+ <imageId>d2f627c4-0289-11e4-9f63-52540066fee9</imageId>
+ <imageName>CentOS-6-server-2014-07-01</imageName>
+ <imageSize>2048</imageSize>
+ <imageType>HDD</imageType>
+ <memoryHotpluggable>true</memoryHotpluggable>
+ <osType>LINUX</osType>
+ <public>true</public>
+ <region>NORTH_AMERICA</region>
+ <writeable>true</writeable>
+ </return>
+ </ns2:getAllImagesResponse>
+ </S:Body>
+</S:Envelope>
\ No newline at end of file