diff options
| author | Matt Dietz <matt.dietz@rackspace.com> | 2012-12-17 18:25:27 +0000 |
|---|---|---|
| committer | Matt Dietz <matt.dietz@rackspace.com> | 2013-01-08 17:02:20 +0000 |
| commit | 129fd64ef8f29f5e511eb7c72b1bd9856900905a (patch) | |
| tree | 709588bae73497615351452ddc9f6f7572b87282 /nova/api | |
| parent | 8b83990c12e4dc03aa599dd5e68ce91a9cab4eb7 (diff) | |
| download | nova-129fd64ef8f29f5e511eb7c72b1bd9856900905a.tar.gz nova-129fd64ef8f29f5e511eb7c72b1bd9856900905a.tar.xz nova-129fd64ef8f29f5e511eb7c72b1bd9856900905a.zip | |
Adds a new tenant-centric network extension
Implements: blueprint tenant-networks
Adds a new API extension that supports a more tenant-centric model for
interacting with isolated networks. Additionally, the patch renames the
existing networks extension as os-admin-networks to more fully fit the
self-described implementation. Two new novaclient extensions and a
novaclient change accompany this patch.
DocImpact
Change-Id: Ib90a65c0a92445f86e930fab88ab4495aefff7c7
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/openstack/compute/contrib/admin_networks.py (renamed from nova/api/openstack/compute/contrib/networks.py) | 22 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/networks_associate.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/os_networks.py | 213 |
3 files changed, 226 insertions, 11 deletions
diff --git a/nova/api/openstack/compute/contrib/networks.py b/nova/api/openstack/compute/contrib/admin_networks.py index e21c06813..cdcee02d0 100644 --- a/nova/api/openstack/compute/contrib/networks.py +++ b/nova/api/openstack/compute/contrib/admin_networks.py @@ -27,8 +27,9 @@ from nova import network from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'networks') -authorize_view = extensions.extension_authorizer('compute', 'networks:view') +authorize = extensions.extension_authorizer('compute', 'admin_networks') +authorize_view = extensions.extension_authorizer('compute', + 'admin_networks:view') def network_dict(context, network): @@ -53,7 +54,7 @@ def network_dict(context, network): return {} -class NetworkController(wsgi.Controller): +class AdminNetworkController(wsgi.Controller): def __init__(self, network_api=None): self.network_api = network_api or network.API() @@ -149,20 +150,21 @@ class NetworkController(wsgi.Controller): return webob.Response(status_int=202) -class Networks(extensions.ExtensionDescriptor): - """Admin-only Network Management Extension.""" +class Admin_networks(extensions.ExtensionDescriptor): + """Admin-only Network Management Extension""" - name = "Networks" - alias = "os-networks" - namespace = "http://docs.openstack.org/compute/ext/networks/api/v1.1" + name = "AdminNetworks" + alias = "os-admin-networks" + namespace = ("http://docs.openstack.org/compute/" + "ext/os-admin-networks/api/v1.1") updated = "2011-12-23T00:00:00+00:00" def get_resources(self): member_actions = {'action': 'POST'} collection_actions = {'add': 'POST'} res = extensions.ResourceExtension( - 'os-networks', - NetworkController(), + 'os-admin-networks', + AdminNetworkController(), member_actions=member_actions, collection_actions=collection_actions) return [res] diff --git a/nova/api/openstack/compute/contrib/networks_associate.py b/nova/api/openstack/compute/contrib/networks_associate.py index 3cdda1d76..4990c1b5e 100644 --- a/nova/api/openstack/compute/contrib/networks_associate.py +++ b/nova/api/openstack/compute/contrib/networks_associate.py @@ -62,6 +62,6 @@ class Networks_associate(extensions.ExtensionDescriptor): def get_controller_extensions(self): extension = extensions.ControllerExtension( - self, 'os-networks', NetworkAssociateActionController()) + self, 'os-admin-networks', NetworkAssociateActionController()) return [extension] diff --git a/nova/api/openstack/compute/contrib/os_networks.py b/nova/api/openstack/compute/contrib/os_networks.py new file mode 100644 index 000000000..61a9d3af6 --- /dev/null +++ b/nova/api/openstack/compute/contrib/os_networks.py @@ -0,0 +1,213 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 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 netaddr +import netaddr.core as netexc +from webob import exc + +from nova.api.openstack import extensions +from nova import context as nova_context +from nova import exception +import nova.network +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova import quota + + +CONF = cfg.CONF + +try: + os_network_opts = [ + cfg.BoolOpt("enable_network_quota", + default=False, + help="Enables or disables quotaing of tenant networks"), + cfg.StrOpt('use_quantum_default_nets', + default="False", + help=('Control for checking for default networks')), + cfg.StrOpt('quantum_default_tenant_id', + default="default", + help=('Default tenant id when creating quantum ' + 'networks')) + ] + CONF.register_opts(os_network_opts) +except cfg.DuplicateOptError: + # NOTE(jkoelker) These options are verbatim elsewhere this is here + # to make sure they are registered for our use. + pass + +if CONF.enable_network_quota: + opts = [ + cfg.IntOpt('quota_networks', + default=3, + help='number of private networks allowed per project'), + ] + CONF.register_opts(opts) + +QUOTAS = quota.QUOTAS +LOG = logging.getLogger(__name__) +authorize = extensions.extension_authorizer('compute', 'os-networks') + + +def network_dict(network): + return {"id": network.get("uuid") or network["id"], + "cidr": network["cidr"], + "label": network["label"]} + + +class NetworkController(object): + def __init__(self, network_api=None): + self.network_api = nova.network.API() + self._default_networks = [] + + def _refresh_default_networks(self): + self._default_networks = [] + if CONF.use_quantum_default_nets == "True": + try: + self._default_networks = self._get_default_networks() + except Exception: + LOG.exception("Failed to get default networks") + + def _get_default_networks(self): + project_id = CONF.quantum_default_tenant_id + ctx = nova_context.RequestContext(user_id=None, + project_id=project_id) + networks = {} + for n in self.network_api.get_all(ctx): + networks[n['id']] = n['label'] + return [{'id': k, 'label': v} for k, v in networks.iteritems()] + + def index(self, req): + context = req.environ['nova.context'] + authorize(context) + networks = self.network_api.get_all(context) + if not self._default_networks: + self._refresh_default_networks() + networks.extend(self._default_networks) + return {'networks': [network_dict(n) for n in networks]} + + def show(self, req, id): + context = req.environ['nova.context'] + authorize(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_dict(network) + + def delete(self, req, id): + context = req.environ['nova.context'] + authorize(context) + try: + if CONF.enable_network_quota: + reservation = QUOTAS.reserve(context, networks=-1) + except Exception: + reservation = None + LOG.exception(_("Failed to update usages deallocating " + "network.")) + + LOG.info(_("Deleting network with id %s") % id) + + try: + self.network_api.delete(context, id) + if CONF.enable_network_quota and reservation: + QUOTAS.commit(context, reservation) + response = exc.HTTPAccepted() + except exception.NetworkNotFound: + response = exc.HTTPNotFound(_("Network not found")) + + return response + + def create(self, req, body): + if not body: + raise exc.HTTPUnprocessableEntity() + + context = req.environ["nova.context"] + authorize(context) + + network = body["network"] + keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size", + "num_networks"] + kwargs = dict((k, network.get(k)) for k in keys) + + label = network["label"] + + if not (kwargs["cidr"] or kwargs["cidr_v6"]): + msg = _("No CIDR requested") + raise exc.HTTPBadRequest(explanation=msg) + if kwargs["cidr"]: + try: + net = netaddr.IPNetwork(kwargs["cidr"]) + if net.size < 4: + msg = _("Requested network does not contain " + "enough (2+) usable hosts") + raise exc.HTTPBadRequest(explanation=msg) + except netexc.AddrFormatError: + msg = _("CIDR is malformed.") + raise exc.HTTPBadRequest(explanation=msg) + except netexc.AddrConversionError: + msg = _("Address could not be converted.") + raise exc.HTTPBadRequest(explanation=msg) + + networks = [] + try: + if CONF.enable_network_quota: + reservation = QUOTAS.reserve(context, networks=1) + except exception.OverQuota: + msg = _("Quota exceeded, too many networks.") + raise exc.HTTPBadRequest(explanation=msg) + + try: + networks = self.network_api.create(context, + label=label, **kwargs) + if CONF.enable_network_quota: + QUOTAS.commit(context, reservation) + except Exception: + if CONF.enable_network_quota: + QUOTAS.rollback(context, reservation) + msg = _("Create networks failed") + LOG.exception(msg, extra=network) + raise exc.HTTPServiceUnavailable(explanation=msg) + return {"network": network_dict(networks[0])} + + +class Os_networks(extensions.ExtensionDescriptor): + """Tenant-based Network Management Extension""" + + name = "OSNetworks" + alias = "os-networks" + namespace = "http://docs.openstack.org/compute/ext/os-networks/api/v1.1" + updated = "2012-03-07T09:46:43-05:00" + + def get_resources(self): + ext = extensions.ResourceExtension('os-networks', + NetworkController()) + return [ext] + + +def _sync_networks(context, project_id, session): + ctx = nova_context.RequestContext(user_id=None, project_id=project_id) + ctx = ctx.elevated() + networks = nova.network.api.API().get_all(ctx) + return dict(networks=len(networks)) + + +if CONF.enable_network_quota: + QUOTAS.register_resource(quota.ReservableResource('networks', + _sync_networks, + 'quota_networks')) |
