Merge "Adding quotas on alarms"
This commit is contained in:
commit
9af6af5e52
@ -65,6 +65,14 @@ ALARM_API_OPTS = [
|
|||||||
default=True,
|
default=True,
|
||||||
help='Record alarm change events.'
|
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')
|
cfg.CONF.register_opts(ALARM_API_OPTS, group='alarm')
|
||||||
@ -99,6 +107,43 @@ class AlarmNotFound(ClientSideError):
|
|||||||
super(AlarmNotFound, self).__init__(msg, status_code=404)
|
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):
|
class AdvEnum(wtypes.wsproperty):
|
||||||
"""Handle default and mandatory for wtypes.Enum."""
|
"""Handle default and mandatory for wtypes.Enum."""
|
||||||
def __init__(self, name, *args, **kwargs):
|
def __init__(self, name, *args, **kwargs):
|
||||||
@ -2106,6 +2151,10 @@ class AlarmsController(rest.RestController):
|
|||||||
_set_ownership('user', user_limit, 'X-User-Id')
|
_set_ownership('user', user_limit, 'X-User-Id')
|
||||||
_set_ownership('project', project_limit, 'X-Project-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.timestamp = now
|
||||||
data.state_timestamp = now
|
data.state_timestamp = now
|
||||||
|
|
||||||
|
@ -2001,3 +2001,118 @@ class TestAlarms(v2.FunctionalTest,
|
|||||||
self.assertTrue(set(['alarm_id', 'detail', 'event_id', 'on_behalf_of',
|
self.assertTrue(set(['alarm_id', 'detail', 'event_id', 'on_behalf_of',
|
||||||
'project_id', 'timestamp', 'type',
|
'project_id', 'timestamp', 'type',
|
||||||
'user_id']).issubset(payload.keys()))
|
'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))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user