summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorMatt Dietz <matt.dietz@rackspace.com>2012-12-17 18:25:27 +0000
committerMatt Dietz <matt.dietz@rackspace.com>2013-01-08 17:02:20 +0000
commit129fd64ef8f29f5e511eb7c72b1bd9856900905a (patch)
tree709588bae73497615351452ddc9f6f7572b87282 /nova/api
parent8b83990c12e4dc03aa599dd5e68ce91a9cab4eb7 (diff)
downloadnova-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.py2
-rw-r--r--nova/api/openstack/compute/contrib/os_networks.py213
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'))