Add domain level limit support - Manager

Add the manager logic for domain level limit support.

Change-Id: Iea87e8e16ca187f9da9a91a3a1aec48bc9baea10
bp: domain-level-limit
This commit is contained in:
wangxiyuan 2018-12-03 11:03:40 +08:00
parent 484bc96fe7
commit 347269184e
4 changed files with 110 additions and 29 deletions

View File

@ -60,6 +60,7 @@ class LimitModel(sql.ModelBase, sql.ModelDictMixin):
'internal_id',
'id',
'project_id',
'domain_id',
'service_id',
'region_id',
'resource_name',
@ -73,6 +74,7 @@ class LimitModel(sql.ModelBase, sql.ModelDictMixin):
internal_id = sql.Column(sql.Integer, primary_key=True, nullable=False)
id = sql.Column(sql.String(length=64), nullable=False, unique=True)
project_id = sql.Column(sql.String(64))
domain_id = sql.Column(sql.String(64))
_service_id = sql.Column('service_id', sql.String(255))
_region_id = sql.Column('region_id', sql.String(64), nullable=True)
_resource_name = sql.Column('resource_name', sql.String(255))
@ -159,7 +161,12 @@ class UnifiedLimit(base.UnifiedLimitDriverBase):
query,
hints).all()
else:
hints.add_filter('project_id', unified_limit['project_id'])
is_project_limit = (True if unified_limit.get('project_id')
else False)
if is_project_limit:
hints.add_filter('project_id', unified_limit['project_id'])
else:
hints.add_filter('domain_id', unified_limit['domain_id'])
with sql.session_for_read() as session:
query = session.query(LimitModel)
old_unified_limits = sql.filter_limit_query(LimitModel,
@ -167,15 +174,21 @@ class UnifiedLimit(base.UnifiedLimitDriverBase):
hints).all()
query = session.query(
LimitModel).outerjoin(RegisteredLimitModel)
new_unified_limits = query.filter(
LimitModel.project_id ==
unified_limit['project_id'],
query = query.filter(
RegisteredLimitModel.service_id ==
unified_limit['service_id'],
RegisteredLimitModel.region_id ==
unified_limit.get('region_id'),
RegisteredLimitModel.resource_name ==
unified_limit['resource_name']).all()
unified_limit['resource_name'])
if is_project_limit:
query = query.filter(
LimitModel.project_id == unified_limit['project_id'])
else:
query = query.filter(
LimitModel.domain_id == unified_limit['domain_id'])
new_unified_limits = query.all()
unified_limits = old_unified_limits + new_unified_limits
if unified_limits:
@ -315,7 +328,8 @@ class UnifiedLimit(base.UnifiedLimitDriverBase):
hints)
old_format_data = [s.to_dict() for s in limits]
project_filter = hint_copy.get_exact_filter_by_name('project_id')
if hint_copy.filters and (not project_filter
domain_filter = hint_copy.get_exact_filter_by_name('domain_id')
if hint_copy.filters and (not (project_filter or domain_filter)
or len(hint_copy.filters) > 1):
# If the hints contain "service_id", "region_id" or
# "resource_name", we should combine the registered_limit table
@ -328,6 +342,9 @@ class UnifiedLimit(base.UnifiedLimitDriverBase):
if project_filter:
limits = limits.filter(
LimitModel.project_id == project_filter['value'])
elif domain_filter:
limits = limits.filter(
LimitModel.domain_id == domain_filter['value'])
new_format_data = [s.to_dict() for s in limits]
return old_format_data + new_format_data

View File

@ -55,10 +55,14 @@ class Manager(manager.Manager):
project_id = unified_limit.get('project_id')
if project_id is not None:
project = PROVIDERS.resource_api.get_project(project_id)
# Keystone now does not support domain-level limits. This
# check can be removed if it'll be supported in the future.
if project['is_domain']:
raise exception.ProjectNotFound(project_id=project_id)
# Treat the input limit as domain level limit.
unified_limit['domain_id'] = unified_limit.pop(
'project_id')
domain_id = unified_limit.get('domain_id')
if domain_id is not None:
PROVIDERS.resource_api.get_domain(domain_id)
except exception.ServiceNotFound:
raise exception.ValidationError(attribute='service_id',
target=target)
@ -68,6 +72,9 @@ class Manager(manager.Manager):
except exception.ProjectNotFound:
raise exception.ValidationError(attribute='project_id',
target=target)
except exception.DomainNotFound:
raise exception.ValidationError(attribute='domain_id',
target=target)
def get_model(self):
"""Return information of the configured enforcement model."""

View File

@ -408,14 +408,15 @@ class LimitTests(object):
enforcement_model=uuid.uuid4().hex
)
def test_create_limit(self):
def test_create_project_limit(self):
# create one, return it.
limit_1 = unit.new_limit_ref(
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
description='test description')
description='test description',
domain_id=None)
limits = PROVIDERS.unified_limit_api.create_limits([limit_1])
self.assertDictEqual(limit_1, limits[0])
@ -424,12 +425,14 @@ class LimitTests(object):
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex)
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex,
domain_id=None)
limit_3 = unit.new_limit_ref(
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='backup', resource_limit=5, id=uuid.uuid4().hex)
resource_name='backup', resource_limit=5, id=uuid.uuid4().hex,
domain_id=None)
limits = PROVIDERS.unified_limit_api.create_limits([limit_2, limit_3])
for limit in limits:
@ -438,7 +441,18 @@ class LimitTests(object):
if limit['id'] == limit_3['id']:
self.assertDictEqual(limit_3, limit)
def test_create_limit_duplicate(self):
def test_create_domain_limit(self):
limit_1 = unit.new_limit_ref(
project_id=None,
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
description='test description',
domain_id=self.domain_default['id'])
limits = PROVIDERS.unified_limit_api.create_limits([limit_1])
self.assertDictEqual(limit_1, limits[0])
def test_create_project_limit_duplicate(self):
limit_1 = unit.new_limit_ref(
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
@ -456,6 +470,26 @@ class LimitTests(object):
PROVIDERS.unified_limit_api.create_limits,
[limit_1])
def test_create_domain_limit_duplicate(self):
limit_1 = unit.new_limit_ref(
project_id=None,
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
domain_id=self.domain_default['id'])
PROVIDERS.unified_limit_api.create_limits([limit_1])
# use different id but the same domain_id, service_id and region_id
limit_1 = unit.new_limit_ref(
project_id=None,
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
domain_id=self.domain_default['id'])
self.assertRaises(exception.Conflict,
PROVIDERS.unified_limit_api.create_limits,
[limit_1])
def test_create_limit_with_invalid_service_raises_validation_error(self):
limit = unit.new_limit_ref(
project_id=self.project_bar['id'],
@ -539,12 +573,14 @@ class LimitTests(object):
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex)
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
domain_id=None)
limit_2 = unit.new_limit_ref(
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex)
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex,
domain_id=None)
PROVIDERS.unified_limit_api.create_limits([limit_1, limit_2])
# list
@ -565,12 +601,14 @@ class LimitTests(object):
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex)
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
domain_id=None)
limit_2 = unit.new_limit_ref(
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex)
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex,
domain_id=None)
PROVIDERS.unified_limit_api.create_limits([limit_1, limit_2])
# list, limit is 1
@ -587,18 +625,26 @@ class LimitTests(object):
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex)
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
domain_id=None)
limit_2 = unit.new_limit_ref(
project_id=self.project_baz['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=10, id=uuid.uuid4().hex)
PROVIDERS.unified_limit_api.create_limits([limit_1, limit_2])
resource_name='snapshot', resource_limit=10, id=uuid.uuid4().hex,
domain_id=None)
limit_3 = unit.new_limit_ref(
project_id=None,
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=10, id=uuid.uuid4().hex,
domain_id=self.domain_default['id'])
PROVIDERS.unified_limit_api.create_limits([limit_1, limit_2, limit_3])
hints = driver_hints.Hints()
hints.add_filter('service_id', self.service_one['id'])
res = PROVIDERS.unified_limit_api.list_limits(hints)
self.assertEqual(2, len(res))
self.assertEqual(3, len(res))
hints = driver_hints.Hints()
hints.add_filter('region_id', self.region_one['id'])
@ -616,6 +662,11 @@ class LimitTests(object):
res = PROVIDERS.unified_limit_api.list_limits(hints)
self.assertEqual(1, len(res))
hints = driver_hints.Hints()
hints.add_filter('domain_id', self.domain_default['id'])
res = PROVIDERS.unified_limit_api.list_limits(hints)
self.assertEqual(1, len(res))
def test_list_limit_by_multi_filter_with_project_id(self):
limit_1 = unit.new_limit_ref(
project_id=self.project_bar['id'],
@ -641,12 +692,14 @@ class LimitTests(object):
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_one['id'],
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex)
resource_name='volume', resource_limit=10, id=uuid.uuid4().hex,
domain_id=None)
limit_2 = unit.new_limit_ref(
project_id=self.project_bar['id'],
service_id=self.service_one['id'],
region_id=self.region_two['id'],
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex)
resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex,
domain_id=None)
PROVIDERS.unified_limit_api.create_limits([limit_1, limit_2])
# show one

View File

@ -657,11 +657,11 @@ class LimitsTestCase(test_v3.RestfulTestCase):
service_id=self.service_id,
region_id=self.region_id,
resource_name='volume')
self.post(
'/limits',
body={'limits': [ref]},
token=self.system_admin_token,
expected_status=http_client.BAD_REQUEST)
r = self.post('/limits', body={'limits': [ref]},
token=self.system_admin_token)
limits = r.result['limits']
self.assertIsNone(limits[0]['project_id'])
self.assertEqual(self.domain_id, limits[0]['domain_id'])
def test_create_multi_limit(self):
ref1 = unit.new_limit_ref(project_id=self.project_id,
@ -1524,3 +1524,7 @@ class StrictTwoLevelLimitsTestCase(LimitsTestCase):
body={'limit': update_dict},
token=self.system_admin_token,
expected_status=http_client.FORBIDDEN)
def test_create_limit_with_domain_as_project(self):
self.skipTest('enable this test once strict two level model support'
'domain level check.')