CSCO:Tenants not to access unshared n/w profiles

Ensure that a n1kv tenant who has no access to a network profile
belonging to some other tenant, is not allowed to modify that profile.
Currently, a tenant can create networks on any network profile if he
has the network profile id. For this backport the default value for
the config option is set to False for compatibility reasons.

Conflicts:
        neutron/plugins/cisco/db/n1kv_db_v2.py
        neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py

Change-Id: I53d767acceaa5e2c08e75e6f18847f659cda8d8b
Closes-Bug: 1365727
(cherry picked from commit f0ea09dffc)
This commit is contained in:
Saksham Varma 2014-09-04 14:53:50 -07:00 committed by Steven Hillman
parent 792aa823a4
commit 48017f27ba
5 changed files with 96 additions and 19 deletions

View File

@ -98,3 +98,10 @@
# (IntOpt) Timeout duration in seconds for the http request
# Default value: 15
# http_timeout = 15
# (BoolOpt) Specify whether tenants are restricted from accessing network
# profiles belonging to other tenants.
# Default value: False, indicating other tenants can create networks using
# profiles belonging to a tenant.
#
# restrict_network_profiles = False

View File

@ -70,6 +70,10 @@ cisco_n1k_opts = [
help=_("Number of threads to use to make HTTP requests")),
cfg.IntOpt('http_timeout', default=15,
help=_("N1K http timeout duration in seconds")),
cfg.BoolOpt('restrict_network_profiles', default=False,
help=_("Restrict tenants from accessing network profiles "
"belonging to some other tenant")),
]
cfg.CONF.register_opts(cisco_opts, "CISCO")

View File

@ -515,7 +515,7 @@ def reserve_vxlan(db_session, network_profile):
raise n_exc.NoNetworkAvailable()
def alloc_network(db_session, network_profile_id):
def alloc_network(db_session, network_profile_id, tenant_id):
"""
Allocate network using first available free segment ID in segment range.
@ -524,7 +524,7 @@ def alloc_network(db_session, network_profile_id):
"""
with db_session.begin(subtransactions=True):
network_profile = get_network_profile(db_session,
network_profile_id)
network_profile_id, tenant_id)
if network_profile.segment_type == c_const.NETWORK_TYPE_VLAN:
return reserve_vlan(db_session, network_profile)
if network_profile.segment_type == c_const.NETWORK_TYPE_OVERLAY:
@ -784,12 +784,12 @@ def create_network_profile(db_session, network_profile):
return net_profile
def delete_network_profile(db_session, id):
def delete_network_profile(db_session, id, tenant_id=None):
"""Delete Network Profile."""
LOG.debug(_("delete_network_profile()"))
with db_session.begin(subtransactions=True):
try:
network_profile = get_network_profile(db_session, id)
network_profile = get_network_profile(db_session, id, tenant_id)
db_session.delete(network_profile)
(db_session.query(n1kv_models_v2.ProfileBinding).
filter_by(profile_id=id).delete())
@ -798,21 +798,27 @@ def delete_network_profile(db_session, id):
raise c_exc.ProfileTenantBindingNotFound(profile_id=id)
def update_network_profile(db_session, id, network_profile):
def update_network_profile(db_session, id, network_profile, tenant_id=None):
"""Update Network Profile."""
LOG.debug(_("update_network_profile()"))
with db_session.begin(subtransactions=True):
profile = get_network_profile(db_session, id)
profile = get_network_profile(db_session, id, tenant_id)
profile.update(network_profile)
return profile
def get_network_profile(db_session, id):
def get_network_profile(db_session, id, tenant_id=None):
"""Get Network Profile."""
LOG.debug(_("get_network_profile()"))
if tenant_id and c_conf.CISCO_N1K.restrict_network_profiles:
if _profile_binding_exists(db_session=db_session,
tenant_id=tenant_id,
profile_id=id,
profile_type=c_const.NETWORK) is None:
raise c_exc.ProfileTenantBindingNotFound(profile_id=id)
try:
return db_session.query(
n1kv_models_v2.NetworkProfile).filter_by(id=id).one()
return db_session.query(n1kv_models_v2.NetworkProfile).filter_by(
id=id).one()
except exc.NoResultFound:
raise c_exc.NetworkProfileNotFound(profile=id)
@ -1084,10 +1090,12 @@ class NetworkProfile_db_mixin(object):
"""
# Check whether the network profile is in use.
if self._segment_in_use(context.session,
get_network_profile(context.session, id)):
get_network_profile(context.session, id,
context.tenant_id)):
raise c_exc.NetworkProfileInUse(profile=id)
# Delete and return the network profile if it is not in use.
_profile = delete_network_profile(context.session, id)
_profile = delete_network_profile(context.session, id,
context.tenant_id)
return self._make_network_profile_dict(_profile)
def update_network_profile(self, context, id, network_profile):
@ -1104,7 +1112,8 @@ class NetworkProfile_db_mixin(object):
# Flag to check whether network profile is updated or not.
is_updated = False
p = network_profile["network_profile"]
original_net_p = get_network_profile(context.session, id)
original_net_p = get_network_profile(context.session, id,
context.tenant_id)
# Update network profile to tenant id binding.
if context.is_admin and c_const.ADD_TENANTS in p:
profile_bindings = _get_profile_bindings_by_uuid(context.session,
@ -1137,7 +1146,8 @@ class NetworkProfile_db_mixin(object):
p.get("segment_range") != original_net_p.segment_range):
if not self._segment_in_use(context.session, original_net_p):
delete_segment_allocations(context.session, original_net_p)
updated_net_p = update_network_profile(context.session, id, p)
updated_net_p = update_network_profile(context.session, id, p,
context.tenant_id)
self._validate_segment_range_uniqueness(context,
updated_net_p, id)
if original_net_p.segment_type == c_const.NETWORK_TYPE_VLAN:
@ -1162,7 +1172,8 @@ class NetworkProfile_db_mixin(object):
# Return network profile if it is successfully updated.
if is_updated:
return self._make_network_profile_dict(
update_network_profile(context.session, id, p))
update_network_profile(context.session, id, p,
context.tenant_id))
def get_network_profile(self, context, id, fields=None):
"""
@ -1174,7 +1185,7 @@ class NetworkProfile_db_mixin(object):
profile dictionary. Only these fields will be returned
:returns: network profile dictionary
"""
profile = get_network_profile(context.session, id)
profile = get_network_profile(context.session, id, context.tenant_id)
return self._make_network_profile_dict(profile, fields)
def get_network_profiles(self, context, filters=None, fields=None):
@ -1229,7 +1240,7 @@ class NetworkProfile_db_mixin(object):
:returns: true if network profile exist else False
"""
try:
get_network_profile(context.session, id)
get_network_profile(context.session, id, context.tenant_id)
return True
except c_exc.NetworkProfileNotFound(profile=id):
return False

View File

@ -680,7 +680,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
LOG.debug(_('_send_update_network_request: %s'), network['id'])
db_session = context.session
profile = n1kv_db_v2.get_network_profile(
db_session, network[n1kv.PROFILE_ID])
db_session, network[n1kv.PROFILE_ID], context.tenant_id)
n1kvclient = n1kv_client.Client()
body = {'description': network['name'],
'id': network['id'],
@ -880,7 +880,8 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
# tenant network
(physical_network, network_type, segmentation_id,
multicast_ip) = n1kv_db_v2.alloc_network(session,
profile_id)
profile_id,
context.tenant_id)
LOG.debug(_('Physical_network %(phy_net)s, '
'seg_type %(net_type)s, '
'seg_id %(seg_id)s, '

View File

@ -44,6 +44,8 @@ from neutron.tests.unit import test_l3_schedulers
PHYS_NET = 'some-phys-net'
VLAN_MIN = 100
VLAN_MAX = 110
TENANT_NOT_ADMIN = 'not_admin'
TENANT_TEST = 'test'
class FakeResponse(object):
@ -159,6 +161,12 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
profile['physical_network'] = PHYS_NET
net_p = n1kv_db_v2.create_network_profile(db_session, profile)
n1kv_db_v2.sync_vlan_allocations(db_session, net_p)
n1kv_db_v2.create_profile_binding(db_session, self.tenant_id,
net_p['id'], c_const.NETWORK)
n1kv_db_v2.create_profile_binding(db_session, TENANT_NOT_ADMIN,
net_p['id'], c_const.NETWORK)
n1kv_db_v2.create_profile_binding(db_session, TENANT_TEST,
net_p['id'], c_const.NETWORK)
return net_p
def setUp(self, ext_mgr=NetworkProfileTestExtensionManager()):
@ -571,7 +579,7 @@ class TestN1kvNetworkProfiles(N1kvPluginTestCase):
self.new_create_request('network_profiles', net_p_dict)
bindings = (db_session.query(n1kv_models_v2.ProfileBinding).filter_by(
profile_type="network"))
self.assertEqual(bindings.count(), 0)
self.assertEqual(3, bindings.count())
def test_create_network_profile_with_old_add_tenant_fail(self):
data = self._prepare_net_profile_data('vlan')
@ -671,6 +679,52 @@ class TestN1kvNetworkProfiles(N1kvPluginTestCase):
net_p['network_profile']['id'])
self.assertIsNotNone(tenant4)
def test_get_network_profile_restricted(self):
c_conf.CONF.set_override('restrict_network_profiles', True,
'CISCO_N1K')
ctx1 = context.Context(user_id='admin',
tenant_id='tenant1',
is_admin=True)
sess1 = db.get_session()
net_p = self._make_test_profile(name='netp1')
n1kv_db_v2.create_profile_binding(sess1, ctx1.tenant_id,
net_p['id'], c_const.NETWORK)
#network profile binding with creator tenant should always exist
profile = n1kv_db_v2.get_network_profile(sess1, net_p['id'],
ctx1.tenant_id)
self.assertIsNotNone(profile)
ctx2 = context.Context(user_id='non_admin',
tenant_id='tenant2',
is_admin=False)
sess2 = db.get_session()
self.assertRaises(c_exc.ProfileTenantBindingNotFound,
n1kv_db_v2.get_network_profile,
sess2, net_p['id'], ctx2.tenant_id)
def test_get_network_profile_unrestricted(self):
c_conf.CONF.set_override('restrict_network_profiles', False,
'CISCO_N1K')
ctx1 = context.Context(user_id='admin',
tenant_id='tenant1',
is_admin=True)
sess1 = db.get_session()
net_p = self._make_test_profile(name='netp1')
n1kv_db_v2.create_profile_binding(sess1, ctx1.tenant_id,
net_p['id'], c_const.NETWORK)
# network profile binding with creator tenant should always exist
profile = n1kv_db_v2.get_network_profile(sess1, net_p['id'],
ctx1.tenant_id)
self.assertIsNotNone(profile)
ctx2 = context.Context(user_id='non_admin',
tenant_id='tenant2',
is_admin=False)
sess2 = db.get_session()
profile = n1kv_db_v2.get_network_profile(sess2, net_p['id'],
ctx2.tenant_id)
#network profile will be returned even though the profile is
#not bound to tenant of sess2
self.assertIsNotNone(profile)
class TestN1kvBasicGet(test_plugin.TestBasicGet,
N1kvPluginTestCase):