diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-10-16 05:55:44 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-10-16 05:55:44 +0000 |
commit | c33374df8c7d5b0e339287324ce332ccb8c30896 (patch) | |
tree | 552dcc7bdb32dcb6f2c6138ae0c10573bb545e1b /nova/api/openstack/compute/plugins | |
parent | 2b238e822ce33697e1dd13e9035bf668c51588fc (diff) | |
parent | cc452485c2c06d791295520dd70abce90c042d8c (diff) | |
download | nova-c33374df8c7d5b0e339287324ce332ccb8c30896.tar.gz |
Merge "Port os-tenant-networks plugin to v2.1(v3) infrastructure"
Diffstat (limited to 'nova/api/openstack/compute/plugins')
-rw-r--r-- | nova/api/openstack/compute/plugins/v3/tenant_networks.py | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/nova/api/openstack/compute/plugins/v3/tenant_networks.py b/nova/api/openstack/compute/plugins/v3/tenant_networks.py new file mode 100644 index 0000000000..90d3c29b73 --- /dev/null +++ b/nova/api/openstack/compute/plugins/v3/tenant_networks.py @@ -0,0 +1,217 @@ +# Copyright 2013 OpenStack Foundation +# 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 oslo.config import cfg +import six +from webob import exc + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova import context as nova_context +from nova import exception +from nova.i18n import _ +from nova.i18n import _LE +import nova.network +from nova.openstack.common import log as logging +from nova import quota + + +CONF = cfg.CONF +CONF.import_opt('enable_network_quota', + 'nova.api.openstack.compute.contrib.os_tenant_networks') +CONF.import_opt('use_neutron_default_nets', + 'nova.api.openstack.compute.contrib.os_tenant_networks') +CONF.import_opt('neutron_default_tenant_id', + 'nova.api.openstack.compute.contrib.os_tenant_networks') +CONF.import_opt('quota_networks', + 'nova.api.openstack.compute.contrib.os_tenant_networks') + + +ALIAS = 'os-tenant-networks' + +QUOTAS = quota.QUOTAS +LOG = logging.getLogger(__name__) +authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS) + + +def network_dict(network): + return {"id": network.get("uuid") or network.get("id"), + "cidr": str(network.get("cidr")), + "label": network.get("label")} + + +class TenantNetworkController(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_neutron_default_nets == "True": + try: + self._default_networks = self._get_default_networks() + except Exception: + LOG.exception(_LE("Failed to get default networks")) + + def _get_default_networks(self): + project_id = CONF.neutron_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()] + + @extensions.expected_errors(()) + def index(self, req): + context = req.environ['nova.context'] + authorize(context) + networks = list(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]} + + @extensions.expected_errors(404) + def show(self, req, id): + context = req.environ['nova.context'] + authorize(context) + try: + network = self.network_api.get(context, id) + except exception.NetworkNotFound: + msg = _("Network not found") + raise exc.HTTPNotFound(explanation=msg) + return {'network': network_dict(network)} + + @extensions.expected_errors((403, 404, 409)) + @wsgi.response(202) + def delete(self, req, id): + context = req.environ['nova.context'] + authorize(context) + reservation = None + try: + if CONF.enable_network_quota: + reservation = QUOTAS.reserve(context, networks=-1) + except Exception: + reservation = None + LOG.exception(_LE("Failed to update usages deallocating " + "network.")) + + def _rollback_quota(reservation): + if CONF.enable_network_quota and reservation: + QUOTAS.rollback(context, reservation) + + try: + self.network_api.delete(context, id) + except exception.PolicyNotAuthorized as e: + _rollback_quota(reservation) + raise exc.HTTPForbidden(explanation=six.text_type(e)) + except exception.NetworkInUse as e: + _rollback_quota(reservation) + raise exc.HTTPConflict(explanation=e.format_message()) + except exception.NetworkNotFound: + _rollback_quota(reservation) + msg = _("Network not found") + raise exc.HTTPNotFound(explanation=msg) + + if CONF.enable_network_quota and reservation: + QUOTAS.commit(context, reservation) + + @extensions.expected_errors((400, 403, 503)) + def create(self, req, body): + if not body: + _msg = _("Missing request body") + raise exc.HTTPBadRequest(explanation=_msg) + + 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.PolicyNotAuthorized as e: + raise exc.HTTPForbidden(explanation=six.text_type(e)) + 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 TenantNetworks(extensions.V3APIExtensionBase): + """Tenant-based Network Management Extension.""" + + name = "TenantNetworks" + alias = ALIAS + version = 1 + + def get_resources(self): + ext = extensions.ResourceExtension(ALIAS, TenantNetworkController()) + return [ext] + + def get_controller_extensions(self): + return [] + + +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')) |