I was wondering if someone could take a look at the following attached
patch. It's a new component to the Libcloud library to support virtual
network drivers.

I added a driver for OpenNebula, covering versions 1.4 through to the present 
version of OpenNebula.
Furthermore, I added additional test classes for testing the new driver.

My next step is to test in a mockup of my cloud computing production 
environment.

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

=== added directory 'libcloud/network'
=== added file 'libcloud/network/__init__.py'
--- libcloud/network/__init__.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/__init__.py	2011-11-07 08:11:49 +0000
@@ -0,0 +1,3 @@
+"""
+Module for working with virtual networks.
+"""

=== added file 'libcloud/network/base.py'
--- libcloud/network/base.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/base.py	2011-11-09 00:48:45 +0000
@@ -0,0 +1,167 @@
+# 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.
+
+"""
+Provides base classes for working with virtual networks.
+"""
+
+
+from libcloud.common.base import ConnectionUserAndKey, BaseDriver
+
+
+__all__ = ['Network',
+           'NetworkDriver']
+
+
+class Network(object):
+    """
+    Provide a common interface for handling networks of all types.
+
+    Network objects are analogous to physical switches connecting two or
+    more physical nodes together. The Network object provides the interface in
+    libcloud through which we can manipulate networks in different cloud
+    providers in the same way. Network objects don't actually do much directly
+    themselves, instead the network driver handles the connection to the
+    network.
+
+    You don't normally create a network object yourself; instead you use
+    a driver and then have that create the network for you.
+
+    >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+    >>> driver = DummyNetworkDriver()
+    >>> network = driver.create_network()
+    >>> network = driver.list_networks()[0]
+    >>> network.name
+    'dummy-1'
+    """
+
+    def __init__(self, id, name, address, size, driver, extra=None):
+        self.id = str(id)
+        self.name = name
+        self.address = address
+        self.size = size
+        self.driver = driver
+        self.extra = extra or {}
+
+    def get_uuid(self):
+        """
+        Unique hash for this network.
+
+        @return: C{string}
+
+        The hash is a function of an SHA1 hash of the network's ID and
+        its driver which means that it should be unique between all
+        networks. In some subclasses (e.g. GoGrid) there is no ID
+        available so the public IP address is used. This means that,
+        unlike a properly done system UUID, the same UUID may mean a
+        different system install at a different time
+
+        >>> from libcloud.network.drivers.dummy import DummyNetworkDriver
+        >>> driver = DummyNetworkDriver()
+        >>> network = driver.create_network()
+        >>> network.get_uuid()
+        'd3748461511d8b9b0e0bfa0d4d3383a619a2bb9f'
+
+        Note, for example, that this example will always produce the
+        same UUID!
+        """
+        return hashlib.sha1("%s:%d" % (self.id, self.driver.type)).hexdigest()
+
+    def destroy(self):
+        """
+        Destroy this network.
+
+        @return: C{bool}
+
+        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 self.driver.destroy_network(self)
+
+    def __repr__(self):
+        return (('<NodeNetwork: uuid=%s, name=%s, address=%s, size=%s, '
+                 'provider=%s ...>')
+                % (self.uuid, self.name, self.address, self.size,
+                   self.driver.name))
+
+
+class NetworkDriver(BaseDriver):
+    """
+    A base NetworkDriver to derive from.
+    """
+
+    connectionCls = ConnectionUserAndKey
+    name = None
+    type = None
+    port = None
+    features = {"create_network": []}
+
+    def __init__(self, key, secret=None, secure=True, host=None, port=None,
+                 api_version=None):
+        super(NetworkDriver, self).__init__(key=key, secret=secret,
+                                     secure=secure, host=host, port=port,
+                                     api_version=api_version)
+
+    def list_networks(self, location=None):
+        """
+        List virtual networks on a provider.
+
+        @return: C{list} of L{Network} objects.
+        """
+        raise NotImplementedError(
+            'list_networks not implemented for this driver.')
+
+    def destroy_network(self, network):
+        """
+        Destroy a network.
+
+        Depending upon the provider, this may destroy all data associated with
+        the network.
+
+        @return: C{bool} True if the destroy was successful, otherwise False.
+        """
+        raise NotImplementedError(
+            'destroy_network not implemented for this driver.')
+
+    def create_network(self, **kwargs):
+        """
+        Create a new network instance.
+
+        @keyword    name:   String with a name for this new network (required)
+        @type       name:   str
+
+        @keyword    address: IP address based on the Classful Address scheme.
+        @type       address: str
+
+        @keyword    size:   Size of the network in number of IPs within the
+                            network. Size of 255 corresponds to
+                            [address]/24 using the Classless Inter-
+                            Domain Routing scheme.
+        @type       size:   int
+
+        @return: The newly created L{Network}.
+        """
+        raise NotImplementedError(
+            'create_network not implemented for this driver.')
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

=== added directory 'libcloud/network/drivers'
=== added file 'libcloud/network/drivers/__init__.py'
--- libcloud/network/drivers/__init__.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/drivers/__init__.py	2011-11-07 08:11:49 +0000
@@ -0,0 +1,22 @@
+# 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.
+
+"""
+Drivers for working with different providers.
+"""
+
+
+__all__ = ['dummy',
+           'opennebula']

=== added file 'libcloud/network/drivers/dummy.py'
--- libcloud/network/drivers/dummy.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/drivers/dummy.py	2011-11-09 03:44:59 +0000
@@ -0,0 +1,124 @@
+# 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.
+
+"""
+Dummy Driver
+"""
+
+import uuid
+
+from libcloud.network.base import NetworkDriver, Network
+from libcloud.network.types import Provider
+
+
+class DummyNetworkDriver(NetworkDriver):
+    """
+    Dummy network driver.
+
+    This is a fake driver which appears to always create or destroy
+    networks successfully.
+
+    >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+    >>> driver = DummyNetworkDriver()
+    >>> network = driver.create_network()
+    >>> network = driver.list_networks()[0]
+    >>> network.name
+    'dummy-1'
+    """
+
+    name = 'Dummy Network Provider'
+    type = Provider.DUMMY
+
+    def __init__(self):
+        self.nl = []
+
+    def get_uuid(self):
+        return str(uuid.uuid4())
+
+    def list_networks(self):
+        """
+        List the networks known to a particular driver.
+
+        >>> from libcloud.network.drivers.dummy import DummyNetworkDriver
+        >>> driver = DummyNetworkDriver()
+        >>> network_list = driver.list_networks()
+        >>> print network_list
+        []
+
+        Each item in the list returned is a network object from which you
+        can carry out any network actions you wish.
+
+        As more networks are added, list_networks will return them.
+
+        >>> network = driver.create_network()
+        >>> sorted([network.name for network in driver.list_networks()])
+        ['dummy-1']
+        """
+        return self.nl
+
+    def destroy_network(self, network):
+        """
+        Remove the network from the network list.
+
+        >>> from libcloud.network.drivers.dummy import DummyNetworkDriver
+        >>> driver = DummyNetworkDriver()
+        >>> network = driver.create_network()
+        >>> network = [network for network in driver.list_networks()
+        ...     if network.name == 'dummy-1'][0]
+        >>> driver.destroy_network(network)
+        True
+        >>> [network for network in driver.list_networks()
+        ...     if network.name == 'dummy-1']
+        []
+        """
+
+        self.nl.remove(network)
+        return True
+
+    def create_network(self, **kwargs):
+        """
+        Creates a dummy network; the network id is equal to the number of
+        networks in the network list.
+
+        >>> from libcloud.network.drivers.dummy import DummyNetworkDriver
+        >>> driver = DummyNetworkDriver()
+        >>> sorted([network.name for network in driver.list_networks()])
+        []
+        >>> network = driver.create_network()
+        >>> sorted([network.name for network in driver.list_networks()])
+        ['dummy-1']
+        >>> driver.create_network().name
+        'dummy-2'
+        >>> driver.destroy_network(network)
+        True
+        >>> sorted([network.name for network in driver.list_networks()])
+        ['dummy-2']
+
+        See L{NetworkDriver.create_node} for more keyword args.
+        """
+        l = len(self.nl) + 1
+        n = Network(id=l,
+                 name='dummy-%d' % l,
+                 address=['127.0.%d.0' % l],
+                 size=255,
+                 driver=self,
+                 extra={'foo': 'bar'})
+        self.nl.append(n)
+        return n
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

=== added file 'libcloud/network/drivers/opennebula.py'
--- libcloud/network/drivers/opennebula.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/drivers/opennebula.py	2011-11-09 02:44:44 +0000
@@ -0,0 +1,150 @@
+# 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 xml.etree import ElementTree as ET
+from base64 import b64encode
+import httplib
+import hashlib
+
+from libcloud.common.base import ConnectionUserAndKey, XmlResponse
+from libcloud.network.base import NetworkDriver, Network
+from libcloud.network.types import Provider
+
+API_HOST = ''
+API_PORT = (4567, 443)
+API_SECURE = True
+DEFAULT_API_VERSION = '3.0'
+
+
+class OpenNebulaResponse(XmlResponse):
+    """
+    Response class for the OpenNebula driver.
+    """
+
+    def success(self):
+        i = int(self.status)
+        return i >= 200 and i <= 299
+
+    def parse_error(self):
+        if int(self.status) == httplib.UNAUTHORIZED:
+            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 OpenNebulaNetworkDriver(NetworkDriver):
+    """
+    OpenNebula network driver.
+    """
+
+    connectionCls = OpenNebulaConnection
+    name = 'OpenNebula'
+    type = Provider.OPENNEBULA
+
+    def __new__(cls, key, secret=None, api_version=DEFAULT_API_VERSION,
+                **kwargs):
+        if cls is OpenNebulaNetworkDriver:
+            if api_version == '1.4' or api_version == '2.0' or \
+                api_version == '2.2' or api_version == '3.0':
+                cls = OpenNebula_1_4_NetworkDriver
+            else:
+                raise NotImplementedError(
+                    "No OpenNebulaNodeDriver found for API version %s" %
+                    (api_version))
+            return super(OpenNebulaNetworkDriver, cls).__new__(cls)
+
+    def list_networks(self):
+        return self._to_networks(self.connection.request('/network').object)
+
+    def destroy_network(self, network):
+        url = '/network/%s' % (str(network.id))
+        resp = self.connection.request(url, method='DELETE')
+
+        return resp.status == 204
+
+    def create_network(self, **kwargs):
+        """
+        Create a new network instance.
+
+        @keyword    public:   String indicating whether the network is public
+                              for all users (YES || NO).
+        @type       public:   str
+
+        See L{NetworkDriver.create_node} for more keyword args.
+        """
+        network = ET.Element('NETWORK')
+
+        name = ET.SubElement(network, 'NAME')
+        name.text = kwargs['name']
+
+        address = ET.SubElement(network, 'ADDRESS')
+        address.text = kwargs['address']
+
+        size = ET.SubElement(network, 'SIZE')
+        size.text = str(kwargs['size'])
+
+        if 'public' in kwargs:
+            public = ET.SubElement(network, 'PUBLIC')
+            public.text = kwargs['public']
+
+        xml = ET.tostring(network)
+        network = self.connection.request('/network', method='POST',
+                                       data=xml).object
+
+        return self._to_network(network)
+
+    def _to_networks(self, object):
+        networks = []
+        for element in object.findall('NETWORK'):
+            network_id = element.attrib['href'].partition('/network/')[2]
+            network_element = self.connection.request(('/network/%s' % (
+                                             network_id))).object
+            networks.append(self._to_network(network_element))
+
+        return networks
+
+    def _to_network(self, element):
+        return Network(id=element.findtext('ID'),
+                      name=element.findtext('NAME'),
+                      address=element.findtext('ADDRESS'),
+                      size=int(element.findtext('SIZE')),
+                      driver=self.connection.driver)
+
+
+class OpenNebula_1_4_NetworkDriver(OpenNebulaNetworkDriver):
+    pass

=== added file 'libcloud/network/providers.py'
--- libcloud/network/providers.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/providers.py	2011-11-07 08:11:49 +0000
@@ -0,0 +1,36 @@
+# 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.
+"""
+Provider related utilities
+"""
+
+from libcloud.utils import get_driver as _get_provider_driver
+from libcloud.compute.types import Provider
+
+__all__ = [
+    "Provider",
+    "DRIVERS",
+    "get_driver"]
+
+DRIVERS = {
+    Provider.DUMMY:
+        ('libcloud.network.drivers.dummy', 'DummyNodeDriver'),
+    Provider.OPENNEBULA:
+        ('libcloud.network.drivers.opennebula', 'OpenNebulaNetworkDriver'),
+}
+
+
+def get_driver(provider):
+    return _get_provider_driver(DRIVERS, provider)

=== added file 'libcloud/network/types.py'
--- libcloud/network/types.py	1970-01-01 00:00:00 +0000
+++ libcloud/network/types.py	2011-11-07 08:11:49 +0000
@@ -0,0 +1,32 @@
+# 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.
+
+"""
+Base types used by other parts of libcloud.
+"""
+
+
+__all__ = ['Provider']
+
+
+class Provider(object):
+    """
+    Defines for each of the supported providers
+
+    @cvar DUMMY: Example provider
+    @cvar OPENNEBULA: OpenNebula.org
+    """
+    DUMMY = 0
+    OPENNEBULA = 16

=== modified file 'setup.py'
--- setup.py	2011-10-16 15:03:49 +0000
+++ setup.py	2011-11-09 02:28:35 +0000
@@ -30,9 +30,10 @@
 HTML_VIEWSOURCE_BASE = 'https://svn.apache.org/viewvc/libcloud/trunk'
 PROJECT_BASE_DIR = 'http://libcloud.apache.org'
 TEST_PATHS = ['test', 'test/common', 'test/compute', 'test/storage',
-              'test/loadbalancer', 'test/dns']
+              'test/network', 'test/loadbalancer', 'test/dns']
 DOC_TEST_MODULES = [ 'libcloud.compute.drivers.dummy',
                      'libcloud.storage.drivers.dummy',
+                     'libcloud.network.drivers.dummy',
                      'libcloud.dns.drivers.dummy' ]
 
 
@@ -202,6 +203,8 @@
         'libcloud.compute.drivers',
         'libcloud.storage',
         'libcloud.storage.drivers',
+        'libcloud.network',
+        'libcloud.network.drivers',
         'libcloud.drivers',
         'libcloud.loadbalancer',
         'libcloud.loadbalancer.drivers',

=== modified file 'test/__init__.py'
--- test/__init__.py	2011-10-12 21:00:33 +0000
+++ test/__init__.py	2011-11-09 04:29:16 +0000
@@ -46,6 +46,7 @@
                          'expected %d, but %d mock methods were executed'
                          % (expected, actual))
 
+
 class multipleresponse(object):
     """
     A decorator that allows MockHttp objects to return multi responses
@@ -65,7 +66,7 @@
 
 class MockResponse(object):
     """
-    A mock HTTPResponse
+    A mock HTTPResponse.
     """
     headers = {}
     body = StringIO()
@@ -91,6 +92,7 @@
     def msg(self):
         raise NotImplemented
 
+
 class BaseMockHttpObject(object):
     def _get_method_name(self, type, use_param, qs, path):
         meth_name = path.replace('/', '_').replace('.', '_').replace('-', '_')
@@ -101,6 +103,7 @@
             meth_name = '%s_%s' % (meth_name, param)
         return meth_name
 
+
 class MockHttp(BaseMockHttpObject):
     """
     A mock HTTP client/server suitable for testing purposes. This replaces
@@ -188,6 +191,7 @@
         return (httplib.FORBIDDEN, 'Oh Noes!', {'X-Foo': 'fail'},
                 httplib.responses[httplib.FORBIDDEN])
 
+
 class MockHttpTestCase(MockHttp, unittest.TestCase):
     # Same as the MockHttp class, but you can also use assertions in the
     # classes which inherit from this one.
@@ -200,6 +204,7 @@
     def runTest(self):
         pass
 
+
 class StorageMockHttp(MockHttp):
     def putrequest(self, method, action):
         pass
@@ -213,6 +218,7 @@
     def send(self, data):
         pass
 
+
 class MockRawResponse(BaseMockHttpObject):
     """
     Mock RawResponse object suitable for testing.
@@ -283,6 +289,7 @@
             return self
         return self._response
 
+
 if __name__ == "__main__":
     import doctest
     doctest.testmod()

=== modified file 'test/file_fixtures.py'
--- test/file_fixtures.py	2011-10-16 15:03:49 +0000
+++ test/file_fixtures.py	2011-11-09 00:08:31 +0000
@@ -21,6 +21,7 @@
 FIXTURES_ROOT = {
     'compute': 'compute/fixtures',
     'storage': 'storage/fixtures',
+    'network': 'network/fixtures',
     'loadbalancer': 'loadbalancer/fixtures',
     'dns': 'dns/fixtures',
     'openstack': 'compute/fixtures/openstack',
@@ -51,6 +52,11 @@
         super(StorageFileFixtures, self).__init__(fixtures_type='storage',
                                                   sub_dir=sub_dir)
 
+class NetworkFileFixtures(FileFixtures):
+    def __init__(self, sub_dir=''):
+        super(NetworkFileFixtures, self).__init__(fixtures_type='network',
+                                                  sub_dir=sub_dir)
+
 class LoadBalancerFileFixtures(FileFixtures):
     def __init__(self, sub_dir=''):
         super(LoadBalancerFileFixtures, self).__init__(fixtures_type='loadbalancer',

=== added directory 'test/network'
=== added file 'test/network/__init__.py'
--- test/network/__init__.py	1970-01-01 00:00:00 +0000
+++ test/network/__init__.py	2011-11-09 01:45:38 +0000
@@ -0,0 +1,41 @@
+# 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.
+
+from libcloud.network.base import Network
+
+
+class TestCaseMixin(object):
+
+    def test_list_networks_response(self):
+        networks = self.driver.list_networks()
+        self.assertTrue(isinstance(networks, list))
+        for network in networks:
+            self.assertTrue(isinstance(network, Network))
+
+    def test_destroy_network_response(self):
+        network = self.driver.list_networks()[0]
+        ret = self.driver.destroy_network(network)
+        self.assertTrue(isinstance(ret, bool))
+
+    def test_create_network_response(self):
+        network = self.driver.create_network(name='network-name',
+                                     address='192.168.0.1',
+                                     size=255)
+        self.assertTrue(isinstance(network, Network))
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

=== added directory 'test/network/fixtures'
=== added directory 'test/network/fixtures/opennebula'
=== added file 'test/network/fixtures/opennebula/network.xml'
--- test/network/fixtures/opennebula/network.xml	1970-01-01 00:00:00 +0000
+++ test/network/fixtures/opennebula/network.xml	2011-11-09 04:42:26 +0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NETWORK href="http://www.opennebula.org/network/5";>
+    <ID>5</ID>
+    <NAME>Network</NAME>
+    <ADDRESS>192.168.0.1</ADDRESS>
+    <SIZE>255</SIZE>
+</NETWORK>

=== added file 'test/network/fixtures/opennebula/networks.xml'
--- test/network/fixtures/opennebula/networks.xml	1970-01-01 00:00:00 +0000
+++ test/network/fixtures/opennebula/networks.xml	2011-11-09 01:49:50 +0000
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<NETWORKS>  
+    <NETWORK href="http://www.opennebula.org/network/5"; />  
+    <NETWORK href="http://www.opennebula.org/network/15"; />
+</NETWORKS>

=== added file 'test/network/test_base.py'
--- test/network/test_base.py	1970-01-01 00:00:00 +0000
+++ test/network/test_base.py	2011-11-09 01:18:07 +0000
@@ -0,0 +1,49 @@
+# 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.
+
+import unittest
+import sys
+
+from libcloud.common.base import ConnectionKey, ConnectionUserAndKey
+from libcloud.network.base import Network, NetworkDriver
+from libcloud.common.base import Response
+
+from test import MockResponse           # pylint: disable-msg=E0611
+
+
+class FakeDriver(object):
+    type = 0
+
+
+class BaseTests(unittest.TestCase):
+
+    def test_base_network(self):
+        Network(id=0, name=0, address=0, size=0, driver=FakeDriver())
+
+    def test_base_response(self):
+        Response(MockResponse(status=200, body='foo'), ConnectionKey('foo'))
+
+    def test_base_network_driver(self):
+        NetworkDriver('foo')
+
+    def test_base_connection_key(self):
+        ConnectionKey('foo')
+
+    def test_base_connection_userkey(self):
+        ConnectionUserAndKey('foo', 'bar')
+
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

=== added file 'test/network/test_opennebula.py'
--- test/network/test_opennebula.py	1970-01-01 00:00:00 +0000
+++ test/network/test_opennebula.py	2011-11-09 05:37:09 +0000
@@ -0,0 +1,129 @@
+# 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.
+
+import unittest
+import httplib
+import sys
+
+from libcloud.common.types import InvalidCredsError, MalformedResponseError
+from libcloud.network.drivers.opennebula import OpenNebulaNetworkDriver
+from libcloud.network.providers import get_driver
+from libcloud.network.types import Provider
+from libcloud.network.drivers.opennebula import (
+    OpenNebula_1_4_NetworkDriver)
+from libcloud.network.base import Network
+
+from test.file_fixtures import NetworkFileFixtures
+from test.network import TestCaseMixin
+from test import MockHttp
+
+from test.secrets import OPENNEBULA_PARAMS
+
+
+class OpenNebula_1_4_Tests(unittest.TestCase, TestCaseMixin):
+
+    driver_klass = OpenNebula_1_4_NetworkDriver
+    driver_args = OPENNEBULA_PARAMS
+    driver_kwargs = {}
+
+    @classmethod
+    def create_driver(self):
+        if self is not OpenNebula_1_4_FactoryMethodTests:
+            self.driver_type = self.driver_klass
+        return self.driver_type(*self.driver_args, **self.driver_kwargs)
+
+    def setUp(self):
+        OpenNebulaNetworkDriver.connectionCls.conn_classes = (None,
+                                                           OpenNebulaMockHttp)
+        self.driver = OpenNebulaNetworkDriver(*OPENNEBULA_PARAMS + ('1.4',))
+
+    def test_list_networks(self):
+        networks = self.driver.list_networks()
+        self.assertEqual(len(networks), 2)
+        network = networks[0]
+        self.assertEqual(network.id, '5')
+        self.assertEqual(network.name, 'Network')
+        self.assertEqual(network.address, '192.168.0.1')
+        self.assertEqual(network.size, 255)
+
+    def test_destroy_network(self):
+        network = Network(5, None, None, None, None, self.driver)
+        ret = self.driver.destroy_network(network)
+        self.assertTrue(ret)
+
+    def test_create_network(self):
+        network = self.driver.create_network(name='Network',
+                                       address='192.168.0.1', size=255)
+        self.assertEqual(network.id, '5')
+        self.assertEqual(network.name, 'Network')
+        self.assertEqual(network.address, '192.168.0.1')
+        self.assertEqual(network.size, 255)
+
+
+class OpenNebula_1_4_FactoryMethodTests(OpenNebula_1_4_Tests):
+
+    driver_klass = OpenNebula_1_4_NetworkDriver
+    driver_type = get_driver(Provider.OPENNEBULA)
+    driver_args = OPENNEBULA_PARAMS + ('1.0',)
+
+    def test_factory_method_invalid_version(self):
+        try:
+            self.driver_type(*(OPENNEBULA_PARAMS + ('0.5',)))
+        except NotImplementedError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+
+class OpenNebulaMockHttp(MockHttp):
+
+    fixtures = NetworkFileFixtures('opennebula')
+
+    def _network(self, method, url, body, headers):
+        if method == 'GET':
+            body = self.fixtures.load('networks.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        if method == 'POST':
+            body = self.fixtures.load('network.xml')
+            return (httplib.CREATED, body, {},
+                    httplib.responses[httplib.CREATED])
+
+    def _network_5(self, method, url, body, headers):
+        if method == 'GET':
+            body = self.fixtures.load('network.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 _network_15(self, method, url, body, headers):
+        if method == 'GET':
+            body = self.fixtures.load('network.xml')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

=== modified file 'test/test_file_fixtures.py'
--- test/test_file_fixtures.py	2011-10-12 21:00:33 +0000
+++ test/test_file_fixtures.py	2011-11-09 04:29:43 +0000
@@ -12,11 +12,13 @@
 # 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.
+
 import sys
 import unittest
 
 from test.file_fixtures import ComputeFileFixtures
 
+
 class FileFixturesTests(unittest.TestCase):
 
     def test_success(self):
@@ -27,5 +29,6 @@
         f = ComputeFileFixtures('meta')
         self.assertRaises(IOError, f.load, 'nil')
 
+
 if __name__ == '__main__':
     sys.exit(unittest.main())

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

Reply via email to