Merge "Adding quotas on alarms"

This commit is contained in:
Jenkins 2014-08-26 16:09:42 +00:00 committed by Gerrit Code Review
commit 9af6af5e52
2 changed files with 164 additions and 0 deletions

View File

@ -65,6 +65,14 @@ ALARM_API_OPTS = [
default=True,
help='Record alarm change events.'
),
cfg.IntOpt('user_alarm_quota',
default=None,
help='Maximum number of alarms defined for a user.'
),
cfg.IntOpt('project_alarm_quota',
default=None,
help='Maximum number of alarms defined for a project.'
),
]
cfg.CONF.register_opts(ALARM_API_OPTS, group='alarm')
@ -99,6 +107,43 @@ class AlarmNotFound(ClientSideError):
super(AlarmNotFound, self).__init__(msg, status_code=404)
class OverQuota(ClientSideError):
def __init__(self, data):
d = {
'u': data.user_id,
'p': data.project_id
}
super(OverQuota, self).__init__(
_("Alarm quota exceeded for user %(u)s on project %(p)s") % d,
status_code=403)
def is_over_quota(conn, project_id, user_id):
"""Returns False if an alarm is within the set quotas, True otherwise.
:param conn: a backend connection object
:param project_id: the ID of the project setting the alarm
:param user_id: the ID of the user setting the alarm
"""
over_quota = False
# Start by checking for user quota
user_alarm_quota = cfg.CONF.alarm.user_alarm_quota
if user_alarm_quota is not None:
user_alarms = list(conn.get_alarms(user=user_id))
over_quota = len(user_alarms) >= user_alarm_quota
# If the user quota isn't reached, we check for the project quota
if not over_quota:
project_alarm_quota = cfg.CONF.alarm.project_alarm_quota
if project_alarm_quota is not None:
project_alarms = list(conn.get_alarms(project=project_id))
over_quota = len(project_alarms) >= project_alarm_quota
return over_quota
class AdvEnum(wtypes.wsproperty):
"""Handle default and mandatory for wtypes.Enum."""
def __init__(self, name, *args, **kwargs):
@ -2106,6 +2151,10 @@ class AlarmsController(rest.RestController):
_set_ownership('user', user_limit, 'X-User-Id')
_set_ownership('project', project_limit, 'X-Project-Id')
# Check if there's room for one more alarm
if is_over_quota(conn, data.project_id, data.user_id):
raise OverQuota(data)
data.timestamp = now
data.state_timestamp = now

View File

@ -2001,3 +2001,118 @@ class TestAlarms(v2.FunctionalTest,
self.assertTrue(set(['alarm_id', 'detail', 'event_id', 'on_behalf_of',
'project_id', 'timestamp', 'type',
'user_id']).issubset(payload.keys()))
class TestAlarmsQuotas(v2.FunctionalTest,
tests_db.MixinTestsWithBackendScenarios):
def setUp(self):
super(TestAlarmsQuotas, self).setUp()
self.auth_headers = {'X-User-Id': str(uuid.uuid4()),
'X-Project-Id': str(uuid.uuid4())}
def _test_alarm_quota(self):
alarm = {
'name': 'alarm',
'type': 'threshold',
'user_id': self.auth_headers['X-User-Id'],
'project_id': self.auth_headers['X-Project-Id'],
'threshold_rule': {
'meter_name': 'testmeter',
'query': [],
'comparison_operator': 'le',
'statistic': 'max',
'threshold': 42.0,
'period': 60,
'evaluation_periods': 1,
}
}
resp = self.post_json('/alarms', params=alarm,
headers=self.auth_headers)
self.assertEqual(201, resp.status_code)
alarms = self.get_json('/alarms')
self.assertEqual(1, len(alarms))
alarm['name'] = 'another_user_alarm'
resp = self.post_json('/alarms', params=alarm,
expect_errors=True,
headers=self.auth_headers)
self.assertEqual(403, resp.status_code)
faultstring = 'Alarm quota exceeded for user'
self.assertIn(faultstring,
resp.json['error_message']['faultstring'])
alarms = self.get_json('/alarms')
self.assertEqual(1, len(alarms))
def test_alarms_quotas(self):
self.CONF.set_override('user_alarm_quota', 1, group='alarm')
self.CONF.set_override('project_alarm_quota', 1, group='alarm')
self._test_alarm_quota()
def test_project_alarms_quotas(self):
self.CONF.set_override('project_alarm_quota', 1, group='alarm')
self._test_alarm_quota()
def test_user_alarms_quotas(self):
self.CONF.set_override('user_alarm_quota', 1, group='alarm')
self._test_alarm_quota()
def test_larger_limit_project_alarms_quotas(self):
self.CONF.set_override('user_alarm_quota', 1, group='alarm')
self.CONF.set_override('project_alarm_quota', 2, group='alarm')
self._test_alarm_quota()
def test_larger_limit_user_alarms_quotas(self):
self.CONF.set_override('user_alarm_quota', 2, group='alarm')
self.CONF.set_override('project_alarm_quota', 1, group='alarm')
self._test_alarm_quota()
def test_larger_limit_user_alarm_quotas_multitenant_user(self):
self.CONF.set_override('user_alarm_quota', 2, group='alarm')
self.CONF.set_override('project_alarm_quota', 1, group='alarm')
def _test(field, value):
query = [{
'field': field,
'op': 'eq',
'value': value
}]
alarms = self.get_json('/alarms', q=query)
self.assertEqual(1, len(alarms))
alarm = {
'name': 'alarm',
'type': 'threshold',
'user_id': self.auth_headers['X-User-Id'],
'project_id': self.auth_headers['X-Project-Id'],
'threshold_rule': {
'meter_name': 'testmeter',
'query': [],
'comparison_operator': 'le',
'statistic': 'max',
'threshold': 42.0,
'period': 60,
'evaluation_periods': 1,
}
}
resp = self.post_json('/alarms', params=alarm,
headers=self.auth_headers)
self.assertEqual(201, resp.status_code)
_test('project_id', self.auth_headers['X-Project-Id'])
self.auth_headers['X-Project-Id'] = str(uuid.uuid4())
alarm['name'] = 'another_user_alarm'
alarm['project_id'] = self.auth_headers['X-Project-Id']
resp = self.post_json('/alarms', params=alarm,
headers=self.auth_headers)
self.assertEqual(201, resp.status_code)
_test('project_id', self.auth_headers['X-Project-Id'])
alarms = self.get_json('/alarms')
self.assertEqual(2, len(alarms))