From 382435275e354362c3889cb18dfc70e7c8b10bd1 Mon Sep 17 00:00:00 2001 From: Matt Dietz Date: Fri, 11 Jan 2013 17:52:27 +0000 Subject: [PATCH] Renames the new os-networks extension Merging the new os-networks extension upstream broke what was considered a 'released' extension. This patch returns everything to normal and renames the os-networks extension to os-tenant-networks DocImpact Implements: blueprint tenant-networks Change-Id: I04b17d08c5760fd73736124b36cab0d383767020 --- .../all_extensions/extensions-get-resp.json | 12 +- .../all_extensions/extensions-get-resp.xml | 4 +- .../networks-list-res.json | 0 .../networks-post-res.json | 0 .../compute/contrib/admin_networks.py | 170 ------------ .../compute/contrib/networks_associate.py | 2 +- .../openstack/compute/contrib/os_networks.py | 255 ++++++++---------- .../compute/contrib/os_tenant_networks.py | 214 +++++++++++++++ .../compute/contrib/test_networks.py | 4 +- .../api/openstack/compute/test_extensions.py | 1 - nova/tests/fake_policy.py | 6 +- .../extensions-get-resp.json.tpl | 18 +- .../extensions-get-resp.xml.tpl | 4 +- .../networks-list-res.json.tpl | 0 .../networks-post-req.json.tpl | 0 .../networks-post-res.json.tpl | 0 nova/tests/integrated/test_api_samples.py | 27 +- 17 files changed, 359 insertions(+), 358 deletions(-) rename doc/api_samples/{os-networks => os-tenant-networks}/networks-list-res.json (100%) rename doc/api_samples/{os-networks => os-tenant-networks}/networks-post-res.json (100%) delete mode 100644 nova/api/openstack/compute/contrib/admin_networks.py create mode 100644 nova/api/openstack/compute/contrib/os_tenant_networks.py rename nova/tests/integrated/api_samples/{os-networks => os-tenant-networks}/networks-list-res.json.tpl (100%) rename nova/tests/integrated/api_samples/{os-networks => os-tenant-networks}/networks-post-req.json.tpl (100%) rename nova/tests/integrated/api_samples/{os-networks => os-tenant-networks}/networks-post-res.json.tpl (100%) diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json index 42e86eadd384..25d077f276a2 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.json +++ b/doc/api_samples/all_extensions/extensions-get-resp.json @@ -297,19 +297,19 @@ "updated": "2012-08-07T00:00:00+00:00" }, { - "alias": "os-admin-networks", + "alias": "os-networks", "description": "Admin-only Network Management Extension.", "links": [], - "name": "AdminNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1", + "name": "Networks", + "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", "updated": "2011-12-23T00:00:00+00:00" }, { - "alias": "os-networks", + "alias": "os-tenant-networks", "description": "Tenant-based Network Management Extension.", "links": [], - "name": "OSNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", + "name": "OSTenantNetworks", + "namespace": "http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2", "updated": "2011-12-23T00:00:00+00:00" }, { diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml index ea0b45a1285c..b66c3dbe78a6 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.xml +++ b/doc/api_samples/all_extensions/extensions-get-resp.xml @@ -125,13 +125,13 @@ Allow multiple create in the Create Server v1.1 API. - + Admin-only Network Management Extension. Network association support. - + Tenant-based Network Management Extension. diff --git a/doc/api_samples/os-networks/networks-list-res.json b/doc/api_samples/os-tenant-networks/networks-list-res.json similarity index 100% rename from doc/api_samples/os-networks/networks-list-res.json rename to doc/api_samples/os-tenant-networks/networks-list-res.json diff --git a/doc/api_samples/os-networks/networks-post-res.json b/doc/api_samples/os-tenant-networks/networks-post-res.json similarity index 100% rename from doc/api_samples/os-networks/networks-post-res.json rename to doc/api_samples/os-tenant-networks/networks-post-res.json diff --git a/nova/api/openstack/compute/contrib/admin_networks.py b/nova/api/openstack/compute/contrib/admin_networks.py deleted file mode 100644 index f5facd601cbb..000000000000 --- a/nova/api/openstack/compute/contrib/admin_networks.py +++ /dev/null @@ -1,170 +0,0 @@ -# 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. - -import netaddr -import webob -from webob import exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import exception -from nova import network -from nova.openstack.common import log as logging - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'admin_networks') -authorize_view = extensions.extension_authorizer('compute', - 'admin_networks:view') - - -def network_dict(context, network): - fields = ('id', 'cidr', 'netmask', 'gateway', 'broadcast', 'dns1', 'dns2', - 'cidr_v6', 'gateway_v6', 'label', 'netmask_v6') - admin_fields = ('created_at', 'updated_at', 'deleted_at', 'deleted', - 'injected', 'bridge', 'vlan', 'vpn_public_address', - 'vpn_public_port', 'vpn_private_address', 'dhcp_start', - 'project_id', 'host', 'bridge_interface', 'multi_host', - 'priority', 'rxtx_base') - if network: - # NOTE(mnaser): We display a limited set of fields so users can know - # what networks are available, extra system-only fields - # are only visible if they are an admin. - if context.is_admin: - fields += admin_fields - result = dict((field, network[field]) for field in fields) - if 'uuid' in network: - result['id'] = network['uuid'] - return result - else: - return {} - - -class AdminNetworkController(wsgi.Controller): - - def __init__(self, network_api=None): - self.network_api = network_api or network.API() - - def index(self, req): - context = req.environ['nova.context'] - authorize_view(context) - networks = self.network_api.get_all(context) - result = [network_dict(context, net_ref) for net_ref in networks] - return {'networks': result} - - @wsgi.action("disassociate") - def _disassociate_host_and_project(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - LOG.debug(_("Disassociating network with id %s"), id) - - try: - self.network_api.associate(context, id, host=None, project=None) - except exception.NetworkNotFound: - raise exc.HTTPNotFound(_("Network not found")) - return exc.HTTPAccepted() - - def show(self, req, id): - context = req.environ['nova.context'] - authorize_view(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(context, network)} - - def delete(self, req, id): - context = req.environ['nova.context'] - authorize(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, body): - context = req.environ['nova.context'] - authorize(context) - - def bad(e): - return exc.HTTPUnprocessableEntity(explanation=e) - - if not (body and body.get("network")): - raise bad(_("Missing network in body")) - - params = body["network"] - if not params.get("label"): - raise bad(_("Network label is required")) - - cidr = params.get("cidr") or params.get("cidr_v6") - if not cidr: - raise bad(_("Network cidr or cidr_v6 is required")) - - LOG.debug(_("Creating network with label %s") % params["label"]) - - params["num_networks"] = 1 - params["network_size"] = netaddr.IPNetwork(cidr).size - - network = self.network_api.create(context, **params)[0] - return {"network": network_dict(context, network)} - - def add(self, req, body): - context = req.environ['nova.context'] - authorize(context) - if not body: - raise exc.HTTPUnprocessableEntity() - - network_id = body.get('id', None) - project_id = context.project_id - LOG.debug(_("Associating network %(network)s" - " with project %(project)s") % - {"network": network_id or "", - "project": project_id}) - try: - self.network_api.add_network_to_project( - context, project_id, network_id) - except Exception as ex: - msg = (_("Cannot associate network %(network)s" - " with project %(project)s: %(message)s") % - {"network": network_id or "", - "project": project_id, - "message": getattr(ex, "value", str(ex))}) - raise exc.HTTPBadRequest(explanation=msg) - - return webob.Response(status_int=202) - - -class Admin_networks(extensions.ExtensionDescriptor): - """Admin-only Network Management Extension.""" - - 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-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 4990c1b5e679..3cdda1d761a8 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-admin-networks', NetworkAssociateActionController()) + self, 'os-networks', NetworkAssociateActionController()) return [extension] diff --git a/nova/api/openstack/compute/contrib/os_networks.py b/nova/api/openstack/compute/contrib/os_networks.py index 4be0bd100523..d1d172686a78 100644 --- a/nova/api/openstack/compute/contrib/os_networks.py +++ b/nova/api/openstack/compute/contrib/os_networks.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2013 OpenStack LLC. +# Copyright 2011 Grid Dynamics +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -15,199 +16,155 @@ # License for the specific language governing permissions and limitations # under the License. - import netaddr -import netaddr.core as netexc +import webob from webob import exc from nova.api.openstack import extensions -from nova import context as nova_context +from nova.api.openstack import wsgi from nova import exception -import nova.network -from nova.openstack.common import cfg +from nova import network 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') +authorize = extensions.extension_authorizer('compute', 'networks') +authorize_view = extensions.extension_authorizer('compute', + 'networks:view') -def network_dict(network): - return {"id": network.get("uuid") or network["id"], - "cidr": network["cidr"], - "label": network["label"]} +def network_dict(context, network): + fields = ('id', 'cidr', 'netmask', 'gateway', 'broadcast', 'dns1', 'dns2', + 'cidr_v6', 'gateway_v6', 'label', 'netmask_v6') + admin_fields = ('created_at', 'updated_at', 'deleted_at', 'deleted', + 'injected', 'bridge', 'vlan', 'vpn_public_address', + 'vpn_public_port', 'vpn_private_address', 'dhcp_start', + 'project_id', 'host', 'bridge_interface', 'multi_host', + 'priority', 'rxtx_base') + if network: + # NOTE(mnaser): We display a limited set of fields so users can know + # what networks are available, extra system-only fields + # are only visible if they are an admin. + if context.is_admin: + fields += admin_fields + result = dict((field, network[field]) for field in fields) + if 'uuid' in network: + result['id'] = network['uuid'] + return result + else: + return {} -class NetworkController(object): +class NetworkController(wsgi.Controller): + 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()] + self.network_api = network_api or network.API() def index(self, req): context = req.environ['nova.context'] - authorize(context) + authorize_view(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]} + result = [network_dict(context, net_ref) for net_ref in networks] + return {'networks': result} + + @wsgi.action("disassociate") + def _disassociate_host_and_project(self, req, id, body): + context = req.environ['nova.context'] + authorize(context) + LOG.debug(_("Disassociating network with id %s"), id) + + try: + self.network_api.associate(context, id, host=None, project=None) + except exception.NetworkNotFound: + raise exc.HTTPNotFound(_("Network not found")) + return exc.HTTPAccepted() def show(self, req, id): context = req.environ['nova.context'] - authorize(context) + authorize_view(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) + return {'network': network_dict(context, 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 + raise exc.HTTPNotFound(_("Network not found")) + return exc.HTTPAccepted() def create(self, req, body): + context = req.environ['nova.context'] + authorize(context) + + def bad(e): + return exc.HTTPUnprocessableEntity(explanation=e) + + if not (body and body.get("network")): + raise bad(_("Missing network in body")) + + params = body["network"] + if not params.get("label"): + raise bad(_("Network label is required")) + + cidr = params.get("cidr") or params.get("cidr_v6") + if not cidr: + raise bad(_("Network cidr or cidr_v6 is required")) + + LOG.debug(_("Creating network with label %s") % params["label"]) + + params["num_networks"] = 1 + params["network_size"] = netaddr.IPNetwork(cidr).size + + network = self.network_api.create(context, **params)[0] + return {"network": network_dict(context, network)} + + def add(self, req, body): + context = req.environ['nova.context'] + authorize(context) 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 = [] + network_id = body.get('id', None) + project_id = context.project_id + LOG.debug(_("Associating network %(network)s" + " with project %(project)s") % + {"network": network_id or "", + "project": project_id}) try: - if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=1) - except exception.OverQuota: - msg = _("Quota exceeded, too many networks.") + self.network_api.add_network_to_project( + context, project_id, network_id) + except Exception as ex: + msg = (_("Cannot associate network %(network)s" + " with project %(project)s: %(message)s") % + {"network": network_id or "", + "project": project_id, + "message": getattr(ex, "value", str(ex))}) 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])} + return webob.Response(status_int=202) class Os_networks(extensions.ExtensionDescriptor): - """Tenant-based Network Management Extension.""" + """Admin-only Network Management Extension.""" - name = "OSNetworks" + name = "Networks" alias = "os-networks" - namespace = "http://docs.openstack.org/compute/ext/os-networks/api/v1.1" - updated = "2012-03-07T09:46:43-05:00" + namespace = ("http://docs.openstack.org/compute/" + "ext/os-networks/api/v1.1") + updated = "2011-12-23T00:00:00+00: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')) + member_actions = {'action': 'POST'} + collection_actions = {'add': 'POST'} + res = extensions.ResourceExtension( + 'os-networks', + NetworkController(), + member_actions=member_actions, + collection_actions=collection_actions) + return [res] diff --git a/nova/api/openstack/compute/contrib/os_tenant_networks.py b/nova/api/openstack/compute/contrib/os_tenant_networks.py new file mode 100644 index 000000000000..03178ab650ef --- /dev/null +++ b/nova/api/openstack/compute/contrib/os_tenant_networks.py @@ -0,0 +1,214 @@ +# 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-tenant-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_tenant_networks(extensions.ExtensionDescriptor): + """Tenant-based Network Management Extension.""" + + name = "OSTenantNetworks" + alias = "os-tenant-networks" + namespace = ("http://docs.openstack.org/compute/" + "ext/os-tenant-networks/api/v2") + updated = "2012-03-07T09:46:43-05:00" + + def get_resources(self): + ext = extensions.ResourceExtension('os-tenant-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')) diff --git a/nova/tests/api/openstack/compute/contrib/test_networks.py b/nova/tests/api/openstack/compute/contrib/test_networks.py index ba65e8f6a932..44d9e8af3168 100644 --- a/nova/tests/api/openstack/compute/contrib/test_networks.py +++ b/nova/tests/api/openstack/compute/contrib/test_networks.py @@ -21,8 +21,8 @@ import uuid import webob -from nova.api.openstack.compute.contrib import admin_networks as networks from nova.api.openstack.compute.contrib import networks_associate +from nova.api.openstack.compute.contrib import os_networks as networks from nova import exception from nova.openstack.common import cfg from nova import test @@ -177,7 +177,7 @@ class NetworksTest(test.TestCase): def setUp(self): super(NetworksTest, self).setUp() self.fake_network_api = FakeNetworkAPI() - self.controller = networks.AdminNetworkController( + self.controller = networks.NetworkController( self.fake_network_api) self.associate_controller = networks_associate\ .NetworkAssociateActionController(self.fake_network_api) diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index e3810510bb99..485968209289 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -185,7 +185,6 @@ class ExtensionControllerTest(ExtensionTestCase): "Keypairs", "Multinic", "MultipleCreate", - "OSNetworks", "QuotaClasses", "Quotas", "Rescue", diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index c5d160209293..51f3a3f85cc1 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -136,10 +136,10 @@ policy_data = """ "compute_extension:instance_usage_audit_log": "", "compute_extension:keypairs": "", "compute_extension:multinic": "", - "compute_extension:admin_networks": "", - "compute_extension:admin_networks:view": "", + "compute_extension:networks": "", + "compute_extension:networks:view": "", "compute_extension:networks_associate": "", - "compute_extension:os-networks": "", + "compute_extension:os-tenant-networks": "", "compute_extension:quotas:show": "", "compute_extension:quotas:update": "", "compute_extension:quota_classes": "", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl index 0dd777fe2c48..3d69fad45e9b 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl @@ -304,22 +304,22 @@ "namespace": "http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1", "updated": "%(timestamp)s" }, - { - "alias": "os-admin-networks", - "description": "%(text)s", - "links": [], - "name": "AdminNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1", - "updated": "%(timestamp)s" - }, { "alias": "os-networks", "description": "%(text)s", "links": [], - "name": "OSNetworks", + "name": "Networks", "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", "updated": "%(timestamp)s" }, + { + "alias": "os-tenant-networks", + "description": "%(text)s", + "links": [], + "name": "OSTenantNetworks", + "namespace": "http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2", + "updated": "%(timestamp)s" + }, { "alias": "os-networks-associate", "description": "%(text)s", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl index fe34f369b090..5953ba7040b5 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl @@ -114,10 +114,10 @@ %(text)s - + %(text)s - + %(text)s diff --git a/nova/tests/integrated/api_samples/os-networks/networks-list-res.json.tpl b/nova/tests/integrated/api_samples/os-tenant-networks/networks-list-res.json.tpl similarity index 100% rename from nova/tests/integrated/api_samples/os-networks/networks-list-res.json.tpl rename to nova/tests/integrated/api_samples/os-tenant-networks/networks-list-res.json.tpl diff --git a/nova/tests/integrated/api_samples/os-networks/networks-post-req.json.tpl b/nova/tests/integrated/api_samples/os-tenant-networks/networks-post-req.json.tpl similarity index 100% rename from nova/tests/integrated/api_samples/os-networks/networks-post-req.json.tpl rename to nova/tests/integrated/api_samples/os-tenant-networks/networks-post-req.json.tpl diff --git a/nova/tests/integrated/api_samples/os-networks/networks-post-res.json.tpl b/nova/tests/integrated/api_samples/os-tenant-networks/networks-post-res.json.tpl similarity index 100% rename from nova/tests/integrated/api_samples/os-networks/networks-post-res.json.tpl rename to nova/tests/integrated/api_samples/os-tenant-networks/networks-post-res.json.tpl diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index 0fb0e91078aa..98ac6a230940 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -371,7 +371,7 @@ class ApiSamplesTrap(ApiSampleTestBase): do_not_approve_additions.append('os-fping') do_not_approve_additions.append('os-hypervisors') do_not_approve_additions.append('os-instance_usage_audit_log') - do_not_approve_additions.append('os-admin-networks') + do_not_approve_additions.append('os-networks') do_not_approve_additions.append('os-services') do_not_approve_additions.append('os-volumes') @@ -2359,8 +2359,8 @@ class DiskConfigXmlTest(DiskConfigJsonTest): class OsNetworksJsonTests(ApiSampleTestBase): - extension_name = ("nova.api.openstack.compute.contrib.os_networks" - ".Os_networks") + extension_name = ("nova.api.openstack.compute.contrib.os_tenant_networks" + ".Os_tenant_networks") def setUp(self): super(OsNetworksJsonTests, self).setUp() @@ -2377,21 +2377,22 @@ class OsNetworksJsonTests(ApiSampleTestBase): self.stubs.Set(nova.quota.QuotaEngine, "rollback", fake) def test_list_networks(self): - response = self._do_get('os-networks') + response = self._do_get('os-tenant-networks') self.assertEqual(response.status, 200) subs = self._get_regexes() return self._verify_response('networks-list-res', subs, response) def test_create_network(self): - response = self._do_post('os-networks', "networks-post-req", {}) + response = self._do_post('os-tenant-networks', "networks-post-req", {}) self.assertEqual(response.status, 200) subs = self._get_regexes() self._verify_response('networks-post-res', subs, response) - def test_delete_networK(self): - response = self._do_post('os-networks', "networks-post-req", {}) + def test_delete_network(self): + response = self._do_post('os-tenant-networks', "networks-post-req", {}) net = json.loads(response.read()) - response = self._do_delete('os-networks/%s' % net["network"]["id"]) + response = self._do_delete('os-tenant-networks/%s' % + net["network"]["id"]) self.assertEqual(response.status, 202) @@ -2406,7 +2407,7 @@ class NetworksAssociateJsonTests(ApiSampleTestBase): f['osapi_compute_extension'] = CONF.osapi_compute_extension[:] # Networks_associate requires Networks to be update f['osapi_compute_extension'].append( - 'nova.api.openstack.compute.contrib.admin_networks.Admin_networks') + 'nova.api.openstack.compute.contrib.os_networks.Os_networks') return f def setUp(self): @@ -2420,25 +2421,25 @@ class NetworksAssociateJsonTests(ApiSampleTestBase): self.stubs.Set(network_api.API, "associate", fake_associate) def test_disassociate(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-disassociate-req', {}) self.assertEqual(response.status, 202) def test_disassociate_host(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-disassociate-host-req', {}) self.assertEqual(response.status, 202) def test_disassociate_project(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-disassociate-project-req', {}) self.assertEqual(response.status, 202) def test_associate_host(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-associate-host-req', {"host": "testHost"}) self.assertEqual(response.status, 202)