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())
signature.asc
Description: This is a digitally signed message part
