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. Change-Id: I53d767acceaa5e2c08e75e6f18847f659cda8d8b Closes-Bug: 1365727
This commit is contained in:
parent
faaf73e95c
commit
dfb716b2c2
@ -98,3 +98,10 @@
|
|||||||
# (IntOpt) Timeout duration in seconds for the http request
|
# (IntOpt) Timeout duration in seconds for the http request
|
||||||
# Default value: 15
|
# Default value: 15
|
||||||
# http_timeout = 15
|
# http_timeout = 15
|
||||||
|
|
||||||
|
# (BoolOpt) Specify whether tenants are restricted from accessing network
|
||||||
|
# profiles belonging to other tenants.
|
||||||
|
# Default value: True, indicating other tenants cannot access network
|
||||||
|
# profiles belonging to a tenant.
|
||||||
|
#
|
||||||
|
# restrict_network_profiles = True
|
||||||
|
@ -70,6 +70,10 @@ cisco_n1k_opts = [
|
|||||||
help=_("Number of threads to use to make HTTP requests")),
|
help=_("Number of threads to use to make HTTP requests")),
|
||||||
cfg.IntOpt('http_timeout', default=15,
|
cfg.IntOpt('http_timeout', default=15,
|
||||||
help=_("N1K http timeout duration in seconds")),
|
help=_("N1K http timeout duration in seconds")),
|
||||||
|
cfg.BoolOpt('restrict_network_profiles', default=True,
|
||||||
|
help=_("Restrict tenants from accessing network profiles "
|
||||||
|
"belonging to some other tenant")),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
cfg.CONF.register_opts(cisco_opts, "CISCO")
|
cfg.CONF.register_opts(cisco_opts, "CISCO")
|
||||||
|
@ -516,7 +516,7 @@ def reserve_vxlan(db_session, network_profile):
|
|||||||
raise n_exc.NoNetworkAvailable()
|
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.
|
Allocate network using first available free segment ID in segment range.
|
||||||
|
|
||||||
@ -525,7 +525,7 @@ def alloc_network(db_session, network_profile_id):
|
|||||||
"""
|
"""
|
||||||
with db_session.begin(subtransactions=True):
|
with db_session.begin(subtransactions=True):
|
||||||
network_profile = get_network_profile(db_session,
|
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:
|
if network_profile.segment_type == c_const.NETWORK_TYPE_VLAN:
|
||||||
return reserve_vlan(db_session, network_profile)
|
return reserve_vlan(db_session, network_profile)
|
||||||
if network_profile.segment_type == c_const.NETWORK_TYPE_OVERLAY:
|
if network_profile.segment_type == c_const.NETWORK_TYPE_OVERLAY:
|
||||||
@ -785,12 +785,12 @@ def create_network_profile(db_session, network_profile):
|
|||||||
return net_profile
|
return net_profile
|
||||||
|
|
||||||
|
|
||||||
def delete_network_profile(db_session, id):
|
def delete_network_profile(db_session, id, tenant_id=None):
|
||||||
"""Delete Network Profile."""
|
"""Delete Network Profile."""
|
||||||
LOG.debug("delete_network_profile()")
|
LOG.debug("delete_network_profile()")
|
||||||
with db_session.begin(subtransactions=True):
|
with db_session.begin(subtransactions=True):
|
||||||
try:
|
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.delete(network_profile)
|
||||||
(db_session.query(n1kv_models_v2.ProfileBinding).
|
(db_session.query(n1kv_models_v2.ProfileBinding).
|
||||||
filter_by(profile_id=id).delete())
|
filter_by(profile_id=id).delete())
|
||||||
@ -799,21 +799,27 @@ def delete_network_profile(db_session, id):
|
|||||||
raise c_exc.ProfileTenantBindingNotFound(profile_id=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."""
|
"""Update Network Profile."""
|
||||||
LOG.debug("update_network_profile()")
|
LOG.debug("update_network_profile()")
|
||||||
with db_session.begin(subtransactions=True):
|
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)
|
profile.update(network_profile)
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
|
||||||
def get_network_profile(db_session, id):
|
def get_network_profile(db_session, id, tenant_id=None):
|
||||||
"""Get Network Profile."""
|
"""Get Network Profile."""
|
||||||
LOG.debug("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:
|
try:
|
||||||
return db_session.query(
|
return db_session.query(n1kv_models_v2.NetworkProfile).filter_by(
|
||||||
n1kv_models_v2.NetworkProfile).filter_by(id=id).one()
|
id=id).one()
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
raise c_exc.NetworkProfileNotFound(profile=id)
|
raise c_exc.NetworkProfileNotFound(profile=id)
|
||||||
|
|
||||||
@ -1085,10 +1091,12 @@ class NetworkProfile_db_mixin(object):
|
|||||||
"""
|
"""
|
||||||
# Check whether the network profile is in use.
|
# Check whether the network profile is in use.
|
||||||
if self._segment_in_use(context.session,
|
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)
|
raise c_exc.NetworkProfileInUse(profile=id)
|
||||||
# Delete and return the network profile if it is not in use.
|
# 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)
|
return self._make_network_profile_dict(_profile)
|
||||||
|
|
||||||
def update_network_profile(self, context, id, network_profile):
|
def update_network_profile(self, context, id, network_profile):
|
||||||
@ -1105,7 +1113,8 @@ class NetworkProfile_db_mixin(object):
|
|||||||
# Flag to check whether network profile is updated or not.
|
# Flag to check whether network profile is updated or not.
|
||||||
is_updated = False
|
is_updated = False
|
||||||
p = network_profile["network_profile"]
|
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.
|
# Update network profile to tenant id binding.
|
||||||
if context.is_admin and c_const.ADD_TENANTS in p:
|
if context.is_admin and c_const.ADD_TENANTS in p:
|
||||||
profile_bindings = _get_profile_bindings_by_uuid(context.session,
|
profile_bindings = _get_profile_bindings_by_uuid(context.session,
|
||||||
@ -1138,7 +1147,8 @@ class NetworkProfile_db_mixin(object):
|
|||||||
p.get("segment_range") != original_net_p.segment_range):
|
p.get("segment_range") != original_net_p.segment_range):
|
||||||
if not self._segment_in_use(context.session, original_net_p):
|
if not self._segment_in_use(context.session, original_net_p):
|
||||||
delete_segment_allocations(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,
|
self._validate_segment_range_uniqueness(context,
|
||||||
updated_net_p, id)
|
updated_net_p, id)
|
||||||
if original_net_p.segment_type == c_const.NETWORK_TYPE_VLAN:
|
if original_net_p.segment_type == c_const.NETWORK_TYPE_VLAN:
|
||||||
@ -1163,7 +1173,8 @@ class NetworkProfile_db_mixin(object):
|
|||||||
# Return network profile if it is successfully updated.
|
# Return network profile if it is successfully updated.
|
||||||
if is_updated:
|
if is_updated:
|
||||||
return self._make_network_profile_dict(
|
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):
|
def get_network_profile(self, context, id, fields=None):
|
||||||
"""
|
"""
|
||||||
@ -1175,7 +1186,7 @@ class NetworkProfile_db_mixin(object):
|
|||||||
profile dictionary. Only these fields will be returned
|
profile dictionary. Only these fields will be returned
|
||||||
:returns: network profile dictionary
|
: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)
|
return self._make_network_profile_dict(profile, fields)
|
||||||
|
|
||||||
def get_network_profiles(self, context, filters=None, fields=None):
|
def get_network_profiles(self, context, filters=None, fields=None):
|
||||||
@ -1230,7 +1241,7 @@ class NetworkProfile_db_mixin(object):
|
|||||||
:returns: true if network profile exist else False
|
:returns: true if network profile exist else False
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
get_network_profile(context.session, id)
|
get_network_profile(context.session, id, context.tenant_id)
|
||||||
return True
|
return True
|
||||||
except c_exc.NetworkProfileNotFound(profile=id):
|
except c_exc.NetworkProfileNotFound(profile=id):
|
||||||
return False
|
return False
|
||||||
|
@ -683,7 +683,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
LOG.debug('_send_update_network_request: %s', network['id'])
|
LOG.debug('_send_update_network_request: %s', network['id'])
|
||||||
db_session = context.session
|
db_session = context.session
|
||||||
profile = n1kv_db_v2.get_network_profile(
|
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()
|
n1kvclient = n1kv_client.Client()
|
||||||
body = {'description': network['name'],
|
body = {'description': network['name'],
|
||||||
'id': network['id'],
|
'id': network['id'],
|
||||||
@ -882,7 +882,8 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
# tenant network
|
# tenant network
|
||||||
(physical_network, network_type, segmentation_id,
|
(physical_network, network_type, segmentation_id,
|
||||||
multicast_ip) = n1kv_db_v2.alloc_network(session,
|
multicast_ip) = n1kv_db_v2.alloc_network(session,
|
||||||
profile_id)
|
profile_id,
|
||||||
|
context.tenant_id)
|
||||||
LOG.debug('Physical_network %(phy_net)s, '
|
LOG.debug('Physical_network %(phy_net)s, '
|
||||||
'seg_type %(net_type)s, '
|
'seg_type %(net_type)s, '
|
||||||
'seg_id %(seg_id)s, '
|
'seg_id %(seg_id)s, '
|
||||||
|
@ -44,6 +44,8 @@ from neutron.tests.unit import test_l3_schedulers
|
|||||||
PHYS_NET = 'some-phys-net'
|
PHYS_NET = 'some-phys-net'
|
||||||
VLAN_MIN = 100
|
VLAN_MIN = 100
|
||||||
VLAN_MAX = 110
|
VLAN_MAX = 110
|
||||||
|
TENANT_NOT_ADMIN = 'not_admin'
|
||||||
|
TENANT_TEST = 'test'
|
||||||
|
|
||||||
|
|
||||||
class FakeResponse(object):
|
class FakeResponse(object):
|
||||||
@ -159,6 +161,12 @@ class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||||||
profile['physical_network'] = PHYS_NET
|
profile['physical_network'] = PHYS_NET
|
||||||
net_p = n1kv_db_v2.create_network_profile(db_session, profile)
|
net_p = n1kv_db_v2.create_network_profile(db_session, profile)
|
||||||
n1kv_db_v2.sync_vlan_allocations(db_session, net_p)
|
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
|
return net_p
|
||||||
|
|
||||||
def setUp(self, ext_mgr=NetworkProfileTestExtensionManager()):
|
def setUp(self, ext_mgr=NetworkProfileTestExtensionManager()):
|
||||||
@ -557,7 +565,7 @@ class TestN1kvNetworkProfiles(N1kvPluginTestCase):
|
|||||||
self.new_create_request('network_profiles', net_p_dict)
|
self.new_create_request('network_profiles', net_p_dict)
|
||||||
bindings = (db_session.query(n1kv_models_v2.ProfileBinding).filter_by(
|
bindings = (db_session.query(n1kv_models_v2.ProfileBinding).filter_by(
|
||||||
profile_type="network"))
|
profile_type="network"))
|
||||||
self.assertEqual(bindings.count(), 0)
|
self.assertEqual(3, bindings.count())
|
||||||
|
|
||||||
def test_create_network_profile_with_old_add_tenant_fail(self):
|
def test_create_network_profile_with_old_add_tenant_fail(self):
|
||||||
data = self._prepare_net_profile_data('vlan')
|
data = self._prepare_net_profile_data('vlan')
|
||||||
@ -657,6 +665,52 @@ class TestN1kvNetworkProfiles(N1kvPluginTestCase):
|
|||||||
net_p['network_profile']['id'])
|
net_p['network_profile']['id'])
|
||||||
self.assertIsNotNone(tenant4)
|
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,
|
class TestN1kvBasicGet(test_plugin.TestBasicGet,
|
||||||
N1kvPluginTestCase):
|
N1kvPluginTestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user