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:
Matt Dietz 2012-12-17 18:25:27 +00:00
parent 8b83990c12
commit 129fd64ef8
32 changed files with 389 additions and 57 deletions

View File

@ -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"
}, },
{ {

View File

@ -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>

View 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"
}
]
}

View File

@ -0,0 +1,7 @@
{
"network": {
"cidr": "172.0.0.0/24",
"id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9",
"label": "public"
}
}

View File

@ -13,4 +13,4 @@
"security_group_rules": 20, "security_group_rules": 20,
"security_groups": 10 "security_groups": 10
} }
} }

View File

@ -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>

View File

@ -13,4 +13,4 @@
"security_group_rules": 20, "security_group_rules": 20,
"security_groups": 10 "security_groups": 10
} }
} }

View File

@ -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>

View File

@ -12,4 +12,4 @@
"security_group_rules": 20, "security_group_rules": 20,
"security_groups": 45 "security_groups": 45
} }
} }

View File

@ -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>

View File

@ -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]

View File

@ -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]

View 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'))

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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',

View File

@ -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)

View File

@ -186,7 +186,7 @@ class ExtensionControllerTest(ExtensionTestCase):
"Keypairs", "Keypairs",
"Multinic", "Multinic",
"MultipleCreate", "MultipleCreate",
"Networks", "OSNetworks",
"QuotaClasses", "QuotaClasses",
"Quotas", "Quotas",
"Rescue", "Rescue",

View File

@ -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": "",

View File

@ -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"
}, },
{ {

View File

@ -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">

View File

@ -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"
}
]
}

View File

@ -0,0 +1,9 @@
{
"network": {
"label": "public",
"cidr": "172.0.0.0/24",
"vlan_start": 1,
"num_networks": 1,
"network_size": 255
}
}

View File

@ -0,0 +1,7 @@
{
"network": {
"cidr": "172.0.0.0/24",
"id": "%(id)s",
"label": "public"
}
}

View File

@ -13,4 +13,4 @@
"security_group_rules": 20, "security_group_rules": 20,
"security_groups": 10 "security_groups": 10
} }
} }

View File

@ -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>

View File

@ -13,4 +13,4 @@
"security_group_rules": 20, "security_group_rules": 20,
"security_groups": 10 "security_groups": 10
} }
} }

View File

@ -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>

View File

@ -12,4 +12,4 @@
"security_group_rules": 20, "security_group_rules": 20,
"security_groups": 45 "security_groups": 45
} }
} }

View File

@ -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>

View File

@ -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)