=== modified file 'libcloud/compute/base.py'
--- libcloud/compute/base.py	2011-10-17 15:20:59 +0000
+++ libcloud/compute/base.py	2011-10-21 21:47:58 +0000
@@ -219,7 +219,7 @@
     4
     """
 
-    def __init__(self, id, name, ram, disk, bandwidth, price, driver, cpu=None, vcpu=None):
+    def __init__(self, id, name, ram, disk, bandwidth, price, driver):
         self.id = str(id)
         self.name = name
         self.ram = ram
@@ -227,12 +227,10 @@
         self.bandwidth = bandwidth
         self.price = price
         self.driver = driver
-        self.cpu = cpu
-        self.vcpu = vcpu
 
     def __repr__(self):
-        return (('<NodeSize: id=%s name=%s ram=%s disk=%s bandwidth=%s '
-                 'price=%s driver=%s ...>')
+        return (('<NodeSize: id=%s, name=%s, ram=%s, disk=%s, bandwidth=%s, '
+                 'price=%s, driver=%s ...>')
                 % (self.id, self.name, self.ram, self.disk, self.bandwidth,
                    self.price, self.driver.name))
 
@@ -271,6 +269,33 @@
                 % (self.id, self.name, self.driver.name))
 
 
+class NodeNetwork(object):
+    """
+    A virtual network.
+
+    NodeNetwork objects are analogous to physical switches connecting 2 or more physical nodes together.
+
+    >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+    >>> driver = DummyNodeDriver(0)
+    >>> network = driver.list_networks()[0]
+    >>> network.name
+    'BlueNetwork'
+
+    Apart from name and id, there is no further standard information;
+    other parameters are stored in a driver specific "extra" variable
+    """
+
+    def __init__(self, id, name, driver, extra=None):
+        self.id = str(id)
+        self.name = name
+        self.driver = driver
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return (('<NodeNetwork: id=%s, name=%s, driver=%s ...>')
+                % (self.id, self.name, self.driver.name))
+
+
 class NodeLocation(object):
     """
     A physical location where nodes can be.
@@ -386,7 +411,8 @@
             'create_node not implemented for this driver')
 
     def destroy_node(self, node):
-        """Destroy a node.
+        """
+        Destroy a node.
 
         Depending upon the provider, this may destroy all data associated with
         the node, including backups.
@@ -436,6 +462,14 @@
         raise NotImplementedError(
             'list_locations not implemented for this driver')
 
+    def list_networks(self, location=None):
+        """
+        List virtual networks on a provider
+        @return: C{list} of L{NodeNetwork} objects
+        """
+        raise NotImplementedError(
+            'list_networks not implemented for this driver')
+
     def deploy_node(self, **kwargs):
         """
         Create a new node, and start deployment.

=== modified file 'libcloud/compute/drivers/opennebula.py'
--- libcloud/compute/drivers/opennebula.py	2011-10-20 18:32:22 +0000
+++ libcloud/compute/drivers/opennebula.py	2011-10-22 05:55:31 +0000
@@ -15,6 +15,9 @@
 # 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.
+"""
+OpenNebula Driver
+"""
 
 try:
     import simplejson as json
@@ -24,21 +27,24 @@
 from xml.etree import ElementTree as ET
 from base64 import b64encode
 import hashlib
-import sys
+import httplib
 
 from libcloud.compute.base import NodeState, NodeDriver, Node, NodeLocation
 from libcloud.common.base import ConnectionUserAndKey, Response
 from libcloud.compute.base import NodeImage, NodeSize
 from libcloud.common.types import InvalidCredsError
 from libcloud.compute.providers import Provider
-from libcloud.common.base import Response
 
 API_HOST = ''
 API_PORT = (4567, 443)
 API_SECURE = True
 DEFAULT_API_VERSION = '3.0'
 
+
 class OpenNebulaResponse(Response):
+    """
+    Response class for the OpenNebula driver.
+    """
 
     def success(self):
         i = int(self.status)
@@ -50,14 +56,15 @@
         return ET.XML(self.body)
 
     def parse_error(self):
-        if int(self.status) == 401:
+        if self.status == httplib.UNAUTHORIZED:
             raise InvalidCredsError(self.body)
+
         return self.body
 
 
 class OpenNebulaConnection(ConnectionUserAndKey):
     """
-    Connection class for the OpenNebula driver
+    Connection class for the OpenNebula driver.
     """
 
     host = API_HOST
@@ -67,35 +74,57 @@
 
     def add_default_headers(self, headers):
         pass_sha1 = hashlib.sha1(self.key).hexdigest()
-        headers['Authorization'] = ("Basic %s" % b64encode("%s:%s" %
+        headers['Authorization'] = ('Basic %s' % b64encode('%s:%s' %
                                                 (self.user_id, pass_sha1)))
         return headers
 
 
+class OpenNebulaNodeSize(NodeSize):
+
+    def __init__(self, id, name, ram, disk, bandwidth, price, driver,
+        cpu=None, vcpu=None):
+
+        self.id = str(id)
+        self.name = name
+        self.ram = ram
+        self.disk = disk
+        self.bandwidth = bandwidth
+        self.price = price
+        self.driver = driver
+        self.cpu = cpu
+        self.vcpu = vcpu
+
+    def __repr__(self):
+        return (('<NodeSize: id=%s, name=%s, ram=%s, disk=%s, bandwidth=%s, '
+                 'price=%s, driver=%s, cpu=%s ...>')
+                % (self.id, self.name, self.ram, self.disk, self.bandwidth,
+                   self.price, self.driver.name, self.cpu))
+
+
 class OpenNebulaNodeDriver(NodeDriver):
     """
-    OpenNebula node driver
+    OpenNebula node driver.
     """
 
     connectionCls = OpenNebulaConnection
+    name = 'OpenNebula'
     type = Provider.OPENNEBULA
-    name = 'OpenNebula'
 
     NODE_STATE_MAP = {
-        'pending': NodeState.PENDING,
-        'hold': NodeState.PENDING,
-        'prolog': NodeState.PENDING,
-        'running': NodeState.RUNNING,
-        'migrate': NodeState.PENDING,
-        'epilog': NodeState.TERMINATED,
-        'stopped': NodeState.TERMINATED,
-        'suspended': NodeState.PENDING,
-        'failed': NodeState.TERMINATED,
-        'unknown': NodeState.UNKNOWN,
-        'done': NodeState.TERMINATED,
+        'PENDING': NodeState.PENDING,
+        'HOLD': NodeState.PENDING,
+        'PROLOG': NodeState.PENDING,
+        'RUNNING': NodeState.RUNNING,
+        'MIGRATE': NodeState.PENDING,
+        'EPILOG': NodeState.TERMINATED,
+        'STOPPED': NodeState.TERMINATED,
+        'SUSPENDED': NodeState.PENDING,
+        'FAILED': NodeState.TERMINATED,
+        'UNKNOWN': NodeState.UNKNOWN,
+        'DONE': NodeState.TERMINATED,
     }
 
-    def __new__(cls, key, secret=None, api_version=DEFAULT_API_VERSION,
+    def __new__(cls, key, api_version=DEFAULT_API_VERSION,
                 **kwargs):
         if cls is OpenNebulaNodeDriver:
             if api_version == '1.4':
@@ -104,35 +133,50 @@
                 cls = OpenNebula_3_0_NodeDriver
             else:
                 raise NotImplementedError(
-                    "No OpenNebulaNodeDriver found for API version %s" %
-                    (api_version)
-                )
+                    "No OpenNebulaNodeDriver found for API version %s." %
+                    (api_version))
             return super(OpenNebulaNodeDriver, cls).__new__(cls)
 
-    def list_sizes(self, location=None):
-        return [
-          NodeSize(id=1,
-                   name="small",
-                   ram=None,
-                   disk=None,
-                   bandwidth=None,
-                   price=None,
-                   driver=self),
-          NodeSize(id=2,
-                   name="medium",
-                   ram=None,
-                   disk=None,
-                   bandwidth=None,
-                   price=None,
-                   driver=self),
-          NodeSize(id=3,
-                   name="large",
-                   ram=None,
-                   disk=None,
-                   bandwidth=None,
-                   price=None,
-                   driver=self),
-        ]
+    def create_node(self, **kwargs):
+        """
+        Create a new OpenNebula node.
+
+        See L{NodeDriver.create_node} for more keyword args.
+        """
+        compute = ET.Element('COMPUTE')
+
+        name = ET.SubElement(compute, 'NAME')
+        name.text = kwargs['name']
+
+        xml = ET.tostring(compute)
+        node = self.connection.request('/compute', method='POST',
+                                       data=xml).object
+
+        return self._to_node(node)
+
+    def destroy_node(self, node):
+        url = '/compute/%s' % (str(node.id))
+        resp = self.connection.request(url, method='DELETE')
+
+        return resp.status == httplib.NO_CONTENT
+
+    def reboot_node(self, node):
+        url = '/compute/%s' % (str(compute_id))
+        resp1 = self.connection.request(url, method='PUT',
+                                        data=self._xml_action(compute_id,
+                                                              'STOPPED'))
+
+        if resp1.status == httplib.BAD_REQUEST:
+            return False
+
+        resp2 = self.connection.request(url, method='PUT',
+                                        data=self._xml_action(compute_id,
+                                        'RESUME'))
+
+        if resp2.status == httplib.BAD_REQUEST:
+            return False
+
+        return True
 
     def list_nodes(self):
         return self._to_nodes(self.connection.request('/compute').object)
@@ -140,103 +184,149 @@
     def list_images(self, location=None):
         return self._to_images(self.connection.request('/storage').object)
 
+    def list_sizes(self, location=None):
+        return [
+            OpenNebulaNodeSize(id=1,
+                name='small',
+                ram=None,
+                disk=None,
+                bandwidth=None,
+                price=None,
+                driver=self),
+            OpenNebulaNodeSize(id=2,
+                name='medium',
+                ram=None,
+                disk=None,
+                bandwidth=None,
+                price=None,
+                driver=self),
+            OpenNebulaNodeSize(id=3,
+                name='large',
+                ram=None,
+                disk=None,
+                bandwidth=None,
+                price=None,
+                driver=self),
+        ]
+
     def list_locations(self):
         return [NodeLocation(0,  'OpenNebula', 'ONE', self)]
 
-    def reboot_node(self, node):
-        compute_id = str(node.id)
-
-        url = '/compute/%s' % compute_id
-        resp1 = self.connection.request(url, method='PUT',
-                                        data=self._xml_action(compute_id,
-                                                              'STOPPED'))
-
-        if resp1.status == 400:
-            return False
-
-        resp2 = self.connection.request(url, method='PUT',
-                                        data=self._xml_action(compute_id,
-                                        'RESUME'))
-
-        if resp2.status == 400:
-            return False
-
-        return True
-
-    def destroy_node(self, node):
-        url = '/compute/%s' % (str(node.id))
-        resp = self.connection.request(url, method='DELETE')
-
-        return resp.status == 204
-
-    def create_node(self, **kwargs):
-        """Create a new OpenNebula node
-
-        See L{NodeDriver.create_node} for more keyword args.
-        """
-        compute = ET.Element('COMPUTE')
-
-        name = ET.SubElement(compute, 'NAME')
-        name.text = kwargs['name']
-
-        xml = ET.tostring(compute)
-        node = self.connection.request('/compute', method='POST',
-                                       data=xml).object
-
-        return self._to_node(node)
+    def list_networks(self, location=None):
+        return self._to_networks(self.connection.request('/network').object)
 
     def _to_images(self, object):
         images = []
-        for element in object.findall("DISK"):
-            image_id = element.attrib["href"].partition("/storage/")[2]
-            image = self.connection.request(("/storage/%s" % (
+        for element in object.findall('DISK'):
+            image_id = element.attrib['href'].partition('/storage/')[2]
+            imageElement = self.connection.request(('/storage/%s' % (
                                              image_id))).object
-            images.append(self._to_image(image))
+            images.append(self._to_image(imageElement))
 
         return images
 
-    def _to_image(self, image):
-        return NodeImage(id=image.findtext("ID"),
-                         name=image.findtext("NAME"),
-                         driver=self.connection.driver,
-                         extra={"size": image.findtext("SIZE"),
-                                "url": image.findtext("URL")})
+    def _to_image(self, element):
+        return NodeImage(id=element.findtext('ID'),
+                      name=element.findtext('NAME'),
+                      driver=self.connection.driver,
+                      extra={'size': element.findtext('SIZE'),
+                             'url': element.findtext('URL')})
+
+    def _to_networks(self, object):
+        networks = []
+        for element in object.findall('NETWORK'):
+            network_id = element.attrib['href'].partition('/network/')[2]
+            networkElement = self.connection.request(('/network/%s' % (
+                                             network_id))).object
+            networks.append(self._to_network(networkElement))
+
+        return networks
+
+    def _to_network(self, element):
+        return NodeNetwork(id=element.findtext('ID'),
+                      name=element.findtext('NAME'),
+                      driver=self.connection.driver,
+                      extra={'address': element.findtext('ADRESS'),
+                             'size': element.findtext('SIZE')})
 
     def _to_nodes(self, object):
         computes = []
-        for element in object.findall("COMPUTE"):
-            compute_id = element.attrib["href"].partition("/compute/")[2]
-            compute = self.connection.request(("/compute/%s" % (
+        for element in object.findall('COMPUTE'):
+            compute_id = element.attrib['href'].partition('/compute/')[2]
+            computeElement = self.connection.request(('/compute/%s' % (
                                                compute_id))).object
-            computes.append(self._to_node(compute))
+            computes.append(self._to_node(computeElement))
 
         return computes
 
-    def _extract_networks(self, compute):
-        networks = []
-
-        for element in compute.findall("NIC"):
-            ip = element.element.attrib.get('ip', None)
-
-            if ip is not None:
-                networks.append(ip)
-
-        return networks
-
     def _to_node(self, compute):
         try:
-            state = self.NODE_STATE_MAP[compute.findtext("STATE")]
+            state = self.NODE_STATE_MAP[compute.findtext('STATE')]
         except KeyError:
             state = NodeState.UNKNOWN
 
         networks = self._extract_networks(compute)
+        images = self._extract_images(compute)
+        nodeSizes = filter(lambda nodeSize:
+                          nodeSize.name == compute.findtext('INSTANCE_TYPE'),
+                          self.list_sizes())
+        if nodeSizes:
+            nodeSize = nodeSizes[0]
+        else:
+            nodeSize = None
 
-        return Node(id=compute.findtext("ID"),
-                    name=compute.findtext("NAME"),
+        return Node(id=compute.findtext('ID'),
+                    name=compute.findtext('NAME'),
                     state=state,
-                    public_ip=networks,
+                    public_ip=[],
                     private_ip=[],
-                    driver=self.connection.driver)
+                    driver=self.connection.driver,
+                    size=nodeSize,
+                    image=images,
+                    extra={'networks': networks})
+
+    def _extract_networks(self, compute):
+        networks = []
+
+        networkList = compute.find('NETWORK')
+        for element in networkList.findall('NIC'):
+            networks.append(
+                NodeNetwork(id=element.attrib['network'],
+                    name=None,
+                    driver=self.connection.driver,
+                    extra={'address': element.attrib.get('ip', None)}))
+
+        return networks
+
+    def _extract_images(self, compute):
+        images = []
+
+        diskList = compute.find('DISKS')
+        for element in diskList.findall('DISK'):
+            networks.append(
+                NodeImage(id=element.attrib['image'],
+                    name=None,
+                    driver=self.connection.driver,
+                    extra={'dev': element.attrib.get('dev', None)}))
+
+        for element in diskList.findall('SWAP'):
+            networks.append(
+                NodeImage(id=None,
+                    name=None,
+                    driver=self.connection.driver,
+                    extra={'dev': element.attrib.get('dev', None),
+                           'size': element.attrib.get('size', None)}))
+
+        for element in diskList.findall('FS'):
+            networks.append(
+                NodeImage(id=None,
+                    name=None,
+                    driver=self.connection.driver,
+                    extra={'dev': element.attrib.get('dev', None),
+                           'format': element.attrib.get('format', None),
+                           'size': element.attrib.get('size', None)}))
+
+        return images
 
     def _xml_action(self, compute_id, action):
         compute = ET.Element('COMPUTE')
@@ -252,38 +342,70 @@
 
 
 class OpenNebula_1_4_NodeDriver(OpenNebulaNodeDriver):
+    """
+    OpenNebula v1.4 node driver.
+    """
+
     pass
 
 
 class OpenNebula_3_0_NodeDriver(OpenNebulaNodeDriver):
+    """
+    OpenNebula v3.0 node driver.
+    """
+
+    def create_node(self, **kwargs):
+        """
+        Create a new OpenNebula node.
+
+        See L{NodeDriver.create_node} for more keyword args.
+        """
+        compute = ET.Element('COMPUTE')
+
+        name = ET.SubElement(compute, 'NAME')
+        name.text = kwargs['name']
+
+        instance_type = ET.SubElement(compute, 'INSTANCE_TYPE')
+        instance_type.text = kwargs['size'].name
+
+        disk = ET.SubElement(compute, 'DISK')
+        storage = ET.SubElement(disk, 'STORAGE', {'href': '/storage/%s' %
+                                                  (str(kwargs['image'].id))})
+
+        xml = ET.tostring(compute)
+        node = self.connection.request('/compute', method='POST',
+                                       data=xml).object
+
+        return self._to_node(node)
+
     def list_sizes(self, location=None):
         return [
-          NodeSize(id=1,
-                   name="small",
+          OpenNebulaNodeSize(id=1,
+                   name='small',
                    ram=1024,
                    cpu=1,
                    disk=None,
                    bandwidth=None,
                    price=None,
                    driver=self),
-          NodeSize(id=2,
-                   name="medium",
+          OpenNebulaNodeSize(id=2,
+                   name='medium',
                    ram=4096,
                    cpu=4,
                    disk=None,
                    bandwidth=None,
                    price=None,
                    driver=self),
-          NodeSize(id=3,
-                   name="large",
+          OpenNebulaNodeSize(id=3,
+                   name='large',
                    ram=8192,
                    cpu=8,
                    disk=None,
                    bandwidth=None,
                    price=None,
                    driver=self),
-          NodeSize(id=4,
-                   name="custom",
+          OpenNebulaNodeSize(id=4,
+                   name='custom',
                    ram=0,
                    cpu=0,
                    disk=None,
@@ -292,52 +414,57 @@
                    driver=self),
         ]
 
-    def create_node(self, **kwargs):
-        """Create a new OpenNebula node
-
-        See L{NodeDriver.create_node} for more keyword args.
-        """
-        compute = ET.Element('COMPUTE')
-
-        name = ET.SubElement(compute, 'NAME')
-        name.text = kwargs['name']
-
-        instance_type = ET.SubElement(compute, 'INSTANCE_TYPE')
-        instance_type.text = kwargs['size'].name
-
-        disk = ET.SubElement(compute, 'DISK')
-        storage = ET.SubElement(disk, 'STORAGE', {'href': '/storage/%s' %
-                                                  (str(kwargs['image'].id))})
-
-        xml = ET.tostring(compute)
-        node = self.connection.request('/compute', method='POST',
-                                       data=xml).object
-
-        return self._to_node(node)
-
     def _to_images(self, object):
         images = []
-        for element in object.findall("STORAGE"):
-            image_id = element.attrib["href"].partition("/storage/")[2]
-            image = self.connection.request(("/storage/%s" %
+        for element in object.findall('STORAGE'):
+            image_id = element.attrib['href'].partition('/storage/')[2]
+            imageElement = self.connection.request(('/storage/%s' %
                                              (image_id))).object
-            images.append(self._to_image(image))
+            images.append(self._to_image(imageElement))
 
         return images
 
-    def _to_image(self, image):
-        return NodeImage(id=image.findtext("ID"),
-                         name=image.findtext("NAME"),
-                         driver=self.connection.driver,
-                         extra={"description": image.findtext("DESCRIPTION"),
-                                "TYPE": image.findtext("TYPE"),
-                                "size": image.findtext("SIZE"),
-                                "fstype": image.findtext("FSTYPE", None)})
+    def _to_image(self, element):
+        return NodeImage(id=element.findtext('ID'),
+                      name=element.findtext('NAME'),
+                      driver=self.connection.driver,
+                      extra={'description': element.findtext('DESCRIPTION'),
+                             'type': element.findtext('TYPE'),
+                             'size': element.findtext('SIZE'),
+                             'fstype': element.findtext('FSTYPE', None)})
 
     def _extract_networks(self, compute):
         networks = []
-        for element in compute.findall("NIC"):
-            for ip in element.findall("IP"):
-                networks.append(ip)
+
+        for element in compute.findall('NIC'):
+            network = element.find('NETWORK')
+            network_id = network.attrib['href'].partition('/network/')[2]
+
+            ips = []
+            for ip in element.findall('IP'):
+                ips.append(ip)
+
+            networks.append(
+                NodeNetwork(id=network_id,
+                         name=network.attrib['name'],
+                         driver=self.connection.driver,
+                         extra={'address': ips,
+                                'mac': element.findtext('MAC')}))
 
         return networks
+
+    def _extract_images(self, compute):
+        images = []
+
+        for element in compute.findall('DISK'):
+            storage = element.find('STORAGE')
+            storage_id = storage.attrib['href'].partition('/storage/')[2]
+
+            images.append(
+                NodeImage(id=storage_id,
+                    name=storage.attrib['name'],
+                    driver=self.connection.driver,
+                    extra={'type': element.findtext('TYPE'),
+                           'target': element.findtext('TARGET')}))
+
+        return images

=== modified file 'libcloud/compute/drivers/openstack.py'
--- libcloud/compute/drivers/openstack.py	2011-10-20 21:09:25 +0000
+++ libcloud/compute/drivers/openstack.py	2011-10-20 23:37:39 +0000
@@ -23,9 +23,9 @@
 
 import os
 
+import warnings
+import httplib
 import base64
-import httplib
-import warnings
 
 from xml.etree import ElementTree as ET
 
@@ -49,30 +49,12 @@
     'OpenStackNodeDriver'
     ]
 
-
 ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
 
-OPENSTACK_NODE_STATE_MAP = {'BUILD': NodeState.PENDING,
-                            'REBUILD': NodeState.PENDING,
-                            'ACTIVE': NodeState.RUNNING,
-                            'SUSPENDED': NodeState.TERMINATED,
-                            'QUEUE_RESIZE': NodeState.PENDING,
-                            'PREP_RESIZE': NodeState.PENDING,
-                            'VERIFY_RESIZE': NodeState.RUNNING,
-                            'PASSWORD': NodeState.PENDING,
-                            'RESCUE': NodeState.PENDING,
-                            'REBUILD': NodeState.PENDING,
-                            'REBOOT': NodeState.REBOOTING,
-                            'HARD_REBOOT': NodeState.REBOOTING,
-                            'SHARE_IP': NodeState.PENDING,
-                            'SHARE_IP_NO_CONFIG': NodeState.PENDING,
-                            'DELETE_IP': NodeState.PENDING,
-                            'UNKNOWN': NodeState.UNKNOWN}
-
 DEFAULT_API_VERSION = '1.1'
 
 
-class OpenStack_Response(Response):
+class OpenStackResponse(Response):
 
     node_driver = None
 
@@ -152,9 +134,35 @@
 
 
 class OpenStackNodeDriver(NodeDriver):
-
-    def __new__(cls, key, secret=None, secure=True, host=None, port=None,
-                 api_version=DEFAULT_API_VERSION, **kwargs):
+    """
+    OpenStack node driver.
+    """
+
+    connectionCls = OpenStackComputeConnection
+    name = 'OpenStack'
+    type = Provider.OPENSTACK
+
+    NODE_STATE_MAP = {
+        'BUILD': NodeState.PENDING,
+        'REBUILD': NodeState.PENDING,
+        'ACTIVE': NodeState.RUNNING,
+        'SUSPENDED': NodeState.TERMINATED,
+        'QUEUE_RESIZE': NodeState.PENDING,
+        'PREP_RESIZE': NodeState.PENDING,
+        'VERIFY_RESIZE': NodeState.RUNNING,
+        'PASSWORD': NodeState.PENDING,
+        'RESCUE': NodeState.PENDING,
+        'REBUILD': NodeState.PENDING,
+        'REBOOT': NodeState.REBOOTING,
+        'HARD_REBOOT': NodeState.REBOOTING,
+        'SHARE_IP': NodeState.PENDING,
+        'SHARE_IP_NO_CONFIG': NodeState.PENDING,
+        'DELETE_IP': NodeState.PENDING,
+        'UNKNOWN': NodeState.UNKNOWN
+    }
+
+    def __new__(cls, key, api_version=DEFAULT_API_VERSION,
+                **kwargs):
         if cls is OpenStackNodeDriver:
             if api_version == '1.0':
                 cls = OpenStack_1_0_NodeDriver
@@ -167,6 +175,31 @@
                 )
         return super(OpenStackNodeDriver, cls).__new__(cls)
 
+    def destroy_node(self, node):
+        uri = '/servers/%s' % (node.id)
+        resp = self.connection.request(uri, method='DELETE')
+        # The OpenStack and Rackspace documentation both say this API will
+        # return a 204, but in-fact, everyone everywhere agrees it actually
+        # returns a 202, so we are going to accept either, and someday,
+        # someone will fix either the implementation or the documentation to
+        # agree.
+        return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
+
+    def reboot_node(self, node):
+        return self._reboot_node(node, reboot_type='HARD')
+
+    def list_nodes(self):
+        return self._to_nodes(self.connection.request('/servers/detail')
+                                             .object)
+
+    def list_images(self, location=None, ex_only_active=True):
+        return self._to_images(self.connection.request('/images/detail')
+                                              .object, ex_only_active)
+
+    def list_sizes(self, location=None):
+        return self._to_sizes(self.connection.request('/flavors/detail')
+                                             .object)
+
     def _ex_connection_class_kwargs(self):
         rv = {}
         if self._ex_force_base_url:
@@ -177,18 +210,6 @@
             rv['ex_force_auth_version'] = self._ex_force_auth_version
         return rv
 
-    def list_nodes(self):
-        return self._to_nodes(self.connection.request('/servers/detail')
-                                             .object)
-
-    def list_sizes(self, location=None):
-        return self._to_sizes(self.connection.request('/flavors/detail')
-                                             .object)
-
-    def list_images(self, location=None, ex_only_active=True):
-        return self._to_images(self.connection.request('/images/detail')
-                                              .object, ex_only_active)
-
     def ex_get_node_details(self, node_id):
         # @TODO: Remove this if in 0.6
         if isinstance(node_id, Node):
@@ -201,27 +222,14 @@
 
         return self._to_node_from_obj(resp.object)
 
-    def destroy_node(self, node):
-        uri = '/servers/%s' % (node.id)
-        resp = self.connection.request(uri, method='DELETE')
-        # The OpenStack and Rackspace documentation both say this API will
-        # return a 204, but in-fact, everyone everywhere agrees it actually
-        # returns a 202, so we are going to accept either, and someday,
-        # someone will fix either the implementation or the documentation to
-        # agree.
-        return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
     def ex_soft_reboot_node(self, node):
         return self._reboot_node(node, reboot_type='SOFT')
 
     def ex_hard_reboot_node(self, node):
         return self._reboot_node(node, reboot_type='HARD')
 
-    def reboot_node(self, node):
-        return self._reboot_node(node, reboot_type='HARD')
-
-
-class OpenStack_1_0_Response(OpenStack_Response):
+
+class OpenStack_1_0_Response(OpenStackResponse):
 
     def __init__(self, *args, **kwargs):
         # done because of a circular reference from
@@ -255,8 +263,6 @@
 
     features = {"create_node": ["generates_password"]}
 
-    NODE_STATE_MAP = OPENSTACK_NODE_STATE_MAP
-
     def __init__(self, *args, **kwargs):
         self._ex_force_base_url = kwargs.pop('ex_force_base_url', None)
         self._ex_force_auth_url = kwargs.pop('ex_force_auth_url', None)
@@ -266,6 +272,25 @@
         self.XML_NAMESPACE = self.connectionCls.XML_NAMESPACE
         super(OpenStack_1_0_NodeDriver, self).__init__(*args, **kwargs)
 
+    def _to_images(self, object, ex_only_active):
+        images = []
+        for image in self._findall(object, 'image'):
+            if ex_only_active and image.get('status') != 'ACTIVE':
+                continue
+            images.append(self._to_image(image))
+
+        return images
+
+    def _to_image(self, element):
+        return NodeImage(id=element.get('id'),
+                      name=element.get('name'),
+                      driver=self.connection.driver,
+                      extra={'updated': element.get('updated'),
+                             'created': element.get('created'),
+                             'status': element.get('status'),
+                             'serverId': element.get('serverId'),
+                             'progress': element.get('progress')})
+
     def _change_password_or_name(self, node, name=None, password=None):
         uri = '/servers/%s' % (node.id)
 
@@ -283,32 +308,14 @@
         resp = self.connection.request(
             uri, method='PUT', data=ET.tostring(server_elm))
 
-        if resp.status == 204 and password != None:
+        if resp.status == httplib.NO_CONTENT and password != None:
             node.extra['password'] = password
 
-        return resp.status == 204
-
-    def ex_set_password(self, node, password):
-        """
-        Sets the Node's root password.
-
-        This will reboot the instance to complete the operation.
-
-        L{node.extra['password']} will be set to the new value if the
-        operation was successful.
-        """
-        return self._change_password_or_name(node, password=password)
-
-    def ex_set_server_name(self, node, name):
-        """
-        Sets the Node's name.
-
-        This will reboot the instance to complete the operation.
-        """
-        return self._change_password_or_name(node, name=name)
+        return resp.status == httplib.NO_CONTENT
 
     def create_node(self, **kwargs):
-        """Create a new node
+        """
+        Create a new node
 
         See L{NodeDriver.create_node} for more keyword args.
         @keyword    ex_metadata: Key/Value metadata to associate with a node
@@ -354,6 +361,25 @@
                                        data=ET.tostring(server_elm))
         return self._to_node(resp.object)
 
+    def ex_set_password(self, node, password):
+        """
+        Sets the Node's root password.
+
+        This will reboot the instance to complete the operation.
+
+        L{node.extra['password']} will be set to the new value if the
+        operation was successful.
+        """
+        return self._change_password_or_name(node, password=password)
+
+    def ex_set_server_name(self, node, name):
+        """
+        Sets the Node's name.
+
+        This will reboot the instance to complete the operation.
+        """
+        return self._change_password_or_name(node, name=name)
+
     def ex_resize(self, node, size):
         """
         Change an existing server flavor / scale the server up or down.
@@ -374,7 +400,7 @@
         resp = self.connection.request("/servers/%s/action" % (node.id),
                                        method='POST',
                                        data=ET.tostring(elm))
-        return resp.status == 202
+        return resp.status == httplib.ACCEPTED
 
     def ex_confirm_resize(self, node):
         """
@@ -395,7 +421,7 @@
         resp = self.connection.request("/servers/%s/action" % (node.id),
                                        method='POST',
                                        data=ET.tostring(elm))
-        return resp.status == 204
+        return resp.status == httplib.NO_CONTENT
 
     def ex_revert_resize(self, node):
         """
@@ -416,7 +442,7 @@
         resp = self.connection.request("/servers/%s/action" % (node.id),
                                        method='POST',
                                        data=ET.tostring(elm))
-        return resp.status == 204
+        return resp.status == httplib.NO_CONTENT
 
     def ex_rebuild(self, node_id, image_id):
         # @TODO: Remove those ifs in 0.6
@@ -435,7 +461,7 @@
         resp = self.connection.request("/servers/%s/action" % node_id,
                                        method='POST',
                                        data=ET.tostring(elm))
-        return resp.status == 202
+        return resp.status == httplib.ACCEPTED
 
     def ex_create_ip_group(self, group_name, node_id=None):
         # @TODO: Remove this if in 0.6
@@ -470,7 +496,7 @@
     def ex_delete_ip_group(self, group_id):
         uri = '/shared_ip_groups/%s' % group_id
         resp = self.connection.request(uri, method='DELETE')
-        return resp.status == 204
+        return resp.status == httplib.NO_CONTENT
 
     def ex_share_ip(self, group_id, node_id, ip, configure_node=True):
         # @TODO: Remove this if in 0.6
@@ -494,7 +520,7 @@
         resp = self.connection.request(uri,
                                        method='PUT',
                                        data=ET.tostring(elm))
-        return resp.status == 202
+        return resp.status == httplib.ACCEPTED
 
     def ex_unshare_ip(self, node_id, ip):
         # @TODO: Remove this if in 0.6
@@ -505,7 +531,7 @@
 
         resp = self.connection.request(uri,
                                        method='DELETE')
-        return resp.status == 202
+        return resp.status == httplib.ACCEPTED
 
     def ex_list_ip_addresses(self, node_id):
         # @TODO: Remove this if in 0.6
@@ -543,7 +569,7 @@
 
     def _reboot_node(self, node, reboot_type='SOFT'):
         resp = self._node_action(node, ['reboot', ('type', reboot_type)])
-        return resp.status == 202
+        return resp.status == httplib.ACCEPTED
 
     def _node_action(self, node, body):
         if isinstance(body, list):
@@ -558,14 +584,6 @@
         node_elements = self._findall(object, 'server')
         return [self._to_node(el) for el in node_elements]
 
-    def _fixxpath(self, xpath):
-        # ElementTree wants namespaces in its xpaths, so here we add them.
-        return "/".join(["{%s}%s" % (self.XML_NAMESPACE, e) for e
-                         in xpath.split("/")])
-
-    def _findall(self, element, xpath):
-        return element.findall(self._fixxpath(xpath))
-
     def _to_node_from_obj(self, obj):
         return self._to_node(self._findall(obj, 'server')[0])
 
@@ -609,7 +627,7 @@
         return [self._to_size(el) for el in elements]
 
     def _to_size(self, el):
-        s = NodeSize(id=el.get('id'),
+        return NodeSize(id=el.get('id'),
                      name=el.get('name'),
                      ram=int(el.get('ram')),
                      disk=int(el.get('disk')),
@@ -618,27 +636,14 @@
                      # Hardcoded
                      price=self._get_size_price(el.get('id')),
                      driver=self.connection.driver)
-        return s
-
-    def _to_images(self, object, ex_only_active):
-        elements = self._findall(object, "image")
-        rv = []
-        for el in elements:
-            if ex_only_active and el.get('status') != 'ACTIVE':
-                continue
-            rv.append(self._to_image(el))
-        return rv
-
-    def _to_image(self, el):
-        i = NodeImage(id=el.get('id'),
-                      name=el.get('name'),
-                      driver=self.connection.driver,
-                      extra={'updated': el.get('updated'),
-                             'created': el.get('created'),
-                             'status': el.get('status'),
-                             'serverId': el.get('serverId'),
-                             'progress': el.get('progress')})
-        return i
+
+    def _fixxpath(self, xpath):
+        # ElementTree wants namespaces in its xpaths, so here we add them.
+        return "/".join(["{%s}%s" % (self.XML_NAMESPACE, e) for e
+                         in xpath.split("/")])
+
+    def _findall(self, element, xpath):
+        return element.findall(self._fixxpath(xpath))
 
     def ex_limits(self):
         """
@@ -696,7 +701,7 @@
         """
         uri = '/images/%s' % image.id
         resp = self.connection.request(uri, method='DELETE')
-        return resp.status == 204
+        return resp.status == httplib.NO_CONTENT
 
     def _to_shared_ip_group(self, el):
         servers_el = self._findall(el, 'servers')
@@ -747,7 +752,7 @@
         self.private_addresses = private_addresses
 
 
-class OpenStack_1_1_Response(OpenStack_Response):
+class OpenStack_1_1_Response(OpenStackResponse):
 
     def __init__(self, *args, **kwargs):
         # done because of a circular reference from
@@ -777,8 +782,6 @@
 
     features = {"create_node": ["generates_password"]}
 
-    NODE_STATE_MAP = OPENSTACK_NODE_STATE_MAP
-
     def __init__(self, *args, **kwargs):
         self._ex_force_base_url = kwargs.pop('ex_force_base_url', None)
         self._ex_force_auth_url = kwargs.pop('ex_force_auth_url', None)
@@ -788,6 +791,49 @@
                                                     None))
         super(OpenStack_1_1_NodeDriver, self).__init__(*args, **kwargs)
 
+    def create_node(self, **kwargs):
+        """Create a new node
+
+        See L{NodeDriver.create_node} for more keyword args.
+        @keyword    ex_metadata: Key/Value metadata to associate with a node
+        @type       ex_metadata: C{dict}
+
+        @keyword    ex_files:   File Path => File contents to create on
+                                the node
+        @type       ex_files:   C{dict}
+        """
+
+        server_params = self._create_args_to_params(None, kwargs)
+
+        resp = self.connection.request("/servers",
+                                       method='POST',
+                                       data={'server': server_params})
+
+        return self._to_node(resp.object['server'])
+
+    def _to_images(self, obj, ex_only_active):
+        images = []
+        for image in obj['images']:
+            if ex_only_active and image.get('status') != 'ACTIVE':
+                continue
+            images.append(self._to_image(image))
+
+        return images
+
+    def _to_image(self, api_image):
+        return NodeImage(
+                      id=api_image['id'],
+                      name=api_image['name'],
+                      driver=self,
+                      extra=dict(
+                                 updated=api_image['updated'],
+                                 created=api_image['created'],
+                                 status=api_image['status'],
+                                 progress=api_image.get('progress'),
+                                 metadata=api_image.get('metadata'),
+                      ),
+                  )
+
     def _to_nodes(self, obj):
         servers = obj['servers']
         return [self._to_node(server) for server in servers]
@@ -796,15 +842,6 @@
         flavors = obj['flavors']
         return [self._to_size(flavor) for flavor in flavors]
 
-    def _to_images(self, obj, ex_only_active):
-        images = obj['images']
-        rv = []
-        for image in images:
-            if ex_only_active and image.get('status') != 'ACTIVE':
-                continue
-            rv.append(self._to_image(image))
-        return rv
-
     def _create_args_to_params(self, node, kwargs):
         server_params = {
             'name': kwargs.get('name'),
@@ -830,26 +867,6 @@
 
         return server_params
 
-    def create_node(self, **kwargs):
-        """Create a new node
-
-        See L{NodeDriver.create_node} for more keyword args.
-        @keyword    ex_metadata: Key/Value metadata to associate with a node
-        @type       ex_metadata: C{dict}
-
-        @keyword    ex_files:   File Path => File contents to create on
-                                the node
-        @type       ex_files:   C{dict}
-        """
-
-        server_params = self._create_args_to_params(None, kwargs)
-
-        resp = self.connection.request("/servers",
-                                       method='POST',
-                                       data={'server': server_params})
-
-        return self._to_node(resp.object['server'])
-
     def _files_to_personality(self, files):
         rv = []
 
@@ -981,20 +998,6 @@
             driver=self,
         )
 
-    def _to_image(self, api_image):
-        return NodeImage(
-            id=api_image['id'],
-            name=api_image['name'],
-            driver=self,
-            extra=dict(
-                updated=api_image['updated'],
-                created=api_image['created'],
-                status=api_image['status'],
-                progress=api_image.get('progress'),
-                metadata=api_image.get('metadata'),
-            ),
-        )
-
     def _get_size_price(self, size_id):
             try:
                 return get_size_price(

=== modified file 'libcloud/compute/drivers/opsource.py'
--- libcloud/compute/drivers/opsource.py	2011-10-12 21:00:33 +0000
+++ libcloud/compute/drivers/opsource.py	2011-10-21 05:25:30 +0000
@@ -15,15 +15,16 @@
 """
 Opsource Driver
 """
-import base64
 from xml.etree import ElementTree as ET
+from base64 import b64encode
+import httplib
 
-from libcloud.utils import fixxpath, findtext, findall
-from libcloud.common.base import ConnectionUserAndKey, Response
 from libcloud.common.types import LibcloudError, InvalidCredsError, MalformedResponseError
-from libcloud.compute.types import NodeState, Provider
 from libcloud.compute.base import NodeDriver, Node, NodeAuthPassword
 from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
+from libcloud.common.base import ConnectionUserAndKey, Response
+from libcloud.utils import fixxpath, findtext, findall
+from libcloud.compute.types import NodeState, Provider
 
 # Roadmap / TODO:
 #
@@ -85,28 +86,38 @@
 class OpsourceResponse(Response):
 
     def parse_body(self):
+        if self.status == httplib.NO_CONTENT or not self.body:
+            return None
+
         try:
-            body = ET.XML(self.body)
+            return ET.XML(self.body)
         except:
-            raise MalformedResponseError("Failed to parse XML", body=self.body, driver=OpsourceNodeDriver)
-        return body
+            raise MalformedResponseError(
+                'Failed to parse XML',
+                body=self.body,
+                driver=OpsourceNodeDriver)
+
+        return self.body
 
     def parse_error(self):
-        if self.status == 401:
-            raise InvalidCredsError(self.body)
-
-        if self.status == 403:
-            raise InvalidCredsError(self.body)
-
-        try:
-            body = ET.XML(self.body)
-        except:
-            raise MalformedResponseError("Failed to parse XML", body=self.body, driver=OpsourceNodeDriver)
-
-        if self.status == 400:
+        if self.status == httplib.BAD_REQUEST:
             code = findtext(body, 'resultCode', SERVER_NS)
             message = findtext(body, 'resultDetail', SERVER_NS)
-            raise OpsourceAPIException(code, message, driver=OpsourceNodeDriver)
+            raise OpsourceAPIException(code,
+                message,
+                driver=OpsourceNodeDriver)
+        elif self.status == httplib.UNAUTHORIZED:
+            raise InvalidCredsError(self.body)
+        elif self.status == httplib.FORBIDDEN:
+            raise InvalidCredsError(self.body)
+
+        try:
+            return ET.XML(self.body)
+        except:
+            raise MalformedResponseError(
+                'Failed to parse XML',
+                body=self.body,
+                driver=OpsourceNodeDriver)
 
         return self.body
 
@@ -134,8 +145,8 @@
     responseCls = OpsourceResponse
 
     def add_default_headers(self, headers):
-        headers['Authorization'] = ('Basic %s'
-                              % (base64.b64encode('%s:%s' % (self.user_id, self.key))))
+        headers['Authorization'] = ('Basic %s' % b64encode('%s:%s' %
+                                                (self.user_id, self.key)))
         return headers
 
     def request(self, action, params=None, data='', headers=None, method='GET'):
@@ -203,7 +214,7 @@
 
 class OpsourceNetwork(object):
     """
-    Opsource network with location
+    Opsource network with location.
     """
 
     def __init__(self, id, name, description, location, privateNet,
@@ -225,45 +236,17 @@
 
 class OpsourceNodeDriver(NodeDriver):
     """
-    Opsource node driver
+    Opsource node driver.
     """
 
     connectionCls = OpsourceConnection
-
+    name = 'Opsource'
     type = Provider.OPSOURCE
-    name = 'Opsource'
-
     features = {"create_node": ["password"]}
 
-    def list_nodes(self):
-        nodes = self._to_nodes(self.connection.request_with_orgId('server/deployed').object)
-        nodes.extend(self._to_nodes(self.connection.request_with_orgId('server/pendingDeploy').object))
-        return nodes
-
-    def list_sizes(self, location=None):
-        return [ NodeSize(id=1,
-                  name="default",
-                  ram=0,
-                  disk=0,
-                  bandwidth=0,
-                  price=0,
-                  driver=self.connection.driver) ]
-
-    def list_images(self, location=None):
-        """return a list of available images
-            Currently only returns the default 'base OS images' provided by opsource.
-            Customer images (snapshots) are not yet supported.
-        """
-        return self._to_base_images(self.connection.request('base/image').object)
-
-    def list_locations(self):
-        """list locations (datacenters) available for instantiating servers and
-            networks.
-        """
-        return self._to_locations(self.connection.request_with_orgId('datacenter').object)
-
     def create_node(self, **kwargs):
-        """Create a new opsource node
+        """
+        Create a new opsource node
 
         Standard keyword arguments from L{NodeDriver.create_node}:
         @keyword    name:   String with a name for this new node (required)
@@ -334,17 +317,85 @@
         #      because opsource allows multiple nodes to have the same name
         return filter(lambda x: x.name == name, self.list_nodes())[-1]
 
+    def destroy_node(self, node):
+        body = self.connection.request_with_orgId('server/%s?delete' % node.id).object
+        result = findtext(body, 'result', GENERAL_NS)
+        return result == 'SUCCESS'
+
     def reboot_node(self, node):
-        """reboots the node"""
         body = self.connection.request_with_orgId('server/%s?restart' % node.id).object
         result = findtext(body, 'result', GENERAL_NS)
         return result == 'SUCCESS'
 
-    def destroy_node(self, node):
-        """Destroys the node"""
-        body = self.connection.request_with_orgId('server/%s?delete' % node.id).object
-        result = findtext(body, 'result', GENERAL_NS)
-        return result == 'SUCCESS'
+    def list_nodes(self):
+        nodes = self._to_nodes(self.connection.request_with_orgId('server/deployed').object)
+        nodes.extend(self._to_nodes(self.connection.request_with_orgId('server/pendingDeploy').object))
+        return nodes
+
+    def list_images(self, location=None):
+        """return a list of available images
+            Currently only returns the default 'base OS images' provided by opsource.
+            Customer images (snapshots) are not yet supported.
+        """
+        return self._to_base_images(self.connection.request('base/image').object)
+
+    def list_sizes(self, location=None):
+        return [
+            NodeSize(id=1,
+                name="default",
+                ram=0,
+                disk=0,
+                bandwidth=0,
+                price=0,
+                driver=self.connection.driver),
+        ]
+
+    def list_locations(self):
+        """list locations (datacenters) available for instantiating servers and
+            networks.
+        """
+        return self._to_locations(self.connection.request_with_orgId('datacenter').object)
+
+    def list_networks(self, location=None):
+        """List networks deployed across all data center locations for your
+        organization.  The response includes the location of each network.
+
+        Returns a list of OpsourceNetwork objects
+        """
+        return self._to_networks(self.connection.request_with_orgId('networkWithLocation').object)
+
+    def _to_base_images(self, object):
+        images = []
+        for element in object.findall(fixxpath("ServerImage", SERVER_NS)):
+            images.append(self._to_base_image(element))
+
+        return images
+
+    def _to_base_image(self, element):
+        # Eventually we will probably need multiple _to_image() functions
+        # that parse <ServerImage> differently than <DeployedImage>.
+        # DeployedImages are customer snapshot images, and ServerImages are
+        # 'base' images provided by opsource
+        location_id = findtext(element, 'location', SERVER_NS)
+        location = self.ex_get_location_by_id(location_id)
+
+        extra = {
+            'description': findtext(element, 'description', SERVER_NS),
+            'OS_type': findtext(element, 'operatingSystem/type', SERVER_NS),
+            'OS_displayName': findtext(element, 'operatingSystem/displayName', SERVER_NS),
+            'cpuCount': findtext(element, 'cpuCount', SERVER_NS),
+            'resourcePath': findtext(element, 'resourcePath', SERVER_NS),
+            'memory': findtext(element, 'memory', SERVER_NS),
+            'osStorage': findtext(element, 'osStorage', SERVER_NS),
+            'additionalStorage': findtext(element, 'additionalStorage', SERVER_NS),
+            'created': findtext(element, 'created', SERVER_NS),
+            'location': location,
+        }
+
+        return NodeImage(id=str(findtext(element, 'id', SERVER_NS)),
+                     name=str(findtext(element, 'name', SERVER_NS)),
+                     extra=extra,
+                     driver=self.connection.driver)
 
     def ex_start_node(self, node):
         """Powers on an existing deployed server"""
@@ -372,14 +423,6 @@
         result = findtext(body, 'result', GENERAL_NS)
         return result == 'SUCCESS'
 
-    def ex_list_networks(self):
-        """List networks deployed across all data center locations for your
-        organization.  The response includes the location of each network.
-
-        Returns a list of OpsourceNetwork objects
-        """
-        return self._to_networks(self.connection.request_with_orgId('networkWithLocation').object)
-
     def ex_get_location_by_id(self, id):
         location = None
         if id is not None:
@@ -387,8 +430,11 @@
         return location
 
     def _to_networks(self, object):
-        node_elements = findall(object, 'network', NETWORK_NS)
-        return [ self._to_network(el) for el in node_elements ]
+        networks = []
+        for element in findall(object, 'network', NETWORK_NS):
+            networks.append(self._to_network(element))
+
+        return networks
 
     def _to_network(self, element):
         multicast = False
@@ -409,8 +455,11 @@
                                status=status)
 
     def _to_locations(self, object):
-        node_elements = object.findall(fixxpath('datacenter', DATACENTER_NS))
-        return [ self._to_location(el) for el in node_elements ]
+        locations = []
+        for element in object.findall(fixxpath('datacenter', DATACENTER_NS)):
+            locations.append(self._to_location(element))
+
+        return locations
 
     def _to_location(self, element):
         l = NodeLocation(id=findtext(element, 'location', DATACENTER_NS),
@@ -456,37 +505,6 @@
                  extra=extra)
         return n
 
-    def _to_base_images(self, object):
-        node_elements = object.findall(fixxpath("ServerImage", SERVER_NS))
-        return [ self._to_base_image(el) for el in node_elements ]
-
-    def _to_base_image(self, element):
-        # Eventually we will probably need multiple _to_image() functions
-        # that parse <ServerImage> differently than <DeployedImage>.
-        # DeployedImages are customer snapshot images, and ServerImages are
-        # 'base' images provided by opsource
-        location_id = findtext(element, 'location', SERVER_NS)
-        location = self.ex_get_location_by_id(location_id)
-
-        extra = {
-            'description': findtext(element, 'description', SERVER_NS),
-            'OS_type': findtext(element, 'operatingSystem/type', SERVER_NS),
-            'OS_displayName': findtext(element, 'operatingSystem/displayName', SERVER_NS),
-            'cpuCount': findtext(element, 'cpuCount', SERVER_NS),
-            'resourcePath': findtext(element, 'resourcePath', SERVER_NS),
-            'memory': findtext(element, 'memory', SERVER_NS),
-            'osStorage': findtext(element, 'osStorage', SERVER_NS),
-            'additionalStorage': findtext(element, 'additionalStorage', SERVER_NS),
-            'created': findtext(element, 'created', SERVER_NS),
-            'location': location,
-        }
-
-        i = NodeImage(id=str(findtext(element, 'id', SERVER_NS)),
-                     name=str(findtext(element, 'name', SERVER_NS)),
-                     extra=extra,
-                     driver=self.connection.driver)
-        return i
-
     def _to_status(self, element):
         if element == None:
             return OpsourceStatus()

