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:
parent
792aa823a4
commit
48017f27ba
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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, '
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user