summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/v2/contrib/networks.py117
-rw-r--r--nova/api/openstack/v2/extensions.py11
-rw-r--r--nova/network/api.py26
-rw-r--r--nova/network/manager.py17
-rw-r--r--nova/tests/api/openstack/v2/contrib/test_networks.py137
-rw-r--r--nova/tests/api/openstack/v2/test_extensions.py1
-rw-r--r--nova/tests/fake_network.py6
-rw-r--r--nova/tests/test_network.py61
8 files changed, 374 insertions, 2 deletions
diff --git a/nova/api/openstack/v2/contrib/networks.py b/nova/api/openstack/v2/contrib/networks.py
new file mode 100644
index 000000000..4a96e534f
--- /dev/null
+++ b/nova/api/openstack/v2/contrib/networks.py
@@ -0,0 +1,117 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Grid Dynamics
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed 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 webob import exc
+
+from nova.api.openstack.v2 import extensions
+from nova import exception
+from nova import flags
+from nova import log as logging
+import nova.network.api
+
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.api.openstack.v2.contrib.networks')
+
+
+def network_dict(network):
+ if network:
+ fields = ('bridge', 'vpn_public_port', 'dhcp_start',
+ 'bridge_interface', 'updated_at', 'id', 'cidr_v6',
+ 'deleted_at', 'gateway', 'label', 'project_id',
+ 'vpn_private_address', 'deleted', 'vlan', 'broadcast',
+ 'netmask', 'injected', 'cidr', 'vpn_public_address',
+ 'multi_host', 'dns1', 'host', 'gateway_v6', 'netmask_v6',
+ 'created_at')
+ return dict((field, network[field]) for field in fields)
+ else:
+ return {}
+
+
+class NetworkController(object):
+
+ def __init__(self, network_api=None):
+ self.network_api = network_api or nova.network.api.API()
+
+ def action(self, req, id, body):
+ _actions = {
+ 'disassociate': self._disassociate,
+ }
+
+ for action, data in body.iteritems():
+ try:
+ return _actions[action](req, id, body)
+ except KeyError:
+ msg = _("Network does not have %s action") % action
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ raise exc.HTTPBadRequest(explanation=_("Invalid request body"))
+
+ def _disassociate(self, request, network_id, body):
+ context = request.environ['nova.context']
+ LOG.debug(_("Disassociating network with id %s" % network_id))
+ try:
+ self.network_api.disassociate(context, network_id)
+ except exception.NetworkNotFound:
+ raise exc.HTTPNotFound(_("Network not found"))
+ return exc.HTTPAccepted()
+
+ def index(self, req):
+ context = req.environ['nova.context']
+ networks = self.network_api.get_all(context)
+ result = [network_dict(net_ref) for net_ref in networks]
+ return {'networks': result}
+
+ def show(self, req, id):
+ context = req.environ['nova.context']
+ LOG.debug(_("Showing network with id %s") % id)
+ try:
+ network = self.network_api.get(context, id)
+ except exception.NetworkNotFound:
+ raise exc.HTTPNotFound(_("Network not found"))
+ return {'network': network_dict(network)}
+
+ def delete(self, req, id):
+ context = req.environ['nova.context']
+ LOG.info(_("Deleting network with id %s") % id)
+ try:
+ self.network_api.delete(context, id)
+ except exception.NetworkNotFound:
+ raise exc.HTTPNotFound(_("Network not found"))
+ return exc.HTTPAccepted()
+
+ def create(self, req, id, body=None):
+ raise exc.HTTPNotImplemented()
+
+
+class Networks(extensions.ExtensionDescriptor):
+ """Admin-only Network Management Extension"""
+
+ name = "Networks"
+ alias = "os-networks"
+ namespace = "http://docs.openstack.org/compute/ext/networks/api/v1.1"
+ updated = "2011-12-23 00:00:00"
+ admin_only = True
+
+ def get_resources(self):
+ member_actions = {'action': 'POST'}
+ res = extensions.ResourceExtension('os-networks',
+ NetworkController(),
+ member_actions=member_actions)
+ return [res]
diff --git a/nova/api/openstack/v2/extensions.py b/nova/api/openstack/v2/extensions.py
index b6ed897cb..dfda7c0aa 100644
--- a/nova/api/openstack/v2/extensions.py
+++ b/nova/api/openstack/v2/extensions.py
@@ -547,6 +547,17 @@ class ExtensionsXMLSerializer(xmlutil.XMLTemplateSerializer):
return ExtensionTemplate()
+def require_admin(f):
+ @functools.wraps(f)
+ def wraps(self, req, *args, **kwargs):
+ if 'nova.context' in req.environ and\
+ req.environ['nova.context'].is_admin:
+ return f(self, req, *args, **kwargs)
+ else:
+ raise exception.AdminRequired()
+ return wraps
+
+
def wrap_errors(fn):
"""Ensure errors are not passed along."""
def wrapped(*args):
diff --git a/nova/network/api.py b/nova/network/api.py
index 89a746359..b80d15f2f 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -16,8 +16,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Handles all requests relating to instances (guest vms)."""
-
from nova.db import base
from nova import exception
from nova import flags
@@ -33,6 +31,30 @@ LOG = logging.getLogger('nova.network')
class API(base.Base):
"""API for interacting with the network manager."""
+ def get_all(self, context):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_all_networks'})
+
+ def get(self, context, fixed_range, network_uuid):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'get_network',
+ 'args': {'network_uuid': network_uuid}})
+
+ def delete(self, context, network_uuid):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'delete_network',
+ 'args': {'fixed_range': None,
+ 'uuid': network_uuid}})
+
+ def disassociate(self, context, network_uuid):
+ return rpc.call(context,
+ FLAGS.network_topic,
+ {'method': 'disassociate_network',
+ 'args': {'network_uuid': network_uuid}})
+
def get_floating_ip(self, context, id):
return rpc.call(context,
FLAGS.network_topic,
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 2d62581e0..d4f88988d 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -1106,6 +1106,23 @@ class NetworkManager(manager.SchedulerDependentManager):
vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
return [dict(vif.iteritems()) for vif in vifs]
+ def get_network(self, context, network_uuid):
+ networks = self._get_networks_by_uuids(context, [network_uuid])
+ try:
+ network = networks[0]
+ except (IndexError, TypeError):
+ raise exception.NetworkNotFound(network_id=network_uuid)
+
+ return dict(network.iteritems())
+
+ def get_all_networks(self, context):
+ networks = self.db.network_get_all(context)
+ return [dict(network.iteritems()) for network in networks]
+
+ def disassociate_network(self, context, network_uuid):
+ network = self.get_network(context, network_uuid)
+ self.db.network_disassociate(context, network['id'])
+
class FlatManager(NetworkManager):
"""Basic network where no vlans are used.
diff --git a/nova/tests/api/openstack/v2/contrib/test_networks.py b/nova/tests/api/openstack/v2/contrib/test_networks.py
new file mode 100644
index 000000000..04bd82e2c
--- /dev/null
+++ b/nova/tests/api/openstack/v2/contrib/test_networks.py
@@ -0,0 +1,137 @@
+# Copyright 2011 Grid Dynamics
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed 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 copy
+
+import webob
+
+from nova.api.openstack.v2.contrib import networks
+from nova import context
+from nova import exception
+from nova import test
+from nova.tests.api.openstack import fakes
+
+
+FAKE_NETWORKS = [
+ {
+ 'bridge': 'br100', 'vpn_public_port': 1000,
+ 'dhcp_start': '10.0.0.3', 'bridge_interface': 'eth0',
+ 'updated_at': '2011-08-16 09:26:13.048257', 'id': 1,
+ 'cidr_v6': None, 'deleted_at': None,
+ 'gateway': '10.0.0.1', 'label': 'mynet_0',
+ 'project_id': '1234',
+ 'vpn_private_address': '10.0.0.2', 'deleted': False,
+ 'vlan': 100, 'broadcast': '10.0.0.7',
+ 'netmask': '255.255.255.248', 'injected': False,
+ 'cidr': '10.0.0.0/29',
+ 'vpn_public_address': '127.0.0.1', 'multi_host': False,
+ 'dns1': None, 'host': 'nsokolov-desktop',
+ 'gateway_v6': None, 'netmask_v6': None,
+ 'created_at': '2011-08-15 06:19:19.387525',
+ },
+ {
+ 'bridge': 'br101', 'vpn_public_port': 1001,
+ 'dhcp_start': '10.0.0.11', 'bridge_interface': 'eth0',
+ 'updated_at': None, 'id': 2, 'cidr_v6': None,
+ 'deleted_at': None, 'gateway': '10.0.0.9',
+ 'label': 'mynet_1', 'project_id': None,
+ 'vpn_private_address': '10.0.0.10', 'deleted': False,
+ 'vlan': 101, 'broadcast': '10.0.0.15',
+ 'netmask': '255.255.255.248', 'injected': False,
+ 'cidr': '10.0.0.10/29', 'vpn_public_address': None,
+ 'multi_host': False, 'dns1': None, 'host': None,
+ 'gateway_v6': None, 'netmask_v6': None,
+ 'created_at': '2011-08-15 06:19:19.885495',
+ },
+]
+
+
+class FakeNetworkAPI(object):
+
+ def __init__(self):
+ self.networks = copy.deepcopy(FAKE_NETWORKS)
+
+ def delete(self, context, network_id):
+ for i, network in enumerate(self.networks):
+ if network['id'] == network_id:
+ del self.networks[0]
+ return True
+ raise exception.NetworkNotFound()
+
+ #NOTE(bcwaldon): this does nothing other than check for existance
+ def disassociate(self, context, network_id):
+ for i, network in enumerate(self.networks):
+ if network['id'] == network_id:
+ return True
+ raise exception.NetworkNotFound()
+
+ def get_all(self, context):
+ return self.networks
+
+ def get(self, context, network_id):
+ for network in self.networks:
+ if network['id'] == network_id:
+ return network
+ raise exception.NetworkNotFound()
+
+
+class NetworksTest(test.TestCase):
+
+ def setUp(self):
+ super(NetworksTest, self).setUp()
+ self.flags(allow_admin_api=True)
+ self.fake_network_api = FakeNetworkAPI()
+ self.controller = networks.NetworkController(self.fake_network_api)
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ self.context = context.RequestContext('user', '1234', is_admin=True)
+
+ def test_network_list_all(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks')
+ res_dict = self.controller.index(req)
+ self.assertEquals(res_dict, {'networks': FAKE_NETWORKS})
+
+ def test_network_disassociate(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks/1/action')
+ res = self.controller.action(req, 1, {'disassociate': None})
+ self.assertEqual(res.status_int, 202)
+
+ def test_network_disassociate_not_found(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks/100/action')
+ self.assertRaises(webob.exc.HTTPNotFound,
+ self.controller.action,
+ req, 100, {'disassociate': None})
+
+ def test_network_get(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks/1')
+ res_dict = self.controller.show(req, 1)
+ expected = {'network': FAKE_NETWORKS[0]}
+ self.assertEqual(res_dict, expected)
+
+ def test_network_get_not_found(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks/100')
+ self.assertRaises(webob.exc.HTTPNotFound,
+ self.controller.show, req, 100)
+
+ def test_network_delete(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks/1')
+ res = self.controller.delete(req, 1)
+ self.assertEqual(res.status_int, 202)
+
+ def test_network_delete_not_found(self):
+ req = fakes.HTTPRequest.blank('/v2/1234/os-networks/100')
+ self.assertRaises(webob.exc.HTTPNotFound,
+ self.controller.delete, req, 100)
diff --git a/nova/tests/api/openstack/v2/test_extensions.py b/nova/tests/api/openstack/v2/test_extensions.py
index 2063e6e2d..a63902bbb 100644
--- a/nova/tests/api/openstack/v2/test_extensions.py
+++ b/nova/tests/api/openstack/v2/test_extensions.py
@@ -122,6 +122,7 @@ class ExtensionControllerTest(ExtensionTestCase):
"Volumes",
"VolumeTypes",
"Zones",
+ "Networks",
]
self.ext_list.sort()
diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py
index e1af85239..13e5d4f68 100644
--- a/nova/tests/fake_network.py
+++ b/nova/tests/fake_network.py
@@ -89,6 +89,12 @@ class FakeNetworkManager(network_manager.NetworkManager):
def network_get_all(self, context):
raise exception.NoNetworksFound()
+ def network_get_all_by_uuids(self, context):
+ raise exception.NoNetworksFound()
+
+ def network_disassociate(self, context, network_id):
+ return True
+
def virtual_interface_get_all(self, context):
floats = [{'address': '172.16.1.1'},
{'address': '172.16.1.2'},
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index dc26b0298..a37abc0e7 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -1079,6 +1079,67 @@ class CommonNetworkTestCase(test.TestCase):
self.assertEqual(len(res), 1)
self.assertEqual(res[0]['instance_id'], _vifs[2]['instance_id'])
+ def test_get_network(self):
+ manager = fake_network.FakeNetworkManager()
+ fake_context = context.RequestContext('user', 'project')
+ self.mox.StubOutWithMock(manager.db, 'network_get_all_by_uuids')
+ manager.db.network_get_all_by_uuids(mox.IgnoreArg(),
+ mox.IgnoreArg()).\
+ AndReturn(networks)
+ self.mox.ReplayAll()
+ uuid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
+ network = manager.get_network(fake_context, uuid)
+ self.assertEqual(network['uuid'], uuid)
+
+ def test_get_network_not_found(self):
+ manager = fake_network.FakeNetworkManager()
+ fake_context = context.RequestContext('user', 'project')
+ self.mox.StubOutWithMock(manager.db, 'network_get_all_by_uuids')
+ manager.db.network_get_all_by_uuids(mox.IgnoreArg(),
+ mox.IgnoreArg()).\
+ AndReturn([])
+ self.mox.ReplayAll()
+ uuid = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
+ self.assertRaises(exception.NetworkNotFound,
+ manager.get_network, fake_context, uuid)
+
+ def test_get_all_networks(self):
+ manager = fake_network.FakeNetworkManager()
+ fake_context = context.RequestContext('user', 'project')
+ self.mox.StubOutWithMock(manager.db, 'network_get_all')
+ manager.db.network_get_all(mox.IgnoreArg()).\
+ AndReturn(networks)
+ self.mox.ReplayAll()
+ output = manager.get_all_networks(fake_context)
+ self.assertEqual(len(networks), 2)
+ self.assertEqual(output[0]['uuid'],
+ 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
+ self.assertEqual(output[1]['uuid'],
+ 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb')
+
+ def test_disassociate_network(self):
+ manager = fake_network.FakeNetworkManager()
+ fake_context = context.RequestContext('user', 'project')
+ self.mox.StubOutWithMock(manager.db, 'network_get_all_by_uuids')
+ manager.db.network_get_all_by_uuids(mox.IgnoreArg(),
+ mox.IgnoreArg()).\
+ AndReturn(networks)
+ self.mox.ReplayAll()
+ uuid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
+ manager.disassociate_network(fake_context, uuid)
+
+ def test_disassociate_network_not_found(self):
+ manager = fake_network.FakeNetworkManager()
+ fake_context = context.RequestContext('user', 'project')
+ self.mox.StubOutWithMock(manager.db, 'network_get_all_by_uuids')
+ manager.db.network_get_all_by_uuids(mox.IgnoreArg(),
+ mox.IgnoreArg()).\
+ AndReturn([])
+ self.mox.ReplayAll()
+ uuid = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
+ self.assertRaises(exception.NetworkNotFound,
+ manager.disassociate_network, fake_context, uuid)
+
class TestRPCFixedManager(network_manager.RPCAllocateFixedIP,
network_manager.NetworkManager):