An update patch for the OpenNebula compute node for consideration.

Changes:
        * Removed unused JSON import.
        * Added two new node drivers for OpenNebula, for OpenNebula v3.0 and
v3.2, which support additional functionality over the OpenNebula v2.0
driver.
        * Added test suites for those additional capabilities.
        * Removed destroy() from OpenNebulaNetwork which is not supported by
the compute node driver.
        * Removed unused assignments, mostly pertaining to ET.SubElement.
        * Added ability to define the name under which the node image will be
saved after shutting down for v3.0+.
        * Added support for a network attribute for v3.0+.
        * Added support for the upcoming OpenNebula v3.2, and it's new instance
type interface.
        * Added additional tests for a more complete test coverage.

-- 
Hutson Betts
Computer Science and Engineering
Texas A&M University


=== modified file 'libcloud/compute/drivers/opennebula.py'
--- libcloud/compute/drivers/opennebula.py	2011-12-05 00:22:16 +0000
+++ libcloud/compute/drivers/opennebula.py	2011-12-06 07:44:04 +0000
@@ -22,11 +22,6 @@
 
 __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 @@
     '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 @@
         @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 @@
         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 @@
         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 @@
             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 @@
         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 @@
         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_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 @@
         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 @@
 
             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 @@
         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,115 @@
                 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', None)),
+                         cpu=int(element.findtext('CPU', None)),
+                         disk=element.findtext('DISK', None),
+                         bandwidth=element.findtext('BANDWIDTH', None),
+                         price=element.findtext('PRICE', None),
+                         driver=self))
+            ids += 1
+
+        return sizes

=== added file 'test/compute/fixtures/opennebula_2_0/compute_25.xml'
--- test/compute/fixtures/opennebula_2_0/compute_25.xml	1970-01-01 00:00:00 +0000
+++ test/compute/fixtures/opennebula_2_0/compute_25.xml	2011-12-05 06:50:32 +0000
@@ -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 file 'test/compute/fixtures/opennebula_2_0/compute_collection.xml'
--- test/compute/fixtures/opennebula_2_0/compute_collection.xml	2011-12-05 00:22:16 +0000
+++ test/compute/fixtures/opennebula_2_0/compute_collection.xml	2011-12-05 06:17:24 +0000
@@ -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 directory 'test/compute/fixtures/opennebula_3_0'
=== added file 'test/compute/fixtures/opennebula_3_0/network_15.xml'
--- test/compute/fixtures/opennebula_3_0/network_15.xml	1970-01-01 00:00:00 +0000
+++ test/compute/fixtures/opennebula_3_0/network_15.xml	2011-12-05 03:35:24 +0000
@@ -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 file 'test/compute/fixtures/opennebula_3_0/network_5.xml'
--- test/compute/fixtures/opennebula_3_0/network_5.xml	1970-01-01 00:00:00 +0000
+++ test/compute/fixtures/opennebula_3_0/network_5.xml	2011-12-05 03:35:30 +0000
@@ -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 file 'test/compute/fixtures/opennebula_3_0/network_collection.xml'
--- test/compute/fixtures/opennebula_3_0/network_collection.xml	1970-01-01 00:00:00 +0000
+++ test/compute/fixtures/opennebula_3_0/network_collection.xml	2011-12-05 00:38:18 +0000
@@ -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 directory 'test/compute/fixtures/opennebula_3_2'
=== added file 'test/compute/fixtures/opennebula_3_2/instance_type_collection.xml'
--- test/compute/fixtures/opennebula_3_2/instance_type_collection.xml	1970-01-01 00:00:00 +0000
+++ test/compute/fixtures/opennebula_3_2/instance_type_collection.xml	2011-12-05 04:33:14 +0000
@@ -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 file 'test/compute/test_opennebula.py'
--- test/compute/test_opennebula.py	2011-12-05 00:22:16 +0000
+++ test/compute/test_opennebula.py	2011-12-06 07:44:45 +0000
@@ -31,12 +31,27 @@
 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 as detail:
+            self.assertEqual(type(detail), type(InvalidCredsError()))
+
+
 class OpenNebula_1_4_Tests(unittest.TestCase, TestCaseMixin):
     """
     OpenNebula.org test suite for OpenNebula v1.4.
@@ -217,7 +232,7 @@
         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 +243,7 @@
 
 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 +319,7 @@
         """
         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 +338,18 @@
         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 +374,44 @@
         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 +516,104 @@
         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, int))
+        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, int))
+        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, int))
+        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 +832,24 @@
             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 +902,68 @@
             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())

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to