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
This commit is contained in:
parent
8b83990c12
commit
129fd64ef8
@ -297,11 +297,19 @@
|
|||||||
"updated": "2012-08-07T00:00:00+00:00"
|
"updated": "2012-08-07T00:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "os-networks",
|
"alias": "os-admin-networks",
|
||||||
"description": "Admin-only Network Management Extension.",
|
"description": "Admin-only Network Management Extension",
|
||||||
"links": [],
|
"links": [],
|
||||||
"name": "Networks",
|
"name": "AdminNetworks",
|
||||||
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
"namespace": "http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1",
|
||||||
|
"updated": "2011-12-23T00:00:00+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "os-networks",
|
||||||
|
"description": "Tenant-based Network Management Extension",
|
||||||
|
"links": [],
|
||||||
|
"name": "OSNetworks",
|
||||||
|
"namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1",
|
||||||
"updated": "2011-12-23T00:00:00+00:00"
|
"updated": "2011-12-23T00:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -125,12 +125,15 @@
|
|||||||
<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">
|
<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>
|
<description>Allow multiple create in the Create Server v1.1 API.</description>
|
||||||
</extension>
|
</extension>
|
||||||
<extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks/api/v1.1" name="Networks">
|
<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">
|
||||||
<description>Admin-only Network Management Extension.</description>
|
<description>Admin-only Network Management Extension</description>
|
||||||
</extension>
|
</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">
|
<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>
|
<description>Network association support.</description>
|
||||||
</extension>
|
</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">
|
||||||
|
<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">
|
<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">
|
||||||
<description>Quota classes management support.</description>
|
<description>Quota classes management support.</description>
|
||||||
</extension>
|
</extension>
|
||||||
|
14
doc/api_samples/os-networks/networks-list-res.json
Normal file
14
doc/api_samples/os-networks/networks-list-res.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"networks": [
|
||||||
|
{
|
||||||
|
"cidr": "10.0.0.0/29",
|
||||||
|
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
|
||||||
|
"label": "test_0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cidr": "10.0.0.8/29",
|
||||||
|
"id": "616fb98f-46ca-475e-917e-2563e5a8cd20",
|
||||||
|
"label": "test_1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
doc/api_samples/os-networks/networks-post-res.json
Normal file
7
doc/api_samples/os-networks/networks-post-res.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"cidr": "172.0.0.0/24",
|
||||||
|
"id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9",
|
||||||
|
"label": "public"
|
||||||
|
}
|
||||||
|
}
|
@ -13,4 +13,4 @@
|
|||||||
"security_group_rules": 20,
|
"security_group_rules": 20,
|
||||||
"security_groups": 10
|
"security_groups": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
<ram>51200</ram>
|
<ram>51200</ram>
|
||||||
<security_group_rules>20</security_group_rules>
|
<security_group_rules>20</security_group_rules>
|
||||||
<security_groups>10</security_groups>
|
<security_groups>10</security_groups>
|
||||||
</quota_set>
|
</quota_set>
|
||||||
|
@ -13,4 +13,4 @@
|
|||||||
"security_group_rules": 20,
|
"security_group_rules": 20,
|
||||||
"security_groups": 10
|
"security_groups": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
<ram>51200</ram>
|
<ram>51200</ram>
|
||||||
<security_group_rules>20</security_group_rules>
|
<security_group_rules>20</security_group_rules>
|
||||||
<security_groups>10</security_groups>
|
<security_groups>10</security_groups>
|
||||||
</quota_set>
|
</quota_set>
|
||||||
|
@ -12,4 +12,4 @@
|
|||||||
"security_group_rules": 20,
|
"security_group_rules": 20,
|
||||||
"security_groups": 45
|
"security_groups": 45
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
<ram>51200</ram>
|
<ram>51200</ram>
|
||||||
<security_group_rules>20</security_group_rules>
|
<security_group_rules>20</security_group_rules>
|
||||||
<security_groups>45</security_groups>
|
<security_groups>45</security_groups>
|
||||||
</quota_set>
|
</quota_set>
|
||||||
|
@ -27,8 +27,9 @@ from nova import network
|
|||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
authorize = extensions.extension_authorizer('compute', 'networks')
|
authorize = extensions.extension_authorizer('compute', 'admin_networks')
|
||||||
authorize_view = extensions.extension_authorizer('compute', 'networks:view')
|
authorize_view = extensions.extension_authorizer('compute',
|
||||||
|
'admin_networks:view')
|
||||||
|
|
||||||
|
|
||||||
def network_dict(context, network):
|
def network_dict(context, network):
|
||||||
@ -53,7 +54,7 @@ def network_dict(context, network):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class NetworkController(wsgi.Controller):
|
class AdminNetworkController(wsgi.Controller):
|
||||||
|
|
||||||
def __init__(self, network_api=None):
|
def __init__(self, network_api=None):
|
||||||
self.network_api = network_api or network.API()
|
self.network_api = network_api or network.API()
|
||||||
@ -149,20 +150,21 @@ class NetworkController(wsgi.Controller):
|
|||||||
return webob.Response(status_int=202)
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
|
|
||||||
class Networks(extensions.ExtensionDescriptor):
|
class Admin_networks(extensions.ExtensionDescriptor):
|
||||||
"""Admin-only Network Management Extension."""
|
"""Admin-only Network Management Extension"""
|
||||||
|
|
||||||
name = "Networks"
|
name = "AdminNetworks"
|
||||||
alias = "os-networks"
|
alias = "os-admin-networks"
|
||||||
namespace = "http://docs.openstack.org/compute/ext/networks/api/v1.1"
|
namespace = ("http://docs.openstack.org/compute/"
|
||||||
|
"ext/os-admin-networks/api/v1.1")
|
||||||
updated = "2011-12-23T00:00:00+00:00"
|
updated = "2011-12-23T00:00:00+00:00"
|
||||||
|
|
||||||
def get_resources(self):
|
def get_resources(self):
|
||||||
member_actions = {'action': 'POST'}
|
member_actions = {'action': 'POST'}
|
||||||
collection_actions = {'add': 'POST'}
|
collection_actions = {'add': 'POST'}
|
||||||
res = extensions.ResourceExtension(
|
res = extensions.ResourceExtension(
|
||||||
'os-networks',
|
'os-admin-networks',
|
||||||
NetworkController(),
|
AdminNetworkController(),
|
||||||
member_actions=member_actions,
|
member_actions=member_actions,
|
||||||
collection_actions=collection_actions)
|
collection_actions=collection_actions)
|
||||||
return [res]
|
return [res]
|
@ -62,6 +62,6 @@ class Networks_associate(extensions.ExtensionDescriptor):
|
|||||||
|
|
||||||
def get_controller_extensions(self):
|
def get_controller_extensions(self):
|
||||||
extension = extensions.ControllerExtension(
|
extension = extensions.ControllerExtension(
|
||||||
self, 'os-networks', NetworkAssociateActionController())
|
self, 'os-admin-networks', NetworkAssociateActionController())
|
||||||
|
|
||||||
return [extension]
|
return [extension]
|
||||||
|
213
nova/api/openstack/compute/contrib/os_networks.py
Normal file
213
nova/api/openstack/compute/contrib/os_networks.py
Normal file
@ -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'))
|
@ -1609,7 +1609,7 @@ class NetworkManager(manager.SchedulerDependentManager):
|
|||||||
fixnet = netaddr.IPNetwork(kwargs["cidr"])
|
fixnet = netaddr.IPNetwork(kwargs["cidr"])
|
||||||
each_subnet_size = fixnet.size / kwargs["num_networks"]
|
each_subnet_size = fixnet.size / kwargs["num_networks"]
|
||||||
if each_subnet_size > CONF.network_size:
|
if each_subnet_size > CONF.network_size:
|
||||||
subnet = 32 - int(math.log(CONF.network_size_size, 2))
|
subnet = 32 - int(math.log(CONF.network_size, 2))
|
||||||
oversize_msg = _(
|
oversize_msg = _(
|
||||||
'Subnet(s) too large, defaulting to /%s.'
|
'Subnet(s) too large, defaulting to /%s.'
|
||||||
' To override, specify network_size flag.') % subnet
|
' To override, specify network_size flag.') % subnet
|
||||||
@ -2303,18 +2303,23 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
|
|||||||
"""Create networks based on parameters."""
|
"""Create networks based on parameters."""
|
||||||
self._convert_int_args(kwargs)
|
self._convert_int_args(kwargs)
|
||||||
|
|
||||||
|
kwargs["vlan_start"] = kwargs.get("vlan_start") or CONF.vlan_start
|
||||||
|
kwargs["num_networks"] = (kwargs.get("num_networks") or
|
||||||
|
CONF.num_networks)
|
||||||
|
kwargs["network_size"] = (kwargs.get("network_size") or
|
||||||
|
CONF.network_size)
|
||||||
# Check that num_networks + vlan_start is not > 4094, fixes lp708025
|
# Check that num_networks + vlan_start is not > 4094, fixes lp708025
|
||||||
if kwargs['num_networks'] + kwargs['vlan_start'] > 4094:
|
if kwargs["num_networks"] + kwargs["vlan_start"] > 4094:
|
||||||
raise ValueError(_('The sum between the number of networks and'
|
raise ValueError(_('The sum between the number of networks and'
|
||||||
' the vlan start cannot be greater'
|
' the vlan start cannot be greater'
|
||||||
' than 4094'))
|
' than 4094'))
|
||||||
|
|
||||||
# check that num networks and network size fits in fixed_net
|
# check that num networks and network size fits in fixed_net
|
||||||
fixed_net = netaddr.IPNetwork(kwargs['cidr'])
|
fixed_net = netaddr.IPNetwork(kwargs['cidr'])
|
||||||
if len(fixed_net) < kwargs['num_networks'] * kwargs['network_size']:
|
if fixed_net.size < kwargs['num_networks'] * kwargs['network_size']:
|
||||||
raise ValueError(_('The network range is not big enough to fit '
|
raise ValueError(_('The network range is not '
|
||||||
'%(num_networks)s. Network size is %(network_size)s') %
|
'big enough to fit %(num_networks)s networks. Network '
|
||||||
kwargs)
|
'size is %(network_size)s') % kwargs)
|
||||||
|
|
||||||
kwargs['bridge_interface'] = (kwargs.get('bridge_interface') or
|
kwargs['bridge_interface'] = (kwargs.get('bridge_interface') or
|
||||||
CONF.vlan_interface)
|
CONF.vlan_interface)
|
||||||
|
@ -99,7 +99,7 @@ class CreateserverextTest(test.TestCase):
|
|||||||
osapi_compute_extension=[
|
osapi_compute_extension=[
|
||||||
'nova.api.openstack.compute.contrib.select_extensions'],
|
'nova.api.openstack.compute.contrib.select_extensions'],
|
||||||
osapi_compute_ext_list=['Createserverext', 'User_data',
|
osapi_compute_ext_list=['Createserverext', 'User_data',
|
||||||
'Security_groups', 'Networks'])
|
'Security_groups', 'Os_networks'])
|
||||||
|
|
||||||
def _make_stub_method(self, canned_return):
|
def _make_stub_method(self, canned_return):
|
||||||
def stub_method(*args, **kwargs):
|
def stub_method(*args, **kwargs):
|
||||||
|
@ -21,7 +21,7 @@ import uuid
|
|||||||
|
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute.contrib import networks
|
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 networks_associate
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import cfg
|
from nova.openstack.common import cfg
|
||||||
@ -177,7 +177,8 @@ class NetworksTest(test.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(NetworksTest, self).setUp()
|
super(NetworksTest, self).setUp()
|
||||||
self.fake_network_api = FakeNetworkAPI()
|
self.fake_network_api = FakeNetworkAPI()
|
||||||
self.controller = networks.NetworkController(self.fake_network_api)
|
self.controller = networks.AdminNetworkController(
|
||||||
|
self.fake_network_api)
|
||||||
self.associate_controller = networks_associate\
|
self.associate_controller = networks_associate\
|
||||||
.NetworkAssociateActionController(self.fake_network_api)
|
.NetworkAssociateActionController(self.fake_network_api)
|
||||||
fakes.stub_out_networking(self.stubs)
|
fakes.stub_out_networking(self.stubs)
|
||||||
|
@ -91,8 +91,7 @@ class QuotaClassSetsTest(test.TestCase):
|
|||||||
'injected_file_path_bytes': 255,
|
'injected_file_path_bytes': 255,
|
||||||
'security_groups': 10,
|
'security_groups': 10,
|
||||||
'security_group_rules': 20,
|
'security_group_rules': 20,
|
||||||
'key_pairs': 100,
|
'key_pairs': 100}}
|
||||||
}}
|
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank(
|
req = fakes.HTTPRequest.blank(
|
||||||
'/v2/fake4/os-quota-class-sets/test_class',
|
'/v2/fake4/os-quota-class-sets/test_class',
|
||||||
|
@ -51,8 +51,7 @@ class QuotaSetsTest(test.TestCase):
|
|||||||
'injected_file_content_bytes': 10240,
|
'injected_file_content_bytes': 10240,
|
||||||
'security_groups': 10,
|
'security_groups': 10,
|
||||||
'security_group_rules': 20,
|
'security_group_rules': 20,
|
||||||
'key_pairs': 100,
|
'key_pairs': 100}
|
||||||
}
|
|
||||||
|
|
||||||
quota_set = self.controller._format_quota_set('1234', raw_quota_set)
|
quota_set = self.controller._format_quota_set('1234', raw_quota_set)
|
||||||
qs = quota_set['quota_set']
|
qs = quota_set['quota_set']
|
||||||
@ -88,8 +87,7 @@ class QuotaSetsTest(test.TestCase):
|
|||||||
'injected_file_content_bytes': 10240,
|
'injected_file_content_bytes': 10240,
|
||||||
'security_groups': 10,
|
'security_groups': 10,
|
||||||
'security_group_rules': 20,
|
'security_group_rules': 20,
|
||||||
'key_pairs': 100,
|
'key_pairs': 100}}
|
||||||
}}
|
|
||||||
|
|
||||||
self.assertEqual(res_dict, expected)
|
self.assertEqual(res_dict, expected)
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ class ExtensionControllerTest(ExtensionTestCase):
|
|||||||
"Keypairs",
|
"Keypairs",
|
||||||
"Multinic",
|
"Multinic",
|
||||||
"MultipleCreate",
|
"MultipleCreate",
|
||||||
"Networks",
|
"OSNetworks",
|
||||||
"QuotaClasses",
|
"QuotaClasses",
|
||||||
"Quotas",
|
"Quotas",
|
||||||
"Rescue",
|
"Rescue",
|
||||||
|
@ -136,9 +136,10 @@ policy_data = """
|
|||||||
"compute_extension:instance_usage_audit_log": "",
|
"compute_extension:instance_usage_audit_log": "",
|
||||||
"compute_extension:keypairs": "",
|
"compute_extension:keypairs": "",
|
||||||
"compute_extension:multinic": "",
|
"compute_extension:multinic": "",
|
||||||
"compute_extension:networks": "",
|
"compute_extension:admin_networks": "",
|
||||||
"compute_extension:networks:view": "",
|
"compute_extension:admin_networks:view": "",
|
||||||
"compute_extension:networks_associate": "",
|
"compute_extension:networks_associate": "",
|
||||||
|
"compute_extension:os-networks": "",
|
||||||
"compute_extension:quotas:show": "",
|
"compute_extension:quotas:show": "",
|
||||||
"compute_extension:quotas:update": "",
|
"compute_extension:quotas:update": "",
|
||||||
"compute_extension:quota_classes": "",
|
"compute_extension:quota_classes": "",
|
||||||
@ -191,6 +192,7 @@ policy_data = """
|
|||||||
|
|
||||||
"network:get_all_networks": "",
|
"network:get_all_networks": "",
|
||||||
"network:get_network": "",
|
"network:get_network": "",
|
||||||
|
"network:create_networks": "",
|
||||||
"network:delete_network": "",
|
"network:delete_network": "",
|
||||||
"network:disassociate_network": "",
|
"network:disassociate_network": "",
|
||||||
"network:get_vifs_by_instance": "",
|
"network:get_vifs_by_instance": "",
|
||||||
|
@ -304,12 +304,20 @@
|
|||||||
"namespace": "http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1",
|
"namespace": "http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1",
|
||||||
"updated": "%(timestamp)s"
|
"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",
|
"alias": "os-networks",
|
||||||
"description": "%(text)s",
|
"description": "%(text)s",
|
||||||
"links": [],
|
"links": [],
|
||||||
"name": "Networks",
|
"name": "OSNetworks",
|
||||||
"namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1",
|
"namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1",
|
||||||
"updated": "%(timestamp)s"
|
"updated": "%(timestamp)s"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,10 @@
|
|||||||
<extension alias="os-multiple-create" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1" name="MultipleCreate">
|
<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>
|
<description>%(text)s</description>
|
||||||
</extension>
|
</extension>
|
||||||
<extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks/api/v1.1" name="Networks">
|
<extension alias="os-admin-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1" name="AdminNetworks">
|
||||||
|
<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">
|
||||||
<description>%(text)s</description>
|
<description>%(text)s</description>
|
||||||
</extension>
|
</extension>
|
||||||
<extension alias="os-networks-associate" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
|
<extension alias="os-networks-associate" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"networks": [
|
||||||
|
{
|
||||||
|
"cidr": "10.0.0.0/29",
|
||||||
|
"id": "%(id)s",
|
||||||
|
"label": "test_0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cidr": "10.0.0.8/29",
|
||||||
|
"id": "%(id)s",
|
||||||
|
"label": "test_1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"label": "public",
|
||||||
|
"cidr": "172.0.0.0/24",
|
||||||
|
"vlan_start": 1,
|
||||||
|
"num_networks": 1,
|
||||||
|
"network_size": 255
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"cidr": "172.0.0.0/24",
|
||||||
|
"id": "%(id)s",
|
||||||
|
"label": "public"
|
||||||
|
}
|
||||||
|
}
|
@ -13,4 +13,4 @@
|
|||||||
"security_group_rules": 20,
|
"security_group_rules": 20,
|
||||||
"security_groups": 10
|
"security_groups": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
<ram>51200</ram>
|
<ram>51200</ram>
|
||||||
<security_group_rules>20</security_group_rules>
|
<security_group_rules>20</security_group_rules>
|
||||||
<security_groups>10</security_groups>
|
<security_groups>10</security_groups>
|
||||||
</quota_set>
|
</quota_set>
|
||||||
|
@ -13,4 +13,4 @@
|
|||||||
"security_group_rules": 20,
|
"security_group_rules": 20,
|
||||||
"security_groups": 10
|
"security_groups": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
<ram>51200</ram>
|
<ram>51200</ram>
|
||||||
<security_group_rules>20</security_group_rules>
|
<security_group_rules>20</security_group_rules>
|
||||||
<security_groups>10</security_groups>
|
<security_groups>10</security_groups>
|
||||||
</quota_set>
|
</quota_set>
|
||||||
|
@ -12,4 +12,4 @@
|
|||||||
"security_group_rules": 20,
|
"security_group_rules": 20,
|
||||||
"security_groups": 45
|
"security_groups": 45
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
<ram>51200</ram>
|
<ram>51200</ram>
|
||||||
<security_group_rules>20</security_group_rules>
|
<security_group_rules>20</security_group_rules>
|
||||||
<security_groups>45</security_groups>
|
<security_groups>45</security_groups>
|
||||||
</quota_set>
|
</quota_set>
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
import inspect
|
import inspect
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
@ -29,7 +30,6 @@ from nova.api.openstack.compute.contrib import coverage_ext
|
|||||||
# Import extensions to pull in osapi_compute_extension CONF option used below.
|
# Import extensions to pull in osapi_compute_extension CONF option used below.
|
||||||
from nova.api.openstack.compute import extensions
|
from nova.api.openstack.compute import extensions
|
||||||
from nova.cloudpipe.pipelib import CloudPipe
|
from nova.cloudpipe.pipelib import CloudPipe
|
||||||
from nova.compute import api
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova.db.sqlalchemy import models
|
from nova.db.sqlalchemy import models
|
||||||
@ -41,6 +41,7 @@ from nova.openstack.common import importutils
|
|||||||
from nova.openstack.common import jsonutils
|
from nova.openstack.common import jsonutils
|
||||||
from nova.openstack.common.log import logging
|
from nova.openstack.common.log import logging
|
||||||
from nova.openstack.common import timeutils
|
from nova.openstack.common import timeutils
|
||||||
|
import nova.quota
|
||||||
from nova.scheduler import driver
|
from nova.scheduler import driver
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests import fake_network
|
from nova.tests import fake_network
|
||||||
@ -139,6 +140,7 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase):
|
|||||||
return cls._get_sample_path(name, dirname, suffix='.tpl')
|
return cls._get_sample_path(name, dirname, suffix='.tpl')
|
||||||
|
|
||||||
def _read_template(self, name):
|
def _read_template(self, name):
|
||||||
|
|
||||||
template = self._get_template(name)
|
template = self._get_template(name)
|
||||||
if self.generate_samples and not os.path.exists(template):
|
if self.generate_samples and not os.path.exists(template):
|
||||||
with open(template, 'w') as outf:
|
with open(template, 'w') as outf:
|
||||||
@ -371,7 +373,7 @@ class ApiSamplesTrap(ApiSampleTestBase):
|
|||||||
do_not_approve_additions.append('os-fping')
|
do_not_approve_additions.append('os-fping')
|
||||||
do_not_approve_additions.append('os-hypervisors')
|
do_not_approve_additions.append('os-hypervisors')
|
||||||
do_not_approve_additions.append('os-instance_usage_audit_log')
|
do_not_approve_additions.append('os-instance_usage_audit_log')
|
||||||
do_not_approve_additions.append('os-networks')
|
do_not_approve_additions.append('os-admin-networks')
|
||||||
do_not_approve_additions.append('os-services')
|
do_not_approve_additions.append('os-services')
|
||||||
do_not_approve_additions.append('os-volumes')
|
do_not_approve_additions.append('os-volumes')
|
||||||
|
|
||||||
@ -2308,6 +2310,43 @@ class DiskConfigXmlTest(DiskConfigJsonTest):
|
|||||||
ctype = 'xml'
|
ctype = 'xml'
|
||||||
|
|
||||||
|
|
||||||
|
class OsNetworksJsonTests(ApiSampleTestBase):
|
||||||
|
extension_name = ("nova.api.openstack.compute.contrib.os_networks"
|
||||||
|
".Os_networks")
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(OsNetworksJsonTests, self).setUp()
|
||||||
|
CONF.set_override("enable_network_quota", True)
|
||||||
|
|
||||||
|
def fake(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.stubs.Set(nova.quota.QUOTAS, "reserve", fake)
|
||||||
|
self.stubs.Set(nova.quota.QUOTAS, "commit", fake)
|
||||||
|
self.stubs.Set(nova.quota.QUOTAS, "rollback", fake)
|
||||||
|
self.stubs.Set(nova.quota.QuotaEngine, "reserve", fake)
|
||||||
|
self.stubs.Set(nova.quota.QuotaEngine, "commit", fake)
|
||||||
|
self.stubs.Set(nova.quota.QuotaEngine, "rollback", fake)
|
||||||
|
|
||||||
|
def test_list_networks(self):
|
||||||
|
response = self._do_get('os-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", {})
|
||||||
|
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", {})
|
||||||
|
net = json.loads(response.read())
|
||||||
|
response = self._do_delete('os-networks/%s' % net["network"]["id"])
|
||||||
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
|
|
||||||
class NetworksAssociateJsonTests(ApiSampleTestBase):
|
class NetworksAssociateJsonTests(ApiSampleTestBase):
|
||||||
extension_name = ("nova.api.openstack.compute.contrib"
|
extension_name = ("nova.api.openstack.compute.contrib"
|
||||||
".networks_associate.Networks_associate")
|
".networks_associate.Networks_associate")
|
||||||
@ -2319,7 +2358,7 @@ class NetworksAssociateJsonTests(ApiSampleTestBase):
|
|||||||
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
|
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
|
||||||
# Networks_associate requires Networks to be update
|
# Networks_associate requires Networks to be update
|
||||||
f['osapi_compute_extension'].append(
|
f['osapi_compute_extension'].append(
|
||||||
'nova.api.openstack.compute.contrib.networks.Networks')
|
'nova.api.openstack.compute.contrib.admin_networks.Admin_networks')
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -2333,25 +2372,25 @@ class NetworksAssociateJsonTests(ApiSampleTestBase):
|
|||||||
self.stubs.Set(api.API, "associate", fake_associate)
|
self.stubs.Set(api.API, "associate", fake_associate)
|
||||||
|
|
||||||
def test_disassociate(self):
|
def test_disassociate(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
response = self._do_post('os-admin-networks/1/action',
|
||||||
'network-disassociate-req',
|
'network-disassociate-req',
|
||||||
{})
|
{})
|
||||||
self.assertEqual(response.status, 202)
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
def test_disassociate_host(self):
|
def test_disassociate_host(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
response = self._do_post('os-admin-networks/1/action',
|
||||||
'network-disassociate-host-req',
|
'network-disassociate-host-req',
|
||||||
{})
|
{})
|
||||||
self.assertEqual(response.status, 202)
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
def test_disassociate_project(self):
|
def test_disassociate_project(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
response = self._do_post('os-admin-networks/1/action',
|
||||||
'network-disassociate-project-req',
|
'network-disassociate-project-req',
|
||||||
{})
|
{})
|
||||||
self.assertEqual(response.status, 202)
|
self.assertEqual(response.status, 202)
|
||||||
|
|
||||||
def test_associate_host(self):
|
def test_associate_host(self):
|
||||||
response = self._do_post('os-networks/1/action',
|
response = self._do_post('os-admin-networks/1/action',
|
||||||
'network-associate-host-req',
|
'network-associate-host-req',
|
||||||
{"host": "testHost"})
|
{"host": "testHost"})
|
||||||
self.assertEqual(response.status, 202)
|
self.assertEqual(response.status, 202)
|
||||||
|
Loading…
Reference in New Issue
Block a user