Author: hbetts
Date: Sat Dec 10 06:05:28 2011
New Revision: 1212736

URL: http://svn.apache.org/viewvc?rev=1212736&view=rev
Log:
Support for retrieving OpenNebula v3.2 instance types, OpenNebula v3.0 network 
Public attribute support, and additional code coverage tests.

Added:
    libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_25.xml
    libcloud/trunk/test/compute/fixtures/opennebula_3_0/
    libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_15.xml
    libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_5.xml
    libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_collection.xml
    libcloud/trunk/test/compute/fixtures/opennebula_3_2/
    
libcloud/trunk/test/compute/fixtures/opennebula_3_2/instance_type_collection.xml
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/compute/drivers/opennebula.py
    libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_collection.xml
    libcloud/trunk/test/compute/test_opennebula.py

Modified: libcloud/trunk/CHANGES
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1212736&r1=1212735&r2=1212736&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Sat Dec 10 06:05:28 2011
@@ -2,6 +2,13 @@
 
 Changes with Apache Libcloud in development:
 
+  *) Compute:
+
+     - Added support for retrieving OpenNebula v3.2 instance types, OpenNebula
+       v3.0 network Public attribute support, and additional code coverage
+       tests.
+       [Hutson Betts]
+
   *) Load-balancer:
 
     - Add 'extra' attribute to the LoadBalancer object and retrieve all the

Modified: libcloud/trunk/libcloud/compute/drivers/opennebula.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/opennebula.py?rev=1212736&r1=1212735&r2=1212736&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/opennebula.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/opennebula.py Sat Dec 10 06:05:28 
2011
@@ -22,11 +22,6 @@ OpenNebula.org driver.
 
 __docformat__ = 'epytext'
 
-try:
-    import simplejson as json
-except ImportError:
-    import json
-
 from xml.etree import ElementTree as ET
 from base64 import b64encode
 import hashlib
@@ -49,7 +44,9 @@ __all__ = [
     'OpenNebulaNetwork',
     'OpenNebulaNodeDriver',
     'OpenNebula_1_4_NodeDriver',
-    'OpenNebula_2_0_NodeDriver']
+    'OpenNebula_2_0_NodeDriver',
+    'OpenNebula_3_0_NodeDriver',
+    'OpenNebula_3_2_NodeDriver']
 
 API_HOST = ''
 API_PORT = (4567, 443)
@@ -197,24 +194,8 @@ class OpenNebulaNetwork(object):
         @rtype:  C{string}
         @return: Unique identifier for this instance.
         """
-        return hashlib.sha1(b("%s:%d" % (self.id, 
self.driver.type))).hexdigest()
-
-    def destroy(self):
-        """
-        Destroy this network.
-
-        This calls the networks's driver and destroys the network.
-
-        >>> from libcloud.network.drivers.dummy import DummyNetworkDriver
-        >>> driver = DummyNetworkDriver()
-        >>> network = driver.create_network()
-        >>> network.destroy()
-        True
-
-        @return: True is the network was destroyed, else False.
-        @rtype:  C{bool}
-        """
-        return self.driver.destroy_network(self)
+        return hashlib.sha1(b("%s:%d" % (self.id,
+                                         self.driver.type))).hexdigest()
 
     def __repr__(self):
         return (('<OpenNebulaNetwork: uuid=%s, name=%s, address=%s, size=%s, '
@@ -250,8 +231,12 @@ class OpenNebulaNodeDriver(NodeDriver):
         if cls is OpenNebulaNodeDriver:
             if api_version in ['1.4']:
                 cls = OpenNebula_1_4_NodeDriver
-            elif api_version in ['2.0', '2.2', '3.0', '3.2']:
+            elif api_version in ['2.0', '2.2']:
                 cls = OpenNebula_2_0_NodeDriver
+            elif api_version in ['3.0']:
+                cls = OpenNebula_3_0_NodeDriver
+            elif api_version in ['3.2']:
+                cls = OpenNebula_3_2_NodeDriver
             else:
                 raise NotImplementedError(
                     "No OpenNebulaNodeDriver found for API version %s" %
@@ -280,7 +265,7 @@ class OpenNebulaNodeDriver(NodeDriver):
         instance_type.text = kwargs['size'].name
 
         storage = ET.SubElement(compute, 'STORAGE')
-        disk = ET.SubElement(storage, 'DISK', {'image': '%s' %
+        ET.SubElement(storage, 'DISK', {'image': '%s' %
                                                   (str(kwargs['image'].id))})
 
         if 'networks' in kwargs:
@@ -290,11 +275,11 @@ class OpenNebulaNodeDriver(NodeDriver):
             networkGroup = ET.SubElement(compute, 'NETWORK')
             for network in kwargs['networks']:
                 if network.address:
-                    nic = ET.SubElement(networkGroup, 'NIC',
+                    ET.SubElement(networkGroup, 'NIC',
                         {'network': '%s' % (str(network.id)),
                         'ip': network.address})
                 else:
-                    nic = ET.SubElement(networkGroup, 'NIC',
+                    ET.SubElement(networkGroup, 'NIC',
                         {'network': '%s' % (str(network.id))})
 
         xml = ET.tostring(compute)
@@ -396,7 +381,7 @@ class OpenNebulaNodeDriver(NodeDriver):
         compute = ET.Element('COMPUTE')
 
         compute_id = ET.SubElement(compute, 'ID')
-        compute_id.text = str(compute_node_id)
+        compute_id.text = compute_node_id
 
         state = ET.SubElement(compute, 'STATE')
         state.text = action
@@ -528,12 +513,10 @@ class OpenNebulaNodeDriver(NodeDriver):
         except KeyError:
             state = NodeState.UNKNOWN
 
-        networks = self._extract_networks(compute)
-
         return Node(id=compute.findtext('ID'),
                     name=compute.findtext('NAME'),
                     state=state,
-                    public_ips=networks,
+                    public_ips=self._extract_networks(compute),
                     private_ips=[],
                     driver=self.connection.driver,
                     image=self._extract_images(compute))
@@ -607,7 +590,7 @@ class OpenNebula_1_4_NodeDriver(OpenNebu
 class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver):
     """
     OpenNebula.org node driver for OpenNebula.org v2.0 through OpenNebula.org
-    v3.2.
+    v2.2.
     """
 
     def create_node(self, **kwargs):
@@ -635,7 +618,7 @@ class OpenNebula_2_0_NodeDriver(OpenNebu
         instance_type.text = kwargs['size'].name
 
         disk = ET.SubElement(compute, 'DISK')
-        storage = ET.SubElement(disk, 'STORAGE', {'href': '/storage/%s' %
+        ET.SubElement(disk, 'STORAGE', {'href': '/storage/%s' %
                                                   (str(kwargs['image'].id))})
 
         if 'networks' in kwargs:
@@ -644,7 +627,7 @@ class OpenNebula_2_0_NodeDriver(OpenNebu
 
             for network in kwargs['networks']:
                 nic = ET.SubElement(compute, 'NIC')
-                network_line = ET.SubElement(nic, 'NETWORK',
+                ET.SubElement(nic, 'NETWORK',
                             {'href': '/network/%s' % (str(network.id))})
                 if network.address:
                     ip_line = ET.SubElement(nic, 'IP')
@@ -772,12 +755,10 @@ class OpenNebula_2_0_NodeDriver(OpenNebu
         except KeyError:
             state = NodeState.UNKNOWN
 
-        networks = self._extract_networks(compute)
-
         return Node(id=compute.findtext('ID'),
                     name=compute.findtext('NAME'),
                     state=state,
-                    public_ips=networks,
+                    public_ips=self._extract_networks(compute),
                     private_ips=[],
                     driver=self.connection.driver,
                     image=self._extract_images(compute),
@@ -889,3 +870,121 @@ class OpenNebula_2_0_NodeDriver(OpenNebu
                 contexts[context_element.tag.lower()] = context_element.text
 
         return contexts
+
+
+class OpenNebula_3_0_NodeDriver(OpenNebula_2_0_NodeDriver):
+    """
+    OpenNebula.org node driver for OpenNebula.org v3.0.
+    """
+
+    def ex_node_set_save_name(self, node, name):
+        """
+        Build action representation and instruct node to commit action.
+
+        Build action representation from the compute node ID, the disk image
+        which will be saved, and the name under which the image will be saved
+        upon shutting down the compute node.
+
+        @type  node: L{Node}
+        @param node: Compute node instance.
+        @type  name: C{str}
+        @param name: Name under which the image should be saved after shutting
+                     down the compute node.
+
+        @rtype:  C{bool}
+        @return: False if an HTTP Bad Request is received, else, True is
+                 returned.
+        """
+        compute_node_id = str(node.id)
+
+        compute = ET.Element('COMPUTE')
+
+        compute_id = ET.SubElement(compute, 'ID')
+        compute_id.text = compute_node_id
+
+        disk = ET.SubElement(compute, 'DISK', {'id': str(node.image.id)})
+
+        ET.SubElement(disk, 'STORAGE', {'href': '/storage/%s' %
+                                        (str(node.image.id)),
+                                        'name': node.image.name})
+
+        ET.SubElement(disk, 'SAVE_AS', {'name': str(name)})
+
+        xml = ET.tostring(compute)
+
+        url = '/compute/%s' % compute_node_id
+        resp = self.connection.request(url, method='PUT',
+                                        data=xml)
+
+        if resp.status == httplib.BAD_REQUEST:
+            return False
+        else:
+            return True
+
+    def _to_network(self, element):
+        """
+        Take XML object containing a network description and convert to
+        OpenNebulaNetwork object.
+
+        Take XML representation containing a network description and
+        convert to OpenNebulaNetwork object.
+
+        @rtype:  L{OpenNebulaNetwork}
+        @return: The newly extracted L{OpenNebulaNetwork}.
+        """
+        return OpenNebulaNetwork(id=element.findtext('ID'),
+                      name=element.findtext('NAME'),
+                      address=element.findtext('ADDRESS'),
+                      size=element.findtext('SIZE'),
+                      driver=self.connection.driver,
+                      extra={'public': element.findtext('PUBLIC')})
+
+
+class OpenNebula_3_2_NodeDriver(OpenNebula_3_0_NodeDriver):
+    """
+    OpenNebula.org node driver for OpenNebula.org v3.2.
+    """
+
+    def list_sizes(self, location=None):
+        """
+        Return list of sizes on a provider.
+
+        See L{NodeDriver.list_sizes} for more args.
+
+        @rtype:  C{list} of L{OpenNebulaNodeSize}
+        @return: List of compute node sizes supported by the cloud provider.
+        """
+        return self._to_sizes(self.connection.request('/instance_type').object)
+
+    def _to_sizes(self, object):
+        """
+        Request a list of instance types and convert that list to a list of
+        OpenNebulaNodeSize objects.
+
+        Request a list of instance types from the OpenNebula web interface,
+        and issue a request to convert each XML object representation of an
+        instance type to an OpenNebulaNodeSize object.
+
+        @rtype:  C{list} of L{OpenNebulaNodeSize}
+        @return: List of instance types.
+        """
+        sizes = []
+        ids = 1
+        for element in object.findall('INSTANCE_TYPE'):
+            sizes.append(OpenNebulaNodeSize(id=ids,
+                         name=element.findtext('NAME'),
+                         ram=int(element.findtext('MEMORY'))
+                             if element.findtext('MEMORY', None) else None,
+                         cpu=float(element.findtext('CPU'))
+                             if element.findtext('CPU', None) else None,
+                         vcpu=int(element.findtext('VCPU'))
+                             if element.findtext('VCPU', None) else None,
+                         disk=element.findtext('DISK', None),
+                         bandwidth=float(element.findtext('BANDWIDTH'))
+                             if element.findtext('BANDWIDTH', None) else None,
+                         price=float(element.findtext('PRICE'))
+                             if element.findtext('PRICE', None) else None,
+                         driver=self))
+            ids += 1
+
+        return sizes

Added: libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_25.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_25.xml?rev=1212736&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_25.xml (added)
+++ libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_25.xml Sat Dec 
10 06:05:28 2011
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<COMPUTE href='http://www.opennebula.org/compute/25'>
+    <ID>25</ID>
+    <NAME>Compute 25</NAME>
+    <INSTANCE_TYPE>none</INSTANCE_TYPE>
+    <STATE>none</STATE>
+    <NIC>
+        <NETWORK href='http://www.opennebula.org/network/5' name='Network 5'/>
+        <IP>192.168.0.3</IP>
+        <MAC>02:00:c0:a8:00:03</MAC>
+    </NIC>
+    <NIC>
+        <NETWORK href='http://www.opennebula.org/network/15' name='Network 
15'/>
+        <IP>192.168.1.3</IP>
+        <MAC>02:00:c0:a8:01:03</MAC>
+    </NIC>
+    <CONTEXT>
+        <HOSTNAME>compute-25</HOSTNAME>
+    </CONTEXT>
+</COMPUTE>

Modified: 
libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_collection.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_collection.xml?rev=1212736&r1=1212735&r2=1212736&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_collection.xml 
(original)
+++ libcloud/trunk/test/compute/fixtures/opennebula_2_0/compute_collection.xml 
Sat Dec 10 06:05:28 2011
@@ -2,4 +2,5 @@
 <COMPUTE_COLLECTION>  
     <COMPUTE href="http://www.opennebula.org/compute/5"; name="Compute 5"/>  
     <COMPUTE href="http://www.opennebula.org/compute/15"; name="Compute 15"/>
+    <COMPUTE href="http://www.opennebula.org/compute/25"; name="Compute 25"/>
 </COMPUTE_COLLECTION>

Added: libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_15.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_15.xml?rev=1212736&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_15.xml (added)
+++ libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_15.xml Sat Dec 
10 06:05:28 2011
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NETWORK>
+    <ID>15</ID>
+    <NAME>Network 15</NAME>
+    <ADDRESS>192.168.1.0</ADDRESS>
+    <SIZE>256</SIZE>
+    <PUBLIC>NO</PUBLIC>
+</NETWORK>

Added: libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_5.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_5.xml?rev=1212736&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_5.xml (added)
+++ libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_5.xml Sat Dec 
10 06:05:28 2011
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NETWORK>
+    <ID>5</ID>
+    <NAME>Network 5</NAME>
+    <ADDRESS>192.168.0.0</ADDRESS>
+    <SIZE>256</SIZE>
+    <PUBLIC>YES</PUBLIC>
+</NETWORK>

Added: 
libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_collection.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_collection.xml?rev=1212736&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_collection.xml 
(added)
+++ libcloud/trunk/test/compute/fixtures/opennebula_3_0/network_collection.xml 
Sat Dec 10 06:05:28 2011
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NETWORK_COLLECTION>  
+    <NETWORK href="http://www.opennebula.org/network/5"; name="Network 5"/>  
+    <NETWORK href="http://www.opennebula.org/network/15"; name="Network 15"/>
+</NETWORK_COLLECTION>

Added: 
libcloud/trunk/test/compute/fixtures/opennebula_3_2/instance_type_collection.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/opennebula_3_2/instance_type_collection.xml?rev=1212736&view=auto
==============================================================================
--- 
libcloud/trunk/test/compute/fixtures/opennebula_3_2/instance_type_collection.xml
 (added)
+++ 
libcloud/trunk/test/compute/fixtures/opennebula_3_2/instance_type_collection.xml
 Sat Dec 10 06:05:28 2011
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<INSTANCE_TYPE_COLLECTION>  
+    <INSTANCE_TYPE href="http://www.opennebula.org/instance_type/small";>
+        <NAME>small</NAME>
+        <CPU>1</CPU>
+        <MEMORY>1024</MEMORY>
+    </INSTANCE_TYPE>
+    <INSTANCE_TYPE href="http://www.opennebula.org/instance_type/medium";>
+        <NAME>medium</NAME>
+        <CPU>4</CPU>
+        <MEMORY>4096</MEMORY>
+    </INSTANCE_TYPE>
+    <INSTANCE_TYPE href="http://www.opennebula.org/instance_type/large";>
+        <NAME>large</NAME>
+        <CPU>8</CPU>
+        <MEMORY>8192</MEMORY>
+    </INSTANCE_TYPE>
+</INSTANCE_TYPE_COLLECTION>

Modified: libcloud/trunk/test/compute/test_opennebula.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_opennebula.py?rev=1212736&r1=1212735&r2=1212736&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_opennebula.py (original)
+++ libcloud/trunk/test/compute/test_opennebula.py Sat Dec 10 06:05:28 2011
@@ -31,12 +31,28 @@ from libcloud.compute.base import Node, 
 from libcloud.compute.drivers.opennebula import *
 
 from test.file_fixtures import ComputeFileFixtures
+from libcloud.common.types import InvalidCredsError
+from test import MockResponse, MockHttp
 from test.compute import TestCaseMixin
-from test import MockHttp
 
 from test.secrets import OPENNEBULA_PARAMS
 
 
+class OpenNebula_ResponseTests(unittest.TestCase):
+    XML = """<?xml version="1.0" encoding="UTF-8"?><root/>"""
+
+    def test_unauthorized_response(self):
+        http_response = MockResponse(httplib.UNAUTHORIZED,
+                                     OpenNebula_ResponseTests.XML,
+                                     headers={'content-type':
+                                              'application/xml'})
+        try:
+            OpenNebulaResponse(http_response, None).parse_body()
+        except InvalidCredsError:
+            exceptionType = sys.exc_info()[0]
+            self.assertEqual(exceptionType, type(InvalidCredsError()))
+
+
 class OpenNebula_1_4_Tests(unittest.TestCase, TestCaseMixin):
     """
     OpenNebula.org test suite for OpenNebula v1.4.
@@ -217,7 +233,7 @@ class OpenNebula_1_4_Tests(unittest.Test
         self.assertEqual(network.address, '192.168.1.0')
         self.assertEqual(network.size, '256')
 
-    def test_node_action(self):
+    def test_ex_node_action(self):
         """
         Test ex_node_action functionality.
         """
@@ -228,7 +244,7 @@ class OpenNebula_1_4_Tests(unittest.Test
 
 class OpenNebula_2_0_Tests(unittest.TestCase, TestCaseMixin):
     """
-    OpenNebula.org test suite for OpenNebula v2.0 through v3.2.
+    OpenNebula.org test suite for OpenNebula v2.0 through v2.2.
     """
 
     def setUp(self):
@@ -304,7 +320,7 @@ class OpenNebula_2_0_Tests(unittest.Test
         """
         nodes = self.driver.list_nodes()
 
-        self.assertEqual(len(nodes), 2)
+        self.assertEqual(len(nodes), 3)
         node = nodes[0]
         self.assertEqual(node.id, '5')
         self.assertEqual(node.name, 'Compute 5')
@@ -323,6 +339,18 @@ class OpenNebula_2_0_Tests(unittest.Test
         self.assertEqual(node.private_ips, [])
         self.assertTrue(len([size for size in self.driver.list_sizes() \
                         if size.id == node.size.id]) == 1)
+        self.assertEqual(node.size.id, '1')
+        self.assertEqual(node.size.name, 'small')
+        self.assertEqual(node.size.ram, 1024)
+        self.assertTrue(node.size.cpu is None or isinstance(size.cpu, int))
+        self.assertTrue(node.size.vcpu is None or isinstance(size.vcpu, int))
+        self.assertEqual(node.size.cpu, 1)
+        self.assertEqual(node.size.vcpu, None)
+        self.assertEqual(node.size.disk, None)
+        self.assertEqual(node.size.bandwidth, None)
+        self.assertEqual(node.size.price, None)
+        self.assertTrue(len([image for image in self.driver.list_images() \
+                        if image.id == node.image.id]) == 1)
         self.assertEqual(node.image.id, '5')
         self.assertEqual(node.image.name, 'Ubuntu 9.04 LAMP')
         self.assertEqual(node.image.extra['type'], 'DISK')
@@ -347,12 +375,44 @@ class OpenNebula_2_0_Tests(unittest.Test
         self.assertEqual(node.private_ips, [])
         self.assertTrue(len([size for size in self.driver.list_sizes() \
                         if size.id == node.size.id]) == 1)
+        self.assertEqual(node.size.id, '1')
+        self.assertEqual(node.size.name, 'small')
+        self.assertEqual(node.size.ram, 1024)
+        self.assertTrue(node.size.cpu is None or isinstance(size.cpu, int))
+        self.assertTrue(node.size.vcpu is None or isinstance(size.vcpu, int))
+        self.assertEqual(node.size.cpu, 1)
+        self.assertEqual(node.size.vcpu, None)
+        self.assertEqual(node.size.disk, None)
+        self.assertEqual(node.size.bandwidth, None)
+        self.assertEqual(node.size.price, None)
+        self.assertTrue(len([image for image in self.driver.list_images() \
+                        if image.id == node.image.id]) == 1)
         self.assertEqual(node.image.id, '15')
         self.assertEqual(node.image.name, 'Ubuntu 9.04 LAMP')
         self.assertEqual(node.image.extra['type'], 'DISK')
         self.assertEqual(node.image.extra['target'], 'hda')
         context = node.extra['context']
         self.assertEqual(context['hostname'], 'compute-15')
+        node = nodes[2]
+        self.assertEqual(node.id, '25')
+        self.assertEqual(node.name, 'Compute 25')
+        self.assertEqual(node.state,
+                         OpenNebulaNodeDriver.NODE_STATE_MAP['UNKNOWN'])
+        self.assertEqual(node.public_ips[0].id, '5')
+        self.assertEqual(node.public_ips[0].name, 'Network 5')
+        self.assertEqual(node.public_ips[0].address, '192.168.0.3')
+        self.assertEqual(node.public_ips[0].size, 1)
+        self.assertEqual(node.public_ips[0].extra['mac'], '02:00:c0:a8:00:03')
+        self.assertEqual(node.public_ips[1].id, '15')
+        self.assertEqual(node.public_ips[1].name, 'Network 15')
+        self.assertEqual(node.public_ips[1].address, '192.168.1.3')
+        self.assertEqual(node.public_ips[1].size, 1)
+        self.assertEqual(node.public_ips[1].extra['mac'], '02:00:c0:a8:01:03')
+        self.assertEqual(node.private_ips, [])
+        self.assertEqual(node.size, None)
+        self.assertEqual(node.image, None)
+        context = node.extra['context']
+        self.assertEqual(context['hostname'], 'compute-25')
 
     def test_list_images(self):
         """
@@ -457,6 +517,104 @@ class OpenNebula_2_0_Tests(unittest.Test
         self.assertEqual(network.size, '256')
 
 
+class OpenNebula_3_0_Tests(unittest.TestCase, TestCaseMixin):
+    """
+    OpenNebula.org test suite for OpenNebula v3.0.
+    """
+
+    def setUp(self):
+        """
+        Setup test environment.
+        """
+        OpenNebulaNodeDriver.connectionCls.conn_classes = (
+            OpenNebula_3_0_MockHttp, OpenNebula_3_0_MockHttp)
+        self.driver = OpenNebulaNodeDriver(*OPENNEBULA_PARAMS + ('3.0',))
+
+    def test_ex_list_networks(self):
+        """
+        Test ex_list_networks functionality.
+        """
+        networks = self.driver.ex_list_networks()
+
+        self.assertEqual(len(networks), 2)
+        network = networks[0]
+        self.assertEqual(network.id, '5')
+        self.assertEqual(network.name, 'Network 5')
+        self.assertEqual(network.address, '192.168.0.0')
+        self.assertEqual(network.size, '256')
+        self.assertEqual(network.extra['public'], 'YES')
+        network = networks[1]
+        self.assertEqual(network.id, '15')
+        self.assertEqual(network.name, 'Network 15')
+        self.assertEqual(network.address, '192.168.1.0')
+        self.assertEqual(network.size, '256')
+        self.assertEqual(network.extra['public'], 'NO')
+
+    def test_ex_node_set_save_name(self):
+        """
+        Test ex_node_action functionality.
+        """
+        image = NodeImage(id=5, name='Ubuntu 9.04 LAMP', driver=self.driver)
+        node = Node(5, None, None, None, None, self.driver, image=image)
+        ret = self.driver.ex_node_set_save_name(node, 'test')
+        self.assertTrue(ret)
+
+
+class OpenNebula_3_2_Tests(unittest.TestCase, TestCaseMixin):
+    """
+    OpenNebula.org test suite for OpenNebula v3.2.
+    """
+
+    def setUp(self):
+        """
+        Setup test environment.
+        """
+        OpenNebulaNodeDriver.connectionCls.conn_classes = (
+            OpenNebula_3_2_MockHttp, OpenNebula_3_2_MockHttp)
+        self.driver = OpenNebulaNodeDriver(*OPENNEBULA_PARAMS + ('3.2',))
+
+    def test_list_sizes(self):
+        """
+        Test ex_list_networks functionality.
+        """
+        sizes = self.driver.list_sizes()
+
+        self.assertEqual(len(sizes), 3)
+        size = sizes[0]
+        self.assertEqual(size.id, '1')
+        self.assertEqual(size.name, 'small')
+        self.assertEqual(size.ram, 1024)
+        self.assertTrue(size.cpu is None or isinstance(size.cpu, float))
+        self.assertTrue(size.vcpu is None or isinstance(size.vcpu, int))
+        self.assertEqual(size.cpu, 1)
+        self.assertEqual(size.vcpu, None)
+        self.assertEqual(size.disk, None)
+        self.assertEqual(size.bandwidth, None)
+        self.assertEqual(size.price, None)
+        size = sizes[1]
+        self.assertEqual(size.id, '2')
+        self.assertEqual(size.name, 'medium')
+        self.assertEqual(size.ram, 4096)
+        self.assertTrue(size.cpu is None or isinstance(size.cpu, float))
+        self.assertTrue(size.vcpu is None or isinstance(size.vcpu, int))
+        self.assertEqual(size.cpu, 4)
+        self.assertEqual(size.vcpu, None)
+        self.assertEqual(size.disk, None)
+        self.assertEqual(size.bandwidth, None)
+        self.assertEqual(size.price, None)
+        size = sizes[2]
+        self.assertEqual(size.id, '3')
+        self.assertEqual(size.name, 'large')
+        self.assertEqual(size.ram, 8192)
+        self.assertTrue(size.cpu is None or isinstance(size.cpu, float))
+        self.assertTrue(size.vcpu is None or isinstance(size.vcpu, int))
+        self.assertEqual(size.cpu, 8)
+        self.assertEqual(size.vcpu, None)
+        self.assertEqual(size.disk, None)
+        self.assertEqual(size.bandwidth, None)
+        self.assertEqual(size.price, None)
+
+
 class OpenNebula_1_4_MockHttp(MockHttp):
     """
     Mock HTTP server for testing v1.4 of the OpenNebula.org compute driver.
@@ -675,6 +833,24 @@ class OpenNebula_2_0_MockHttp(MockHttp):
             return (httplib.NO_CONTENT, body, {},
                     httplib.responses[httplib.NO_CONTENT])
 
+    def _compute_25(self, method, url, body, headers):
+        """
+        Compute entry resource.
+        """
+        if method == 'GET':
+            body = self.fixtures.load('compute_25.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        if method == 'PUT':
+            body = ""
+            return (httplib.ACCEPTED, body, {},
+                    httplib.responses[httplib.ACCEPTED])
+
+        if method == 'DELETE':
+            body = ""
+            return (httplib.NO_CONTENT, body, {},
+                    httplib.responses[httplib.NO_CONTENT])
+
     def _storage_5(self, method, url, body, headers):
         """
         Storage entry resource.
@@ -727,5 +903,68 @@ class OpenNebula_2_0_MockHttp(MockHttp):
             return (httplib.NO_CONTENT, body, {},
                     httplib.responses[httplib.NO_CONTENT])
 
+
+class OpenNebula_3_0_MockHttp(OpenNebula_2_0_MockHttp):
+    """
+    Mock HTTP server for testing v3.0 of the OpenNebula.org compute driver.
+    """
+
+    fixtures_3_0 = ComputeFileFixtures('opennebula_3_0')
+
+    def _network(self, method, url, body, headers):
+        """
+        Network pool resources.
+        """
+        if method == 'GET':
+            body = self.fixtures_3_0.load('network_collection.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        if method == 'POST':
+            body = self.fixtures.load('network_5.xml')
+            return (httplib.CREATED, body, {},
+                    httplib.responses[httplib.CREATED])
+
+    def _network_5(self, method, url, body, headers):
+        """
+        Network entry resource.
+        """
+        if method == 'GET':
+            body = self.fixtures_3_0.load('network_5.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        if method == 'DELETE':
+            body = ""
+            return (httplib.NO_CONTENT, body, {},
+                    httplib.responses[httplib.NO_CONTENT])
+
+    def _network_15(self, method, url, body, headers):
+        """
+        Network entry resource.
+        """
+        if method == 'GET':
+            body = self.fixtures_3_0.load('network_15.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        if method == 'DELETE':
+            body = ""
+            return (httplib.NO_CONTENT, body, {},
+                    httplib.responses[httplib.NO_CONTENT])
+
+
+class OpenNebula_3_2_MockHttp(OpenNebula_3_0_MockHttp):
+    """
+    Mock HTTP server for testing v3.2 of the OpenNebula.org compute driver.
+    """
+
+    fixtures_3_2 = ComputeFileFixtures('opennebula_3_2')
+
+    def _instance_type(self, method, url, body, headers):
+        """
+        Instance type pool.
+        """
+        if method == 'GET':
+            body = self.fixtures_3_2.load('instance_type_collection.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 if __name__ == '__main__':
     sys.exit(unittest.main())


Reply via email to