=== added directory 'libcloud/compute/drivers/opennebula'
=== removed file 'libcloud/compute/drivers/opennebula.py'
--- libcloud/compute/drivers/opennebula.py	2011-10-12 21:00:33 +0000
+++ libcloud/compute/drivers/opennebula.py	1970-01-01 00:00:00 +0000
@@ -1,224 +0,0 @@
-# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
-# Complutense de Madrid (dsa-research.org)
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-OpenNebula driver.
-"""
-
-from base64 import b64encode
-import hashlib
-from xml.etree import ElementTree as ET
-
-from libcloud.common.base import ConnectionUserAndKey, Response
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import NodeDriver, Node, NodeLocation
-from libcloud.compute.base import NodeImage, NodeSize
-
-API_HOST = ''
-API_PORT = (4567, 443)
-API_SECURE = True
-
-
-class OpenNebulaResponse(Response):
-
-    def success(self):
-        i = int(self.status)
-        return i >= 200 and i <= 299
-
-    def parse_body(self):
-        if not self.body:
-            return None
-        return ET.XML(self.body)
-
-    def parse_error(self):
-        if int(self.status) == 401:
-            raise InvalidCredsError(self.body)
-        return self.body
-
-
-class OpenNebulaConnection(ConnectionUserAndKey):
-    """
-    Connection class for the OpenNebula driver
-    """
-
-    host = API_HOST
-    port = API_PORT
-    secure = API_SECURE
-    responseCls = OpenNebulaResponse
-
-    def add_default_headers(self, headers):
-        pass_sha1 = hashlib.sha1(self.key).hexdigest()
-        headers['Authorization'] = ("Basic %s" % b64encode("%s:%s" % (self.user_id, pass_sha1)))
-        return headers
-
-
-class OpenNebulaNodeDriver(NodeDriver):
-    """
-    OpenNebula node driver
-    """
-
-    connectionCls = OpenNebulaConnection
-    type = Provider.OPENNEBULA
-    name = 'OpenNebula'
-
-    NODE_STATE_MAP = {
-        'PENDING': NodeState.PENDING,
-        'ACTIVE': NodeState.RUNNING,
-        'DONE': NodeState.TERMINATED,
-        'STOPPED': NodeState.TERMINATED
-    }
-
-    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 list_nodes(self):
-        return self._to_nodes(self.connection.request('/compute').object)
-
-    def list_images(self, location=None):
-        return self._to_images(self.connection.request('/storage').object)
-
-    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']
-
-        # """
-        # Other extractable (but unused) information
-        # """
-        # instance_type = ET.SubElement(compute, 'INSTANCE_TYPE')
-        # instance_type.text = kwargs['size'].name
-        #
-        # storage = ET.SubElement(compute, 'STORAGE')
-        # disk = ET.SubElement(storage, 'DISK', {'image': str(kwargs['image'].id),
-        #                                        'dev': 'sda1'})
-
-        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("DISK"):
-            image_id = element.attrib["href"].partition("/storage/")[2]
-            image = self.connection.request(("/storage/%s" % (image_id))).object
-            images.append(self._to_image(image))
-
-        return images
-
-    def _to_image(self, image):
-        return NodeImage(id=image.findtext("ID"),
-                         name=image.findtext("NAME"),
-                         driver=self.connection.driver)
-
-    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" % (compute_id))).object
-            computes.append(self._to_node(compute))
-
-        return computes
-
-    def _to_node(self, compute):
-        try:
-            state = self.NODE_STATE_MAP[compute.findtext("STATE")]
-        except KeyError:
-            state = NodeState.UNKNOWN
-
-        networks = []
-        for element in compute.findall("NIC"):
-            ip = element.element.attrib.get('ip', None)
-
-            if ip is None:
-                ip = element.findtext("IP")
-
-            networks.append(ip)
-
-        return Node(id=compute.findtext("ID"),
-                    name=compute.findtext("NAME"),
-                    state=state,
-                    public_ip=networks,
-                    private_ip=[],
-                    driver=self.connection.driver)
-
-    def _xml_action(self, compute_id, action):
-        compute = ET.Element('COMPUTE')
-
-        compute_id = ET.SubElement(compute, 'ID')
-        compute_id.text = str(compute_id)
-
-        state = ET.SubElement(compute, 'STATE')
-        state.text = action
-
-        xml = ET.tostring(compute)
-        return xml

=== added file 'libcloud/compute/drivers/opennebula/__init__.py'
--- libcloud/compute/drivers/opennebula/__init__.py	1970-01-01 00:00:00 +0000
+++ libcloud/compute/drivers/opennebula/__init__.py	2011-10-16 20:58:57 +0000
@@ -0,0 +1,244 @@
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+OpenNebula driver base class and factory.
+"""
+
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
+from xml.etree import ElementTree as ET
+from base64 import b64encode
+import hashlib
+import sys
+
+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):
+
+    def success(self):
+        i = int(self.status)
+        return i >= 200 and i <= 299
+
+    def parse_body(self):
+        if not self.body:
+            return None
+        return ET.XML(self.body)
+
+    def parse_error(self):
+        if int(self.status) == 401:
+            raise InvalidCredsError(self.body)
+        return self.body
+
+class OpenNebulaConnection(ConnectionUserAndKey):
+    """
+    Connection class for the OpenNebula driver
+    """
+
+    host = API_HOST
+    port = API_PORT
+    secure = API_SECURE
+    responseCls = OpenNebulaResponse
+
+    def add_default_headers(self, headers):
+        pass_sha1 = hashlib.sha1(self.key).hexdigest()
+        headers['Authorization'] = ("Basic %s" % b64encode("%s:%s" % (self.user_id, pass_sha1)))
+        return headers
+
+class OpenNebulaNodeDriver(NodeDriver):
+    """
+    OpenNebula node driver
+    """
+
+    connectionCls = OpenNebulaConnection
+    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,
+    }
+
+    def __new__(cls, key, secret=None, secure=True, host=None, port=None, api_version=DEFAULT_API_VERSION, **kwargs):
+        if cls is OpenNebulaNodeDriver:
+            # This base class is a factory.
+
+            ver_mod_name = 'libcloud.compute.drivers.opennebula.v%s' % (api_version.replace('.', '_'),)
+            try:
+                __import__(ver_mod_name)
+            except ImportError:
+                raise NotImplementedError('API version %s is not supported by this OpenNebula driver.' % (api_version,))
+
+            ver_mod = sys.modules[ver_mod_name]
+            cls = ver_mod.OpenNebulaNodeDriver
+
+            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 list_nodes(self):
+        return self._to_nodes(self.connection.request('/compute').object)
+
+    def list_images(self, location=None):
+        return self._to_images(self.connection.request('/storage').object)
+
+    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 _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" % (image_id))).object
+            images.append(self._to_image(image))
+
+        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_nodes(self, object):
+        computes = []
+        for element in object.findall("COMPUTE"):
+            compute_id = element.attrib["href"].partition("/compute/")[2]
+            compute = self.connection.request(("/compute/%s" % (compute_id))).object
+            computes.append(self._to_node(compute))
+
+        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")]
+        except KeyError:
+            state = NodeState.UNKNOWN
+
+        networks = self._extract_networks(compute)
+
+        return Node(id=compute.findtext("ID"),
+                    name=compute.findtext("NAME"),
+                    state=state,
+                    public_ip=networks,
+                    private_ip=[],
+                    driver=self.connection.driver)
+
+    def _xml_action(self, compute_id, action):
+        compute = ET.Element('COMPUTE')
+
+        compute_id = ET.SubElement(compute, 'ID')
+        compute_id.text = str(compute_id)
+
+        state = ET.SubElement(compute, 'STATE')
+        state.text = action
+
+        xml = ET.tostring(compute)
+        return xml

=== added file 'libcloud/compute/drivers/opennebula/v1_4.py'
--- libcloud/compute/drivers/opennebula/v1_4.py	1970-01-01 00:00:00 +0000
+++ libcloud/compute/drivers/opennebula/v1_4.py	2011-10-13 04:26:54 +0000
@@ -0,0 +1,37 @@
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+OpenNebula driver for OpenNebula 1.4.
+"""
+
+from libcloud.compute.drivers.opennebula import OpenNebulaNodeDriver as OpenNebulaNodeDriverBase
+from libcloud.compute.drivers.opennebula import OpenNebulaResponse as OpenNebulaResponseBase
+from libcloud.compute.drivers.opennebula import OpenNebulaConnection as OpenNebulaConnectionBase
+
+class OpenNebulaResponse(OpenNebulaResponseBase):
+    pass
+
+class OpenNebulaConnection(OpenNebulaConnectionBase):
+    pass
+
+class OpenNebulaNodeDriver(OpenNebulaNodeDriverBase):
+    """
+    OpenNebula node driver
+    """
+
+    pass

=== added file 'libcloud/compute/drivers/opennebula/v3_0.py'
--- libcloud/compute/drivers/opennebula/v3_0.py	1970-01-01 00:00:00 +0000
+++ libcloud/compute/drivers/opennebula/v3_0.py	2011-10-16 20:58:57 +0000
@@ -0,0 +1,88 @@
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+OpenNebula driver for OpenNebula 3.0.
+"""
+
+from xml.etree import ElementTree as ET
+
+from libcloud.compute.drivers.opennebula import OpenNebulaNodeDriver as OpenNebulaNodeDriverBase
+from libcloud.compute.drivers.opennebula import OpenNebulaResponse as OpenNebulaResponseBase
+from libcloud.compute.drivers.opennebula import OpenNebulaConnection as OpenNebulaConnectionBase
+from libcloud.compute.base import NodeImage
+
+class OpenNebulaResponse(OpenNebulaResponseBase):
+    pass
+
+class OpenNebulaConnection(OpenNebulaConnectionBase):
+    pass
+
+class OpenNebulaNodeDriver(OpenNebulaNodeDriverBase):
+    """
+    OpenNebula 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))})
+
+        #for nic in kwargs['nic']:
+            # networkCard = ET.SubElement(compute, 'NIC')
+            # network = ET.SubElement(networkCard, 'NETWORK', {'href': '/network/%s' % (str(nic.id))})
+            # ip = ET.SubElement(networkCard, 'IP')
+            # ip.text = nic.ip
+
+        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" % (image_id))).object
+            images.append(self._to_image(image))
+
+        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 _extract_networks(self, compute):
+        networks = []
+        for element in compute.findall("NIC"):
+            for ip in element.findall("IP"):
+                networks.append(ip)
+
+        return networks

