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
This commit is contained in:
parent
ddc9d1466e
commit
382435275e
@ -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"
|
||||
},
|
||||
{
|
||||
|
@ -125,13 +125,13 @@
|
||||
<extension alias="os-multiple-create" updated="2012-08-07T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1" name="MultipleCreate">
|
||||
<description>Allow multiple create in the Create Server v1.1 API.</description>
|
||||
</extension>
|
||||
<extension alias="os-admin-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1" name="AdminNetworks">
|
||||
<extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="Networks">
|
||||
<description>Admin-only Network Management Extension.</description>
|
||||
</extension>
|
||||
<extension alias="os-networks-associate" updated="2012-11-19T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
|
||||
<description>Network association support.</description>
|
||||
</extension>
|
||||
<extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="OSNetworks">
|
||||
<extension alias="os-tenant-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2" name="OSTenantNetworks">
|
||||
<description>Tenant-based Network Management Extension.</description>
|
||||
</extension>
|
||||
<extension alias="os-quota-class-sets" updated="2012-03-12T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
|
||||
|
@ -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]
|
@ -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]
|
||||
|
@ -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]
|
||||
|
214
nova/api/openstack/compute/contrib/os_tenant_networks.py
Normal file
214
nova/api/openstack/compute/contrib/os_tenant_networks.py
Normal file
@ -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'))
|
@ -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)
|
||||
|
@ -185,7 +185,6 @@ class ExtensionControllerTest(ExtensionTestCase):
|
||||
"Keypairs",
|
||||
"Multinic",
|
||||
"MultipleCreate",
|
||||
"OSNetworks",
|
||||
"QuotaClasses",
|
||||
"Quotas",
|
||||
"Rescue",
|
||||
|
@ -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": "",
|
||||
|
@ -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",
|
||||
|
@ -114,10 +114,10 @@
|
||||
<extension alias="os-multiple-create" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1" name="MultipleCreate">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="os-admin-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1" name="AdminNetworks">
|
||||
<extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="Networks">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="OSNetworks">
|
||||
<extension alias="os-tenant-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2" name="OSTenantNetworks">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="os-networks-associate" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user