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 2012/04/13 06:30:01 UTC

svn commit: r1325611 - in /libcloud/trunk: ./ libcloud/compute/drivers/ test/compute/ test/compute/fixtures/brightbox/

Author: tomaz
Date: Fri Apr 13 04:30:01 2012
New Revision: 1325611

URL: http://svn.apache.org/viewvc?rev=1325611&view=rev
Log:
Add a lot of improvements and features to the Brightbox driver. This patch has
been contributed by Neil Wilson and is part of LIBCLOUD-184.

Added:
    libcloud/trunk/test/compute/fixtures/brightbox/create_cloud_ip.json
    libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_a.json
    libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_b.json
    libcloud/trunk/test/compute/fixtures/brightbox/list_cloud_ips.json
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/compute/drivers/brightbox.py
    libcloud/trunk/test/compute/fixtures/brightbox/list_images.json
    libcloud/trunk/test/compute/fixtures/brightbox/list_server_types.json
    libcloud/trunk/test/compute/fixtures/brightbox/list_servers.json
    libcloud/trunk/test/compute/fixtures/brightbox/list_zones.json
    libcloud/trunk/test/compute/test_brightbox.py

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Fri Apr 13 04:30:01 2012
@@ -32,6 +32,18 @@ Changes with Apache Libcloud in developm
       (http://www.educause.edu/blog/hes8/CloudComputingandtheVirtualCom/167931)
       [Jason Gionta, Tomaz Muraus]
 
+    - Improve and add new features to Brightbox driver ; LIBCLOUD-184
+        - Update fixtures to represent actual api output
+        - Update compute tests to 100% coverage
+        - Add userdata and server group extensions to create_node
+        - Add ipv6 support to public ip list
+        - Improve in line documentation
+        - Add lots of api output information to Node and Image
+          'extra' attributes
+        - Allow variable API versions (api_version argument)
+        - Allow reverse dns updates for cloud ip extensions
+      [Neil Wilson, Tomaz Muraus]
+
   *) Storage
 
     - Large object upload support for CloudFiles driver

Modified: libcloud/trunk/libcloud/compute/drivers/brightbox.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/brightbox.py?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/brightbox.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/brightbox.py Fri Apr 13 04:30:01 2012
@@ -15,17 +15,26 @@
 """
 Brightbox Driver
 """
+
 from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import b
+from libcloud.utils.py3 import u
 
 from libcloud.common.brightbox import BrightboxConnection
 from libcloud.compute.types import Provider, NodeState
 from libcloud.compute.base import NodeDriver
 from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation
 
+import base64
+
 
 API_VERSION = '1.0'
 
 
+def _extract(d, keys):
+    return dict((k, d[k]) for k in keys if k in d and d[k] is not None)
+
+
 class BrightboxNodeDriver(NodeDriver):
     """
     Brightbox node driver
@@ -45,31 +54,58 @@ class BrightboxNodeDriver(NodeDriver):
                       'failed': NodeState.UNKNOWN,
                       'unavailable': NodeState.UNKNOWN}
 
+    def __init__(self, key, secret=None, secure=True, host=None, port=None,
+                 api_version=API_VERSION, **kwargs):
+        super(BrightboxNodeDriver, self).__init__(key=key, secret=secret,
+                                                  secure=secure,
+                                                  host=host, port=port,
+                                                  api_version=api_version,
+                                                  **kwargs)
+
     def _to_node(self, data):
+        extra_data = _extract(data, ['fqdn', 'user_data', 'status',
+                                    'interfaces', 'snapshots',
+                                    'server_groups', 'hostname',
+                                    'started_at', 'created_at',
+                                    'deleted_at'])
+        extra_data['zone'] = self._to_location(data['zone'])
         return Node(
             id=data['id'],
             name=data['name'],
             state=self.NODE_STATE_MAP[data['status']],
-            public_ips=list(map(lambda cloud_ip: cloud_ip['public_ip'],
-                                                   data['cloud_ips'])),
-            private_ips=list(map(lambda interface: interface['ipv4_address'],
-                                                     data['interfaces'])),
+
+            private_ips=[interface['ipv4_address']
+                         for interface in data['interfaces']
+                         if 'ipv4_address' in interface],
+
+            public_ips=[cloud_ip['public_ip']
+                        for cloud_ip in data['cloud_ips']] +
+                       [interface['ipv6_address']
+                        for interface in data['interfaces']
+                        if 'ipv6_address' in interface],
+
             driver=self.connection.driver,
-            extra={
-                'status': data['status'],
-                'interfaces': data['interfaces']
-            }
+            size=self._to_size(data['server_type']),
+            image=self._to_image(data['image']),
+            extra=extra_data
         )
 
     def _to_image(self, data):
+        extra_data = _extract(data, ['arch', 'compatibility_mode',
+                                   'created_at', 'description',
+                                   'disk_size', 'min_ram', 'official',
+                                   'owner', 'public', 'source',
+                                   'source_type', 'status', 'username',
+                                   'virtual_size', 'licence_name'])
+
+        if data.get('ancestor', None):
+            extra_data['ancestor'] = self._to_image(data['ancestor'])
+
         return NodeImage(
             id=data['id'],
             name=data['name'],
             driver=self,
-            extra={
-                'description': data['description'],
-                'arch': data['arch']
-            }
+            extra=extra_data
         )
 
     def _to_size(self, data):
@@ -93,68 +129,168 @@ class BrightboxNodeDriver(NodeDriver):
 
     def _post(self, path, data={}):
         headers = {'Content-Type': 'application/json'}
+        return self.connection.request(path, data=data, headers=headers,
+                                       method='POST')
 
-        return self.connection.request(path, data=data, headers=headers, method='POST')
+    def _put(self, path, data={}):
+        headers = {'Content-Type': 'application/json'}
+        return self.connection.request(path, data=data, headers=headers,
+                                       method='PUT')
 
     def create_node(self, **kwargs):
+        """Create a new Brightbox node
+
+        See L{NodeDriver.create_node} for more keyword args.
+        Reference: https://api.gb1.brightbox.com/1.0/#server_create_server
+
+        @keyword    ex_userdata: User data
+        @type       ex_userdata: C{str}
+
+        @keyword    ex_servergroup: Name or list of server group ids to
+                                    add server to
+        @type       ex_servergroup: C{str} or C{list} of C{str}s
+        """
         data = {
             'name': kwargs['name'],
             'server_type': kwargs['size'].id,
             'image': kwargs['image'].id,
-            'user_data': ''
         }
 
+        if 'ex_userdata' in kwargs:
+            data['user_data'] = base64.b64encode(b(kwargs['ex_userdata'])) \
+                                      .decode('ascii')
+
         if 'location' in kwargs:
             data['zone'] = kwargs['location'].id
-        else:
-            data['zone'] = ''
 
-        data = self._post('/%s/servers' % API_VERSION, data).object
+        if 'ex_servergroup' in kwargs:
+            if not isinstance(kwargs['ex_servergroup'], list):
+                kwargs['ex_servergroup'] = [kwargs['ex_servergroup']]
+            data['server_groups'] = kwargs['ex_servergroup']
 
+        data = self._post('/%s/servers' % self.api_version, data).object
         return self._to_node(data)
 
     def destroy_node(self, node):
-        response = self.connection.request('/%s/servers/%s' % (API_VERSION, node.id), method='DELETE')
-
+        """
+        Destroy node by passing in the node object
+        """
+        response = self.connection.request('/%s/servers/%s' % \
+                   (self.api_version, node.id), method='DELETE')
         return response.status == httplib.ACCEPTED
 
     def list_nodes(self):
-        data = self.connection.request('/%s/servers' % API_VERSION).object
-
+        data = self.connection.request('/%s/servers' % self.api_version).object
         return list(map(self._to_node, data))
 
     def list_images(self):
-        data = self.connection.request('/%s/images' % API_VERSION).object
-
+        data = self.connection.request('/%s/images' % self.api_version).object
         return list(map(self._to_image, data))
 
     def list_sizes(self):
-        data = self.connection.request('/%s/server_types' % API_VERSION).object
-
+        data = self.connection.request('/%s/server_types' % self.api_version) \
+                              .object
         return list(map(self._to_size, data))
 
     def list_locations(self):
-        data = self.connection.request('/%s/zones' % API_VERSION).object
-
+        data = self.connection.request('/%s/zones' % self.api_version).object
         return list(map(self._to_location, data))
 
     def ex_list_cloud_ips(self):
-        return self.connection.request('/%s/cloud_ips' % API_VERSION).object
+        """
+        List Cloud IPs
 
-    def ex_create_cloud_ip(self):
-        return self._post('/%s/cloud_ips' % API_VERSION).object
+        @note: This is an API extension for use on Brightbox
 
-    def ex_map_cloud_ip(self, cloud_ip_id, interface_id):
-        response = self._post('/%s/cloud_ips/%s/map' % (API_VERSION, cloud_ip_id), {'interface': interface_id})
+        @return: C{list} of C{dict}
+        """
+        return self.connection.request('/%s/cloud_ips' % self.api_version) \
+                              .object
+
+    def ex_create_cloud_ip(self, reverse_dns=None):
+        """
+        Requests a new cloud IP address for the account
+
+        @note: This is an API extension for use on Brightbox
+
+        @param      reverse_dns: Reverse DNS hostname
+        @type       reverse_dns: C{str}
+
+        @return: C{dict}
+        """
+        params = {}
+
+        if reverse_dns:
+            params['reverse_dns'] = reverse_dns
+
+        return self._post('/%s/cloud_ips' % self.api_version, params).object
+
+    def ex_update_cloud_ip(self, cloud_ip_id, reverse_dns):
+        """
+        Update some details of the cloud IP address
 
+        @note: This is an API extension for use on Brightbox
+
+        @param  cloud_ip_id: The id of the cloud ip.
+        @type   cloud_ip_id: C{str}
+
+        @param      reverse_dns: Reverse DNS hostname
+        @type       reverse_dns: C{str}
+
+        @return: C{dict}
+        """
+        response = self._put('/%s/cloud_ips/%s' % (self.api_version,
+            cloud_ip_id), {'reverse_dns': reverse_dns})
+        return response.status == httplib.OK
+
+    def ex_map_cloud_ip(self, cloud_ip_id, interface_id):
+        """
+        Maps (or points) a cloud IP address at a server's interface
+        or a load balancer to allow them to respond to public requests
+
+        @note: This is an API extension for use on Brightbox
+
+        @param  cloud_ip_id: The id of the cloud ip.
+        @type   cloud_ip_id: C{str}
+
+        @param  interface_id: The Interface ID or LoadBalancer ID to
+                              which this Cloud IP should be mapped to
+        @type   interface_id: C{str}
+
+        @return: C{bool} True if the mapping was successful.
+        """
+        response = self._post('/%s/cloud_ips/%s/map' % (self.api_version,
+            cloud_ip_id), {'destination': interface_id})
         return response.status == httplib.ACCEPTED
 
     def ex_unmap_cloud_ip(self, cloud_ip_id):
-        response = self._post('/%s/cloud_ips/%s/unmap' % (API_VERSION, cloud_ip_id))
-
+        """
+        Unmaps a cloud IP address from its current destination making
+        it available to remap. This remains in the account's pool
+        of addresses
+
+        @note: This is an API extension for use on Brightbox
+
+        @param  cloud_ip_id: The id of the cloud ip.
+        @type   cloud_ip_id: C{str}
+
+        @return: C{bool} True if the unmap was successful.
+        """
+        response = self._post('/%s/cloud_ips/%s/unmap' % (self.api_version,
+                                                          cloud_ip_id))
         return response.status == httplib.ACCEPTED
 
     def ex_destroy_cloud_ip(self, cloud_ip_id):
-        response = self.connection.request('/%s/cloud_ips/%s' % (API_VERSION, cloud_ip_id), method='DELETE')
+        """
+        Release the cloud IP address from the account's ownership
+
+        @note: This is an API extension for use on Brightbox
+
+        @param  cloud_ip_id: The id of the cloud ip.
+        @type   cloud_ip_id: C{str}
 
+        @return: C{bool} True if the unmap was successful.
+        """
+        response = self.connection.request('/%s/cloud_ips/%s' %
+                           (self.api_version, cloud_ip_id), method='DELETE')
         return response.status == httplib.OK

Added: libcloud/trunk/test/compute/fixtures/brightbox/create_cloud_ip.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/create_cloud_ip.json?rev=1325611&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/create_cloud_ip.json (added)
+++ libcloud/trunk/test/compute/fixtures/brightbox/create_cloud_ip.json Fri Apr 13 04:30:01 2012
@@ -0,0 +1,19 @@
+{
+    "account": {
+        "id": "acc-tqs4c", 
+        "name": "bbctest", 
+        "resource_type": "account", 
+        "status": "active", 
+        "url": "https://api.gb1.brightbox.com/1.0/account"
+    }, 
+    "id": "cip-jsjc5", 
+    "interface": null, 
+    "load_balancer": null, 
+    "public_ip": "109.107.37.234", 
+    "resource_type": "cloud_ip", 
+    "reverse_dns": "cip-109-107-37-234.gb1.brightbox.com", 
+    "server": null, 
+    "server_group": null, 
+    "status": "unmapped", 
+    "url": "https://api.gb1.brightbox.com/1.0/cloud_ips/cip-jsjc5"
+}

Added: libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_a.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_a.json?rev=1325611&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_a.json (added)
+++ libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_a.json Fri Apr 13 04:30:01 2012
@@ -0,0 +1,76 @@
+{
+    "account": {
+        "id": "acc-tqs4c", 
+        "name": "bbctest", 
+        "resource_type": "account", 
+        "status": "active", 
+        "url": "https://api.gb1.brightbox.com/1.0/account"
+    }, 
+    "cloud_ips": [], 
+    "console_token": null, 
+    "console_token_expires": null, 
+    "console_url": null, 
+    "created_at": "2012-04-12T10:58:49Z", 
+    "deleted_at": null, 
+    "fqdn": "srv-p61uj.gb1.brightbox.com", 
+    "hostname": "srv-p61uj", 
+    "id": "srv-p61uj", 
+    "image": {
+        "arch": "x86_64", 
+        "created_at": "2012-04-11T01:36:19Z", 
+        "description": "Standard server with cloud-init", 
+        "id": "img-n4yek", 
+        "name": "Ubuntu Precise 12.04 LTS server", 
+        "owner": "acc-tqs4c", 
+        "resource_type": "image", 
+        "source": "precise-amd64-17903.gz", 
+        "status": "deprecated", 
+        "url": "https://api.gb1.brightbox.com/1.0/images/img-n4yek", 
+        "username": "ubuntu"
+    }, 
+    "interfaces": [
+        {
+            "id": "int-ctud9", 
+            "ipv4_address": "10.240.156.30", 
+            "ipv6_address": "2a02:1348:14c:2707:24:19ff:fef0:9c1e", 
+            "mac_address": "02:24:19:f0:9c:1e", 
+            "resource_type": "interface", 
+            "url": "https://api.gb1.brightbox.com/1.0/interfaces/int-ctud9"
+        }
+    ], 
+    "name": "Test Node", 
+    "resource_type": "server", 
+    "server_groups": [
+        {
+            "created_at": "2011-08-24T08:41:56Z", 
+            "default": true, 
+            "description": "All new servers are added to this group unless specified otherwise.", 
+            "id": "grp-irgkb", 
+            "name": "default", 
+            "resource_type": "server_group", 
+            "url": "https://api.gb1.brightbox.com/1.0/server_groups/grp-irgkb"
+        }
+    ], 
+    "server_type": {
+        "cores": 2, 
+        "disk_size": 20480, 
+        "handle": "nano", 
+        "id": "typ-4nssg", 
+        "name": "Brightbox Nano Instance", 
+        "ram": 512, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-4nssg"
+    }, 
+    "snapshots": [], 
+    "started_at": null, 
+    "status": "creating", 
+    "url": "https://api.gb1.brightbox.com/1.0/servers/srv-p61uj", 
+    "user_data": null, 
+    "zone": {
+        "handle": "gb1-a", 
+        "id": "zon-6mxqw", 
+        "resource_type": "zone", 
+        "url": "https://api.gb1.brightbox.com/1.0/zones/zon-6mxqw"
+    }
+}

Added: libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_b.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_b.json?rev=1325611&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_b.json (added)
+++ libcloud/trunk/test/compute/fixtures/brightbox/create_server_gb1_b.json Fri Apr 13 04:30:01 2012
@@ -0,0 +1,76 @@
+{
+    "account": {
+        "id": "acc-tqs4c", 
+        "name": "bbctest", 
+        "resource_type": "account", 
+        "status": "active", 
+        "url": "https://api.gb1.brightbox.com/1.0/account"
+    }, 
+    "cloud_ips": [], 
+    "console_token": null, 
+    "console_token_expires": null, 
+    "console_url": null, 
+    "created_at": "2012-04-12T11:01:51Z", 
+    "deleted_at": null, 
+    "fqdn": "srv-nnumd.gb1.brightbox.com", 
+    "hostname": "srv-nnumd", 
+    "id": "srv-nnumd", 
+    "image": {
+        "arch": "x86_64", 
+        "created_at": "2012-04-11T01:36:19Z", 
+        "description": "Standard server with cloud-init", 
+        "id": "img-n4yek", 
+        "name": "Ubuntu Precise 12.04 LTS server", 
+        "owner": "acc-tqs4c", 
+        "resource_type": "image", 
+        "source": "precise-amd64-17903.gz", 
+        "status": "deprecated", 
+        "url": "https://api.gb1.brightbox.com/1.0/images/img-n4yek", 
+        "username": "ubuntu"
+    }, 
+    "interfaces": [
+        {
+            "id": "int-2chhk", 
+            "ipv4_address": "10.232.142.194", 
+            "ipv6_address": "2a02:1348:14d:23b0:24:19ff:fee8:8ec2", 
+            "mac_address": "02:24:19:e8:8e:c2", 
+            "resource_type": "interface", 
+            "url": "https://api.gb1.brightbox.com/1.0/interfaces/int-2chhk"
+        }
+    ], 
+    "name": "Test Node", 
+    "resource_type": "server", 
+    "server_groups": [
+        {
+            "created_at": "2011-08-24T08:41:56Z", 
+            "default": true, 
+            "description": "All new servers are added to this group unless specified otherwise.", 
+            "id": "grp-irgkb", 
+            "name": "default", 
+            "resource_type": "server_group", 
+            "url": "https://api.gb1.brightbox.com/1.0/server_groups/grp-irgkb"
+        }
+    ], 
+    "server_type": {
+        "cores": 2, 
+        "disk_size": 20480, 
+        "handle": "nano", 
+        "id": "typ-4nssg", 
+        "name": "Brightbox Nano Instance", 
+        "ram": 512, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-4nssg"
+    }, 
+    "snapshots": [], 
+    "started_at": null, 
+    "status": "creating", 
+    "url": "https://api.gb1.brightbox.com/1.0/servers/srv-nnumd", 
+    "user_data": null, 
+    "zone": {
+        "handle": "gb1-b", 
+        "id": "zon-remk1", 
+        "resource_type": "zone", 
+        "url": "https://api.gb1.brightbox.com/1.0/zones/zon-remk1"
+    }
+}

Added: libcloud/trunk/test/compute/fixtures/brightbox/list_cloud_ips.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/list_cloud_ips.json?rev=1325611&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/list_cloud_ips.json (added)
+++ libcloud/trunk/test/compute/fixtures/brightbox/list_cloud_ips.json Fri Apr 13 04:30:01 2012
@@ -0,0 +1,96 @@
+[
+    {
+        "account": {
+            "id": "acc-tqs4c", 
+            "name": "bbctest", 
+            "resource_type": "account", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/account"
+        }, 
+        "id": "cip-tlrp3", 
+        "interface": null, 
+        "load_balancer": null, 
+        "public_ip": "109.107.35.16", 
+        "resource_type": "cloud_ip", 
+        "reverse_dns": "cip-109-107-35-16.gb1.brightbox.com", 
+        "server": null, 
+        "server_group": null, 
+        "status": "unmapped", 
+        "url": "https://api.gb1.brightbox.com/1.0/cloud_ips/cip-tlrp3"
+    }, 
+    {
+        "account": {
+            "id": "acc-tqs4c", 
+            "name": "bbctest", 
+            "resource_type": "account", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/account"
+        }, 
+        "id": "cip-w8vbt", 
+        "interface": null, 
+        "load_balancer": null, 
+        "public_ip": "109.107.35.76", 
+        "resource_type": "cloud_ip", 
+        "reverse_dns": "cip-109-107-35-76.gb1.brightbox.com", 
+        "server": null, 
+        "server_group": null, 
+        "status": "unmapped", 
+        "url": "https://api.gb1.brightbox.com/1.0/cloud_ips/cip-w8vbt"
+    }, 
+    {
+        "account": {
+            "id": "acc-tqs4c", 
+            "name": "bbctest", 
+            "resource_type": "account", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/account"
+        }, 
+        "id": "cip-uswab", 
+        "interface": {
+            "id": "int-ztqbx", 
+            "ipv4_address": "10.240.228.234", 
+            "ipv6_address": "2a02:1348:14c:393a:24:19ff:fef0:e4ea", 
+            "mac_address": "02:24:19:f0:e4:ea", 
+            "resource_type": "interface", 
+            "url": "https://api.gb1.brightbox.com/1.0/interfaces/int-ztqbx"
+        }, 
+        "load_balancer": null, 
+        "public_ip": "109.107.35.105", 
+        "resource_type": "cloud_ip", 
+        "reverse_dns": "cip-109-107-35-105.gb1.brightbox.com", 
+        "server": {
+            "created_at": "2012-01-30T14:42:28Z", 
+            "deleted_at": null, 
+            "fqdn": "srv-742vn.gb1.brightbox.com", 
+            "hostname": "srv-742vn", 
+            "id": "srv-742vn", 
+            "name": "Kernel builder", 
+            "resource_type": "server", 
+            "started_at": "2012-03-28T15:26:43Z", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/servers/srv-742vn"
+        }, 
+        "server_group": null, 
+        "status": "mapped", 
+        "url": "https://api.gb1.brightbox.com/1.0/cloud_ips/cip-uswab"
+    }, 
+    {
+        "account": {
+            "id": "acc-tqs4c", 
+            "name": "bbctest", 
+            "resource_type": "account", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/account"
+        }, 
+        "id": "cip-ui4n1", 
+        "interface": null, 
+        "load_balancer": null, 
+        "public_ip": "109.107.37.135", 
+        "resource_type": "cloud_ip", 
+        "reverse_dns": "cip-109-107-37-135.gb1.brightbox.com", 
+        "server": null, 
+        "server_group": null, 
+        "status": "unmapped", 
+        "url": "https://api.gb1.brightbox.com/1.0/cloud_ips/cip-ui4n1"
+    }
+]

Modified: libcloud/trunk/test/compute/fixtures/brightbox/list_images.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/list_images.json?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/list_images.json (original)
+++ libcloud/trunk/test/compute/fixtures/brightbox/list_images.json Fri Apr 13 04:30:01 2012
@@ -1,21 +1,89 @@
-[{"id": "img-9vxqi",
-  "url": "images/(image_id)",
-  "name": "Brightbox Lucid 32",
-  "status": "available",
-  "description": "Jeremy's debian ec2 image",
-  "source": "jeremy_debian-32_ec2",
-  "source_type": "upload",
-  "arch": "32-bit",
-  "created_at": "",
-  "owner": "acc-bright",
-  "ancestor":
-   {"id": "img-9vxqi",
-    "url": "images/(image_id)",
-    "name": "Brightbox Lucid 32",
-    "status": "available",
-    "description": "Jeremy's debian ec2 image",
-    "source": "jeremy_debian-32_ec2",
-    "source_type": "upload",
-    "arch": "32-bit",
-    "created_at": "",
-    "owner": "acc-bright"}}]
+[
+    {
+        "ancestor": {
+            "arch": "i686", 
+            "created_at": "2010-10-04T19:03:37Z", 
+            "description": "Creates a blank disk", 
+            "id": "img-6lybc", 
+            "name": "Blank Image", 
+            "owner": "acc-tqs4c", 
+            "resource_type": "image", 
+            "source": "blank_10G", 
+            "status": "deleted", 
+            "url": "https://api.gb1.brightbox.com/1.0/images/img-6lybc", 
+            "username": null
+        }, 
+        "arch": "i686", 
+        "compatibility_mode": false, 
+        "created_at": "2010-10-02T19:03:37Z", 
+        "description": "login: root using stored ssh key", 
+        "disk_size": 1086, 
+        "id": "img-99q79", 
+        "min_ram": null, 
+        "name": "CentOS 5.5 server", 
+        "official": true, 
+        "owner": "acc-tqs4c", 
+        "public": true, 
+        "resource_type": "image", 
+        "source": "srv-s4mfq", 
+        "source_type": "upload", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/images/img-99q79", 
+        "username": null, 
+        "virtual_size": 10244
+    }, 
+    {
+        "ancestor": {
+            "arch": "i686", 
+            "created_at": "2010-10-04T19:03:37Z", 
+            "description": "Creates a blank disk", 
+            "id": "img-6lybc", 
+            "name": "Blank Image", 
+            "owner": "acc-tqs4c", 
+            "resource_type": "image", 
+            "source": "blank_10G", 
+            "status": "deleted", 
+            "url": "https://api.gb1.brightbox.com/1.0/images/img-6lybc", 
+            "username": null
+        }, 
+        "arch": "x86_64", 
+        "compatibility_mode": false, 
+        "created_at": "2010-10-03T19:03:37Z", 
+        "description": "login: root using stored ssh key", 
+        "disk_size": 1133, 
+        "id": "img-pnqnc", 
+        "min_ram": null, 
+        "name": "CentOS 5.5 server", 
+        "official": true, 
+        "owner": "acc-tqs4c", 
+        "public": true, 
+        "resource_type": "image", 
+        "source": "srv-53fez", 
+        "source_type": "upload", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/images/img-pnqnc", 
+        "username": null, 
+        "virtual_size": 10240
+    }, 
+    {
+        "ancestor": null, 
+        "arch": "i686", 
+        "compatibility_mode": false, 
+        "created_at": "2012-01-22T05:36:24Z", 
+        "description": "Standard server with cloud-init", 
+        "disk_size": 671, 
+        "id": "img-joo06", 
+        "min_ram": null, 
+        "name": "Ubuntu Oneiric 11.10 server", 
+        "official": false, 
+        "owner": "acc-tqs4c", 
+        "public": true, 
+        "resource_type": "image", 
+        "source": "oneiric-i386-20178.gz", 
+        "source_type": "upload", 
+        "status": "deprecated", 
+        "url": "https://api.gb1.brightbox.com/1.0/images/img-joo06", 
+        "username": "ubuntu", 
+        "virtual_size": 1025
+    } 
+]

Modified: libcloud/trunk/test/compute/fixtures/brightbox/list_server_types.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/list_server_types.json?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/list_server_types.json (original)
+++ libcloud/trunk/test/compute/fixtures/brightbox/list_server_types.json Fri Apr 13 04:30:01 2012
@@ -1,8 +1,79 @@
-[{"id": "typ-4nssg",
-  "url": "server_types/typ-4nssg",
-  "handle": "nano",
-  "name": "Brightbox Nano Instance",
-  "status": "",
-  "cores": 1,
-  "ram": 512,
-  "disk_size": 10240}]
+[
+    {
+        "cores": 2, 
+        "disk_size": 20480, 
+        "handle": "nano", 
+        "id": "typ-4nssg", 
+        "name": "Brightbox Nano Instance", 
+        "ram": 512, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-4nssg"
+    }, 
+    {
+        "cores": 8, 
+        "disk_size": 163840, 
+        "handle": "medium", 
+        "id": "typ-qdiwq", 
+        "name": "Brightbox Medium Instance", 
+        "ram": 4096, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-qdiwq"
+    }, 
+    {
+        "cores": 4, 
+        "disk_size": 81920, 
+        "handle": "small", 
+        "id": "typ-urtky", 
+        "name": "Brightbox Small Instance", 
+        "ram": 2048, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-urtky"
+    }, 
+    {
+        "cores": 8, 
+        "disk_size": 327680, 
+        "handle": "large", 
+        "id": "typ-mlbt7", 
+        "name": "Brightbox Large Instance", 
+        "ram": 8192, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-mlbt7"
+    }, 
+    {
+        "cores": 4, 
+        "disk_size": 40960, 
+        "handle": "mini", 
+        "id": "typ-iqisj", 
+        "name": "Brightbox Mini Instance", 
+        "ram": 1024, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-iqisj"
+    }, 
+    {
+        "cores": 8, 
+        "disk_size": 655360, 
+        "handle": "xl", 
+        "id": "typ-wdicw", 
+        "name": "Brightbox XL Instance", 
+        "ram": 16384, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-wdicw"
+    }, 
+    {
+        "cores": 8, 
+        "disk_size": 1310720, 
+        "handle": "xxl", 
+        "id": "typ-lr76m", 
+        "name": "Brightbox XXL Instance", 
+        "ram": 32768, 
+        "resource_type": "server_type", 
+        "status": "available", 
+        "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-lr76m"
+    }
+]

Modified: libcloud/trunk/test/compute/fixtures/brightbox/list_servers.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/list_servers.json?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/list_servers.json (original)
+++ libcloud/trunk/test/compute/fixtures/brightbox/list_servers.json Fri Apr 13 04:30:01 2012
@@ -1,62 +1,155 @@
-[{"id": "srv-3a97e",
-  "url": "servers/(server_id)",
-  "name": "My web server",
-  "status": "active",
-  "hostname": "srv-3a97e.gb1.brightbox.com",
-  "created_at": "",
-  "deleted_at": "",
-  "started_at": "",
-  "account":
-   {"id": "acc-3jd8s",
-    "url": "accounts/(account_id)",
-    "name": "Brightbox Systems Ltd.",
-    "status": "verified",
-    "ram_limit": 20480,
-    "ram_used": 2048,
-    "limits_cloudips": 5},
-  "image":
-   {"id": "img-9vxqi",
-    "url": "images/(image_id)",
-    "name": "Brightbox Lucid 32",
-    "status": "available",
-    "description": "Jeremy's debian ec2 image",
-    "source": "jeremy_debian-32_ec2",
-    "source_type": "upload",
-    "arch": "32-bit",
-    "created_at": "",
-    "owner": "acc-bright"},
-  "server_type":
-   {"id": "typ-a97e6",
-    "url": "server_types/(server_type_id)",
-    "handle": "nano",
-    "name": "Brightbox Nano",
-    "status": "",
-    "cores": 2,
-    "ram": 2048,
-    "disk_size": ""},
-  "zone":
-   {"id": "zon-8ja0a",
-    "url": "zones/(zone_id)",
-    "handle": "gb1-a"},
-  "snapshots":
-   [{"id": "img-9vxqi",
-     "url": "images/(image_id)",
-     "name": "Brightbox Lucid 32",
-     "status": "available",
-     "description": "Jeremy's debian ec2 image",
-     "source": "jeremy_debian-32_ec2",
-     "source_type": "upload",
-     "arch": "32-bit",
-     "created_at": "",
-     "owner": "acc-bright"}],
-  "cloud_ips":
-   [{"id": "cip-ja8ub",
-     "url": "cloud_ips/(cloud_ip_id)",
-     "public_ip": "109.107.42.129",
-     "status": "mapped",
-     "reverse_dns": "cip-109-107-42-129.gb1.brightbox.com"}],
-  "interfaces":
-   [{"id": "int-mc3a9",
-     "url": "interfaces/(interface_id)",
-     "mac_address": "02:24:19:6e:18:36",
-     "ipv4_address": "10.110.24.54"}]}]
+
+[
+    {
+        "account": {
+            "id": "acc-tqs4c", 
+            "name": "bbctest", 
+            "resource_type": "account", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/account"
+        }, 
+        "cloud_ips": [
+            {
+                "id": "cip-tlrp3", 
+                "public_ip": "109.107.35.16", 
+                "resource_type": "cloud_ip", 
+                "reverse_dns": "cip-109-107-35-16.gb1.brightbox.com", 
+                "status": "mapped", 
+                "url": "https://api.gb1.brightbox.com/1.0/cloud_ips/cip-tlrp3"
+            }
+        ], 
+        "created_at": "2010-10-14T10:02:38Z", 
+        "deleted_at": null, 
+        "fqdn": "srv-xvpn7.gb1.brightbox.com", 
+        "hostname": "srv-xvpn7", 
+        "id": "srv-xvpn7", 
+        "image": {
+            "arch": "i686", 
+            "created_at": "2010-10-11T15:23:51Z", 
+            "description": "", 
+            "id": "img-arm8f", 
+            "name": "Snapshot of srv-vf2a4 11 Oct 15:23", 
+            "owner": "acc-tqs4c", 
+            "resource_type": "image", 
+            "source": "srv-vf2a4", 
+            "status": "deleted", 
+            "url": "https://api.gb1.brightbox.com/1.0/images/img-arm8f", 
+            "username": null
+        }, 
+        "interfaces": [
+            {
+                "id": "int-519up", 
+                "ipv4_address": "10.74.210.210", 
+                "mac_address": "02:24:19:4a:d2:d2", 
+                "resource_type": "interface", 
+                "url": "https://api.gb1.brightbox.com/1.0/interfaces/int-519up"
+            }
+        ], 
+        "name": "Ubuntu Image Builder Box", 
+        "resource_type": "server", 
+        "server_groups": [
+            {
+                "created_at": "2011-08-24T08:41:56Z", 
+                "default": true, 
+                "description": "All new servers are added to this group unless specified otherwise.", 
+                "id": "grp-irgkb", 
+                "name": "default", 
+                "resource_type": "server_group", 
+                "url": "https://api.gb1.brightbox.com/1.0/server_groups/grp-irgkb"
+            }
+        ], 
+        "server_type": {
+            "cores": 4, 
+            "disk_size": 81920, 
+            "handle": "small", 
+            "id": "typ-urtky", 
+            "name": "Brightbox Small Instance", 
+            "ram": 2048, 
+            "resource_type": "server_type", 
+            "status": "available", 
+            "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-urtky"
+        }, 
+        "snapshots": [], 
+        "started_at": "2012-03-28T15:25:56Z", 
+        "status": "active", 
+        "url": "https://api.gb1.brightbox.com/1.0/servers/srv-xvpn7", 
+        "zone": {
+            "handle": "gb1-a", 
+            "id": "zon-6mxqw", 
+            "resource_type": "zone", 
+            "url": "https://api.gb1.brightbox.com/1.0/zones/zon-6mxqw"
+        }
+    }, 
+    {
+        "account": {
+            "id": "acc-tqs4c", 
+            "name": "bbctest", 
+            "resource_type": "account", 
+            "status": "active", 
+            "url": "https://api.gb1.brightbox.com/1.0/account"
+        }, 
+        "cloud_ips": [], 
+        "created_at": "2012-01-30T14:42:28Z", 
+        "deleted_at": null, 
+        "fqdn": "srv-742vn.gb1.brightbox.com", 
+        "hostname": "srv-742vn", 
+        "id": "srv-742vn", 
+        "image": {
+            "arch": "x86_64", 
+            "created_at": "2012-01-30T13:25:09Z", 
+            "description": "", 
+            "id": "img-j93gd", 
+            "name": "Snapshot of srv-k0pug 30 Jan 13:25", 
+            "owner": "acc-tqs4c", 
+            "resource_type": "image", 
+            "source": "srv-k0pug", 
+            "status": "available", 
+            "url": "https://api.gb1.brightbox.com/1.0/images/img-j93gd", 
+            "username": null
+        }, 
+        "interfaces": [
+            {
+                "id": "int-ztqbx", 
+                "ipv4_address": "10.240.228.234", 
+                "ipv6_address": "2a02:1348:14c:393a:24:19ff:fef0:e4ea", 
+                "mac_address": "02:24:19:f0:e4:ea", 
+                "resource_type": "interface", 
+                "url": "https://api.gb1.brightbox.com/1.0/interfaces/int-ztqbx"
+            }
+        ], 
+        "name": "Kernel builder", 
+        "resource_type": "server", 
+        "server_groups": [
+            {
+                "created_at": "2011-08-24T08:41:56Z", 
+                "default": true, 
+                "description": "All new servers are added to this group unless specified otherwise.", 
+                "id": "grp-irgkb", 
+                "name": "default", 
+                "resource_type": "server_group", 
+                "url": "https://api.gb1.brightbox.com/1.0/server_groups/grp-irgkb"
+            }
+        ], 
+        "server_type": {
+            "cores": 8, 
+            "disk_size": 163840, 
+            "handle": "medium", 
+            "id": "typ-qdiwq", 
+            "name": "Brightbox Medium Instance", 
+            "ram": 4096, 
+            "resource_type": "server_type", 
+            "status": "available", 
+            "url": "https://api.gb1.brightbox.com/1.0/server_types/typ-qdiwq"
+        }, 
+        "snapshots": [], 
+        "started_at": "2012-03-28T15:26:43Z", 
+        "status": "active", 
+        "url": "https://api.gb1.brightbox.com/1.0/servers/srv-742vn", 
+        "zone": {
+            "handle": "gb1-a", 
+            "id": "zon-6mxqw", 
+            "resource_type": "zone", 
+            "url": "https://api.gb1.brightbox.com/1.0/zones/zon-6mxqw"
+        }
+    }
+]

Modified: libcloud/trunk/test/compute/fixtures/brightbox/list_zones.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/brightbox/list_zones.json?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/brightbox/list_zones.json (original)
+++ libcloud/trunk/test/compute/fixtures/brightbox/list_zones.json Fri Apr 13 04:30:01 2012
@@ -1,3 +1,14 @@
-[{"id": "zon-8ja0a",
-  "url": "zones/(zone_id)",
-  "handle": "gb1-a"}]
+[
+    {
+        "handle": "gb1-a", 
+        "id": "zon-6mxqw", 
+        "resource_type": "zone", 
+        "url": "https://api.gb1.brightbox.com/1.0/zones/zon-6mxqw"
+    }, 
+    {
+        "handle": "gb1-b", 
+        "id": "zon-remk1", 
+        "resource_type": "zone", 
+        "url": "https://api.gb1.brightbox.com/1.0/zones/zon-remk1"
+    }
+]

Modified: libcloud/trunk/test/compute/test_brightbox.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_brightbox.py?rev=1325611&r1=1325610&r2=1325611&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_brightbox.py (original)
+++ libcloud/trunk/test/compute/test_brightbox.py Fri Apr 13 04:30:01 2012
@@ -14,13 +14,17 @@
 # limitations under the License.
 import sys
 import unittest
-from libcloud.utils.py3 import httplib
+import base64
 
 try:
     import simplejson as json
 except ImportError:
     import json
 
+from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import b
+from libcloud.utils.py3 import u
+
 from libcloud.common.types import InvalidCredsError
 from libcloud.compute.drivers.brightbox import BrightboxNodeDriver
 from libcloud.compute.types import NodeState
@@ -30,6 +34,7 @@ from test.compute import TestCaseMixin
 from test.file_fixtures import ComputeFileFixtures
 from test.secrets import BRIGHTBOX_PARAMS
 
+USER_DATA = '#!/bin/sh\ntest_script.sh\n'
 
 class BrightboxTest(unittest.TestCase, TestCaseMixin):
     def setUp(self):
@@ -44,26 +49,97 @@ class BrightboxTest(unittest.TestCase, T
         BrightboxMockHttp.type = 'UNAUTHORIZED_CLIENT'
         self.assertRaises(InvalidCredsError, self.driver.list_nodes)
 
+    def test_invalid_api_version(self):
+        self.driver = BrightboxNodeDriver(*BRIGHTBOX_PARAMS, api_version='2.0')
+        self.assertRaises(Exception, self.driver.list_locations)
+
+    def test_other_host(self):
+        self.driver = BrightboxNodeDriver(*BRIGHTBOX_PARAMS, host='api.gbt.brightbox.com')
+        locations = self.driver.list_locations()
+        self.assertEqual(len(locations), 0)
+
     def test_list_nodes(self):
         nodes = self.driver.list_nodes()
-        self.assertEqual(len(nodes), 1)
-        self.assertTrue('109.107.42.129' in nodes[0].public_ips)
-        self.assertTrue('10.110.24.54' in nodes[0].private_ips)
+        self.assertEqual(len(nodes), 2)
+        self.assertEqual(len(nodes[0].public_ips), 1)
+        self.assertEqual(len(nodes[1].public_ips), 1)
+        self.assertEqual(len(nodes[0].private_ips), 1)
+        self.assertEqual(len(nodes[1].private_ips), 1)
+        self.assertTrue('109.107.35.16' in nodes[0].public_ips)
+        self.assertTrue('10.74.210.210' in nodes[0].private_ips)
+        self.assertTrue('10.240.228.234' in nodes[1].private_ips)
+        self.assertTrue('2a02:1348:14c:393a:24:19ff:fef0:e4ea' in nodes[1].public_ips)
         self.assertEqual(nodes[0].state, NodeState.RUNNING)
+        self.assertEqual(nodes[1].state, NodeState.RUNNING)
+
+    def test_list_node_extras(self):
+        nodes = self.driver.list_nodes()
+        self.assertFalse(nodes[0].size is None)
+        self.assertFalse(nodes[1].size is None)
+        self.assertFalse(nodes[0].image is None)
+        self.assertFalse(nodes[1].image is None)
+        self.assertEqual(nodes[0].image.id, 'img-arm8f')
+        self.assertEqual(nodes[0].size.id, 'typ-urtky')
+        self.assertEqual(nodes[1].image.id, 'img-j93gd')
+        self.assertEqual(nodes[1].size.id, 'typ-qdiwq')
+        self.assertEqual(nodes[0].extra['fqdn'],'srv-xvpn7.gb1.brightbox.com')
+        self.assertEqual(nodes[1].extra['fqdn'],'srv-742vn.gb1.brightbox.com')
+        self.assertEqual(nodes[0].extra['hostname'],'srv-xvpn7')
+        self.assertEqual(nodes[1].extra['hostname'],'srv-742vn')
+        self.assertEqual(nodes[0].extra['status'], 'active')
+        self.assertEqual(nodes[1].extra['status'], 'active')
+        self.assertTrue('interfaces' in nodes[0].extra)
+        self.assertTrue('zone' in nodes[0].extra)
+        self.assertTrue('snapshots' in nodes[0].extra)
+        self.assertTrue('server_groups' in nodes[0].extra)
+        self.assertTrue('started_at' in nodes[0].extra)
+        self.assertTrue('created_at' in nodes[0].extra)
+        self.assertFalse('deleted_at' in nodes[0].extra)
 
     def test_list_sizes(self):
         sizes = self.driver.list_sizes()
-        self.assertEqual(len(sizes), 1)
+        self.assertEqual(len(sizes), 7)
         self.assertEqual(sizes[0].id, 'typ-4nssg')
         self.assertEqual(sizes[0].name, 'Brightbox Nano Instance')
         self.assertEqual(sizes[0].ram, 512)
+        self.assertEqual(sizes[0].disk, 20480)
+        self.assertEqual(sizes[0].bandwidth, 0)
+        self.assertEqual(sizes[0].price, 0)
 
     def test_list_images(self):
         images = self.driver.list_images()
-        self.assertEqual(len(images), 1)
-        self.assertEqual(images[0].id, 'img-9vxqi')
-        self.assertEqual(images[0].name, 'Brightbox Lucid 32')
-        self.assertEqual(images[0].extra['arch'], '32-bit')
+        self.assertEqual(len(images), 3)
+        self.assertEqual(images[0].id, 'img-99q79')
+        self.assertEqual(images[0].name, 'CentOS 5.5 server')
+        self.assertTrue('ancestor' in images[0].extra)
+        self.assertFalse('licence_name' in images[0].extra)
+
+    def test_list_images_extras(self):
+        images = self.driver.list_images()
+        extra = images[-1].extra
+        self.assertEqual(extra['arch'], 'i686')
+        self.assertFalse(extra['compatibility_mode'])
+        self.assertEqual(extra['created_at'], '2012-01-22T05:36:24Z')
+        self.assertTrue('description' in extra)
+        self.assertEqual(extra['disk_size'], 671)
+        self.assertFalse('min_ram' in extra)
+        self.assertFalse(extra['official'])
+        self.assertEqual(extra['owner'], 'acc-tqs4c')
+        self.assertTrue(extra['public'])
+        self.assertEqual(extra['source'], 'oneiric-i386-20178.gz')
+        self.assertEqual(extra['source_type'], 'upload')
+        self.assertEqual(extra['status'], 'deprecated')
+        self.assertEqual(extra['username'], 'ubuntu')
+        self.assertEqual(extra['virtual_size'], 1025)
+        self.assertFalse('ancestor' in extra)
+        self.assertFalse('licence_name' in extra)
+
+    def test_list_locations(self):
+        locations = self.driver.list_locations()
+        self.assertEqual(locations[0].id, 'zon-6mxqw')
+        self.assertEqual(locations[0].name, 'gb1-a')
+        self.assertEqual(locations[1].id, 'zon-remk1')
+        self.assertEqual(locations[1].name, 'gb1-b')
 
     def test_reboot_node_response(self):
         node = self.driver.list_nodes()[0]
@@ -77,8 +153,79 @@ class BrightboxTest(unittest.TestCase, T
         size = self.driver.list_sizes()[0]
         image = self.driver.list_images()[0]
         node = self.driver.create_node(name='Test Node', image=image, size=size)
-        self.assertEqual('srv-3a97e', node.id)
+        self.assertEqual('srv-p61uj', node.id)
         self.assertEqual('Test Node', node.name)
+        self.assertEqual('gb1-a', node.extra['zone'].name)
+
+    def test_create_node_in_location(self):
+        size = self.driver.list_sizes()[0]
+        image = self.driver.list_images()[0]
+        location = self.driver.list_locations()[1]
+        node = self.driver.create_node(name='Test Node', image=image, size=size,location = location)
+        self.assertEqual('srv-nnumd', node.id)
+        self.assertEqual('Test Node', node.name)
+        self.assertEqual('gb1-b', node.extra['zone'].name)
+
+    def test_create_node_with_user_data(self):
+        size = self.driver.list_sizes()[0]
+        image = self.driver.list_images()[0]
+        node = self.driver.create_node(name='Test Node', image=image, size=size, ex_userdata=USER_DATA)
+        decoded = base64.b64decode(b(node.extra['user_data'])).decode('ascii')
+        self.assertEqual('gb1-a', node.extra['zone'].name)
+        self.assertEqual(USER_DATA, decoded)
+
+    def test_create_node_with_a_server_group(self):
+        size = self.driver.list_sizes()[0]
+        image = self.driver.list_images()[0]
+        node = self.driver.create_node(name='Test Node', image=image, size=size, ex_servergroup='grp-12345')
+        self.assertEqual('gb1-a', node.extra['zone'].name)
+        self.assertEqual(len(node.extra['server_groups']), 1)
+        self.assertEqual(node.extra['server_groups'][0]['id'], 'grp-12345')
+
+    def test_create_node_with_a_list_of_server_groups(self):
+        size = self.driver.list_sizes()[0]
+        image = self.driver.list_images()[0]
+        node = self.driver.create_node(name='Test Node', image=image, size=size, ex_servergroup=['grp-12345', 'grp-67890'])
+        self.assertEqual('gb1-a', node.extra['zone'].name)
+        self.assertEqual(len(node.extra['server_groups']), 2)
+        self.assertEqual(node.extra['server_groups'][0]['id'], 'grp-12345')
+        self.assertEqual(node.extra['server_groups'][1]['id'], 'grp-67890')
+
+    def test_list_cloud_ips(self):
+        cip_list = self.driver.ex_list_cloud_ips()
+        self.assertEqual(len(cip_list), 4)
+        self.assertEqual(cip_list[2]['status'], 'mapped')
+        cip_check = cip_list[0]
+        self.assertEqual(cip_check['id'], 'cip-tlrp3')
+        self.assertEqual(cip_check['public_ip'], '109.107.35.16')
+        self.assertEqual(cip_check['reverse_dns'], 'cip-109-107-35-16.gb1.brightbox.com')
+        self.assertEqual(cip_check['status'], 'unmapped')
+        self.assertTrue(cip_check['server'] is None)
+        self.assertTrue(cip_check['server_group'] is None)
+        self.assertTrue(cip_check['interface'] is None)
+        self.assertTrue(cip_check['load_balancer'] is None)
+
+    def test_create_cloud_ip(self):
+        cip = self.driver.ex_create_cloud_ip()
+        self.assertEqual(cip['id'], 'cip-jsjc5')
+        self.assertEqual(cip['reverse_dns'], 'cip-109-107-37-234.gb1.brightbox.com')
+
+    def test_create_cloud_ip_with_dns(self):
+        cip = self.driver.ex_create_cloud_ip('fred.co.uk')
+        self.assertEqual(cip['id'], 'cip-jsjc5')
+        self.assertEqual(cip['reverse_dns'], 'fred.co.uk')
+
+    def test_map_cloud_ip(self):
+        self.assertTrue(self.driver.ex_map_cloud_ip('cip-jsjc5', 'int-ztqbx'))
+
+    def test_unmap_cloud_ip(self):
+        self.assertTrue(self.driver.ex_unmap_cloud_ip('cip-jsjc5'))
+
+    def test_update_cloud_ip(self):
+        self.assertTrue(self.driver.ex_update_cloud_ip('cip-jsjc5', 'fred.co.uk'))
+
+    def test_destroy_cloud_ip(self):
+        self.assertTrue(self.driver.ex_destroy_cloud_ip('cip-jsjc5'))
 
 
 class BrightboxMockHttp(MockHttp):
@@ -105,14 +252,22 @@ class BrightboxMockHttp(MockHttp):
             return self.response(httplib.OK, self.fixtures.load('list_servers.json'))
         elif method == 'POST':
             body = json.loads(body)
+            encoded = base64.b64encode(b(USER_DATA)).decode('ascii')
 
-            node = json.loads(self.fixtures.load('create_server.json'))
-
+            if 'user_data' in body and body['user_data'] != encoded:
+                return self.response(httplib.BAD_REQUEST, '{"error_name":"dodgy user data", "errors": ["User data not encoded properly"]}')
+            if body.get('zone', '') == 'zon-remk1':
+                node = json.loads(self.fixtures.load('create_server_gb1_b.json'))
+            else:
+                node = json.loads(self.fixtures.load('create_server_gb1_a.json'))
             node['name'] = body['name']
-
+            if 'server_groups' in body:
+                node['server_groups'] = [{'id': x} for x in body['server_groups']]
+            if 'user_data' in body:
+                node['user_data'] = body['user_data']
             return self.response(httplib.ACCEPTED, json.dumps(node))
 
-    def _1_0_servers_srv_3a97e(self, method, url, body, headers):
+    def _1_0_servers_srv_xvpn7(self, method, url, body, headers):
         if method == 'DELETE':
             return self.response(httplib.ACCEPTED, '')
 
@@ -122,7 +277,45 @@ class BrightboxMockHttp(MockHttp):
 
     def _1_0_zones(self, method, url, body, headers):
         if method == 'GET':
-            return self.response(httplib.OK, self.fixtures.load('list_zones.json'))
+            if headers['Host'] == 'api.gbt.brightbox.com':
+                return self.response(httplib.OK, "{}")
+            else:
+                return self.response(httplib.OK, self.fixtures.load('list_zones.json'))
+    def _2_0_zones(self, method, url, body, headers):
+        return self.response(httplib.BAD_REQUEST, '{"error_name":"unrecognised_endpoint", "errors": ["The request was for an unrecognised API endpoint"]}')
+        
+    def _1_0_cloud_ips(self, method, url, body, headers):
+        if method == 'GET':
+            return self.response(httplib.OK, self.fixtures.load('list_cloud_ips.json'))
+        elif method == 'POST':
+            body = json.loads(body)
+            node = json.loads(self.fixtures.load('create_cloud_ip.json'))
+            if 'reverse_dns' in body:
+                node['reverse_dns'] = body['reverse_dns']
+            return self.response(httplib.ACCEPTED, json.dumps(node))
+
+    def _1_0_cloud_ips_cip_jsjc5(self, method, url, body, headers):
+        if method == 'DELETE':
+            return self.response(httplib.OK, '')
+        elif method == 'PUT':
+            body = json.loads(body)
+            if body.get('reverse_dns', None) == 'fred.co.uk':
+                return self.response(httplib.OK, '')
+            else:
+                return self.response(httplib.BAD_REQUEST, '{"error_name":"bad dns", "errors": ["Bad dns"]}')
+            
+    def _1_0_cloud_ips_cip_jsjc5_map(self, method, url, body, headers):
+        if method == 'POST':
+            body = json.loads(body)
+            if 'destination' in body:
+                return self.response(httplib.ACCEPTED, '')
+            else:
+                return self.response(httplib.BAD_REQUEST, '{"error_name":"bad destination", "errors": ["Bad destination"]}')
+
+    def _1_0_cloud_ips_cip_jsjc5_unmap(self, method, url, body, headers):
+        if method == 'POST':
+            return self.response(httplib.ACCEPTED, '')
+
 
     def response(self, status, body):
         return (status, body, {'content-type': 'application/json'}, httplib.responses[status])
@@ -130,3 +323,6 @@ class BrightboxMockHttp(MockHttp):
 
 if __name__ == '__main__':
     sys.exit(unittest.main())
+
+# vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=python
+