Add name support for show, update and delete

Since the alarm name is limited to be unique for
each project, add support for users to show, update
and delete alarms using alarm name will be much more
user friendly than using alarm_id.

Partial-Bug: #1556809

Change-Id: I2d9316a24372f28b65c15a02aaefda8c0a1f748b
This commit is contained in:
Kevin_Zheng
2016-03-14 19:17:57 +08:00
parent f4d92717a5
commit 8218e5d53d
3 changed files with 162 additions and 22 deletions

View File

@@ -47,6 +47,10 @@ class MutipleMeaningException(object):
"""An mixin for exception that can be enhanced by reading the details""" """An mixin for exception that can be enhanced by reading the details"""
class CommandError(Exception):
pass
class BadRequest(ClientException): class BadRequest(ClientException):
"""HTTP 400 - Bad request: you sent some malformed data.""" """HTTP 400 - Bad request: you sent some malformed data."""
http_status = 400 http_status = 400
@@ -110,6 +114,10 @@ class RateLimit(RetryAfterException):
message = "Rate limit" message = "Rate limit"
class NoUniqueMatch(Exception):
pass
class NotImplemented(ClientException): class NotImplemented(ClientException):
"""HTTP 501 - Not Implemented: """HTTP 501 - Not Implemented:

View File

@@ -73,6 +73,21 @@ class AodhClientTest(base.ClientTestBase):
self.assertEqual('ev_alarm1', alarm_show['name']) self.assertEqual('ev_alarm1', alarm_show['name'])
self.assertEqual('dummy', alarm_show['event_type']) self.assertEqual('dummy', alarm_show['event_type'])
# GET BY NAME
result = self.aodh(
'alarm', params="show --alarm-name ev_alarm1")
alarm_show = self.details_multiple(result)[0]
self.assertEqual(ALARM_ID, alarm_show["alarm_id"])
self.assertEqual(PROJECT_ID, alarm_show["project_id"])
self.assertEqual('ev_alarm1', alarm_show['name'])
self.assertEqual('dummy', alarm_show['event_type'])
# GET BY NAME AND ID ERROR
self.assertRaises(exceptions.CommandFailed,
self.aodh, u'alarm',
params=(u"show %s --alarm-name ev_alarm1" %
ALARM_ID))
# LIST # LIST
result = self.aodh('alarm', params="list") result = self.aodh('alarm', params="list")
self.assertIn(ALARM_ID, self.assertIn(ALARM_ID,
@@ -117,12 +132,12 @@ class AodhClientTest(base.ClientTestBase):
# CREATE # CREATE
result = self.aodh(u'alarm', result = self.aodh(u'alarm',
params=(u"create --type threshold --name alarm1 " params=(u"create --type threshold --name alarm_th "
"-m meter_name --threshold 5 " "-m meter_name --threshold 5 "
"--project-id %s" % PROJECT_ID)) "--project-id %s" % PROJECT_ID))
alarm = self.details_multiple(result)[0] alarm = self.details_multiple(result)[0]
ALARM_ID = alarm['alarm_id'] ALARM_ID = alarm['alarm_id']
self.assertEqual('alarm1', alarm['name']) self.assertEqual('alarm_th', alarm['name'])
self.assertEqual('meter_name', alarm['meter_name']) self.assertEqual('meter_name', alarm['meter_name'])
self.assertEqual('5.0', alarm['threshold']) self.assertEqual('5.0', alarm['threshold'])
@@ -144,17 +159,18 @@ class AodhClientTest(base.ClientTestBase):
# CREATE FAIL # CREATE FAIL
result = self.aodh(u'alarm', result = self.aodh(u'alarm',
params=(u"create --type threshold --name alarm1 " params=(u"create --type threshold --name alarm_th "
"-m meter_name --threshold 5 " "-m meter_name --threshold 5 "
"--project-id %s" % PROJECT_ID), "--project-id %s" % PROJECT_ID),
fail_ok=True, merge_stderr=True) fail_ok=True, merge_stderr=True)
self.assertFirstLineStartsWith( self.assertFirstLineStartsWith(
result.split('\n'), "Alarm with name='alarm1' exists (HTTP 409)") result.split('\n'),
"Alarm with name='alarm_th' exists (HTTP 409)")
# CREATE FAIL MISSING PARAM # CREATE FAIL MISSING PARAM
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
self.aodh, u'alarm', self.aodh, u'alarm',
params=(u"create --type threshold --name alarm1 " params=(u"create --type threshold --name alarm_th "
"--project-id %s" % PROJECT_ID)) "--project-id %s" % PROJECT_ID))
# UPDATE # UPDATE
@@ -172,10 +188,26 @@ class AodhClientTest(base.ClientTestBase):
alarm_show = self.details_multiple(result)[0] alarm_show = self.details_multiple(result)[0]
self.assertEqual(ALARM_ID, alarm_show["alarm_id"]) self.assertEqual(ALARM_ID, alarm_show["alarm_id"])
self.assertEqual(PROJECT_ID, alarm_show["project_id"]) self.assertEqual(PROJECT_ID, alarm_show["project_id"])
self.assertEqual('alarm1', alarm_show['name']) self.assertEqual('alarm_th', alarm_show['name'])
self.assertEqual('meter_name', alarm_show['meter_name']) self.assertEqual('meter_name', alarm_show['meter_name'])
self.assertEqual('10.0', alarm_show['threshold']) self.assertEqual('10.0', alarm_show['threshold'])
# GET BY NAME
result = self.aodh(
'alarm', params="show --alarm-name alarm_th")
alarm_show = self.details_multiple(result)[0]
self.assertEqual(ALARM_ID, alarm_show["alarm_id"])
self.assertEqual(PROJECT_ID, alarm_show["project_id"])
self.assertEqual('alarm_th', alarm_show['name'])
self.assertEqual('meter_name', alarm_show['meter_name'])
self.assertEqual('10.0', alarm_show['threshold'])
# GET BY NAME AND ID ERROR
self.assertRaises(exceptions.CommandFailed,
self.aodh, u'alarm',
params=(u"show %s --alarm-name alarm_th" %
ALARM_ID))
# LIST # LIST
result = self.aodh('alarm', params="list") result = self.aodh('alarm', params="list")
self.assertIn(ALARM_ID, self.assertIn(ALARM_ID,
@@ -185,14 +217,14 @@ class AodhClientTest(base.ClientTestBase):
for alarm_list in self.parser.listing(result): for alarm_list in self.parser.listing(result):
self.assertEqual(sorted(output_colums), sorted(alarm_list.keys())) self.assertEqual(sorted(output_colums), sorted(alarm_list.keys()))
if alarm_list["alarm_id"] == ALARM_ID: if alarm_list["alarm_id"] == ALARM_ID:
self.assertEqual('alarm1', alarm_list['name']) self.assertEqual('alarm_th', alarm_list['name'])
# LIST WITH QUERY # LIST WITH QUERY
result = self.aodh('alarm', result = self.aodh('alarm',
params=("list --query project_id=%s" % PROJECT_ID)) params=("list --query project_id=%s" % PROJECT_ID))
alarm_list = self.parser.listing(result)[0] alarm_list = self.parser.listing(result)[0]
self.assertEqual(ALARM_ID, alarm_list["alarm_id"]) self.assertEqual(ALARM_ID, alarm_list["alarm_id"])
self.assertEqual('alarm1', alarm_list['name']) self.assertEqual('alarm_th', alarm_list['name'])
# DELETE # DELETE
result = self.aodh('alarm', params="delete %s" % ALARM_ID) result = self.aodh('alarm', params="delete %s" % ALARM_ID)
@@ -272,6 +304,20 @@ class AodhClientTest(base.ClientTestBase):
self.assertEqual(project_id, alarm_show["project_id"]) self.assertEqual(project_id, alarm_show["project_id"])
self.assertEqual('calarm1', alarm_show['name']) self.assertEqual('calarm1', alarm_show['name'])
# GET BY NAME
result = self.aodh(
'alarm', params="show --alarm-name calarm1")
alarm_show = self.details_multiple(result)[0]
self.assertEqual(alarm_id, alarm_show["alarm_id"])
self.assertEqual(project_id, alarm_show["project_id"])
self.assertEqual('calarm1', alarm_show['name'])
# GET BY NAME AND ID ERROR
self.assertRaises(exceptions.CommandFailed,
self.aodh, u'alarm',
params=(u"show %s --alarm-name calarm1" %
alarm_id))
# LIST # LIST
result = self.aodh('alarm', params="list") result = self.aodh('alarm', params="list")
self.assertIn(alarm_id, self.assertIn(alarm_id,
@@ -323,7 +369,7 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase):
result = self.aodh(u'alarm', result = self.aodh(u'alarm',
params=(u"create " params=(u"create "
"--type gnocchi_resources_threshold " "--type gnocchi_resources_threshold "
"--name alarm1 --metric cpu_util " "--name alarm_gn1 --metric cpu_util "
"--threshold 80 " "--threshold 80 "
"--resource-id %s --resource-type instance " "--resource-id %s --resource-type instance "
"--aggregation-method last " "--aggregation-method last "
@@ -331,7 +377,7 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase):
% (RESOURCE_ID, PROJECT_ID))) % (RESOURCE_ID, PROJECT_ID)))
alarm = self.details_multiple(result)[0] alarm = self.details_multiple(result)[0]
ALARM_ID = alarm['alarm_id'] ALARM_ID = alarm['alarm_id']
self.assertEqual('alarm1', alarm['name']) self.assertEqual('alarm_gn1', alarm['name'])
self.assertEqual('cpu_util', alarm['metric']) self.assertEqual('cpu_util', alarm['metric'])
self.assertEqual('80.0', alarm['threshold']) self.assertEqual('80.0', alarm['threshold'])
self.assertEqual('last', alarm['aggregation_method']) self.assertEqual('last', alarm['aggregation_method'])
@@ -343,7 +389,7 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase):
result = self.aodh(u'alarm', result = self.aodh(u'alarm',
params=(u"create " params=(u"create "
"--type gnocchi_resources_threshold " "--type gnocchi_resources_threshold "
"--name alarm1 --metric cpu_util " "--name alarm_gn1 --metric cpu_util "
"--threshold 80 " "--threshold 80 "
"--resource-id %s --resource-type instance " "--resource-id %s --resource-type instance "
"--aggregation-method last " "--aggregation-method last "
@@ -351,7 +397,8 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase):
% (RESOURCE_ID, PROJECT_ID)), % (RESOURCE_ID, PROJECT_ID)),
fail_ok=True, merge_stderr=True) fail_ok=True, merge_stderr=True)
self.assertFirstLineStartsWith( self.assertFirstLineStartsWith(
result.split('\n'), "Alarm with name='alarm1' exists (HTTP 409)") result.split('\n'),
"Alarm with name='alarm_gn1' exists (HTTP 409)")
# CREATE FAIL MISSING PARAM # CREATE FAIL MISSING PARAM
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
@@ -379,13 +426,31 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase):
alarm_show = self.details_multiple(result)[0] alarm_show = self.details_multiple(result)[0]
self.assertEqual(ALARM_ID, alarm_show["alarm_id"]) self.assertEqual(ALARM_ID, alarm_show["alarm_id"])
self.assertEqual(PROJECT_ID, alarm_show["project_id"]) self.assertEqual(PROJECT_ID, alarm_show["project_id"])
self.assertEqual('alarm1', alarm_show['name']) self.assertEqual('alarm_gn1', alarm_show['name'])
self.assertEqual('cpu_util', alarm_show['metric']) self.assertEqual('cpu_util', alarm_show['metric'])
self.assertEqual('90.0', alarm_show['threshold']) self.assertEqual('90.0', alarm_show['threshold'])
self.assertEqual('critical', alarm_show['severity']) self.assertEqual('critical', alarm_show['severity'])
self.assertEqual('last', alarm_show['aggregation_method']) self.assertEqual('last', alarm_show['aggregation_method'])
self.assertEqual('instance', alarm_show['resource_type']) self.assertEqual('instance', alarm_show['resource_type'])
# GET BY NAME
result = self.aodh(
'alarm', params="show --alarm-name alarm_gn1")
self.assertEqual(ALARM_ID, alarm_show["alarm_id"])
self.assertEqual(PROJECT_ID, alarm_show["project_id"])
self.assertEqual('alarm_gn1', alarm_show['name'])
self.assertEqual('cpu_util', alarm_show['metric'])
self.assertEqual('90.0', alarm_show['threshold'])
self.assertEqual('critical', alarm_show['severity'])
self.assertEqual('last', alarm_show['aggregation_method'])
self.assertEqual('instance', alarm_show['resource_type'])
# GET BY NAME AND ID ERROR
self.assertRaises(exceptions.CommandFailed,
self.aodh, u'alarm',
params=(u"show %s --alarm-name alarm_gn1" %
ALARM_ID))
# LIST # LIST
result = self.aodh('alarm', params="list") result = self.aodh('alarm', params="list")
self.assertIn(ALARM_ID, self.assertIn(ALARM_ID,
@@ -395,14 +460,14 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase):
for alarm_list in self.parser.listing(result): for alarm_list in self.parser.listing(result):
self.assertEqual(sorted(output_colums), sorted(alarm_list.keys())) self.assertEqual(sorted(output_colums), sorted(alarm_list.keys()))
if alarm_list["alarm_id"] == ALARM_ID: if alarm_list["alarm_id"] == ALARM_ID:
self.assertEqual('alarm1', alarm_list['name']) self.assertEqual('alarm_gn1', alarm_list['name'])
# LIST WITH QUERY # LIST WITH QUERY
result = self.aodh('alarm', result = self.aodh('alarm',
params=("list --query project_id=%s" % PROJECT_ID)) params=("list --query project_id=%s" % PROJECT_ID))
alarm_list = self.parser.listing(result)[0] alarm_list = self.parser.listing(result)[0]
self.assertEqual(ALARM_ID, alarm_list["alarm_id"]) self.assertEqual(ALARM_ID, alarm_list["alarm_id"])
self.assertEqual('alarm1', alarm_list['name']) self.assertEqual('alarm_gn1', alarm_list['name'])
# DELETE # DELETE
result = self.aodh('alarm', params="delete %s" % ALARM_ID) result = self.aodh('alarm', params="delete %s" % ALARM_ID)

View File

@@ -19,6 +19,8 @@ from cliff import show
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import strutils from oslo_utils import strutils
from aodhclient import exceptions
from aodhclient.i18n import _
from aodhclient import utils from aodhclient import utils
ALARM_TYPES = ['threshold', 'event', 'composite', ALARM_TYPES = ['threshold', 'event', 'composite',
@@ -65,16 +67,56 @@ def _format_alarm(alarm):
return alarm return alarm
def _find_alarm_by_name(client, name, return_id=False):
# then try to get entity as name
query = jsonutils.dumps({"=": {"name": name}})
alarms = client.list(query)
if len(alarms) > 1:
msg = (_("Multiple alarms matches found for '%s', "
"use an ID to be more specific.") % name)
raise exceptions.NoUniqueMatch(msg)
elif not alarms:
msg = (_("Alarm %s not found") % name)
raise exceptions.NotFound(404, msg)
else:
if return_id:
return alarms[0]['alarm_id']
return alarms[0]
def _check_name_and_id(parsed_args, action):
if parsed_args.id and parsed_args.alarm_name:
raise exceptions.CommandError(
"You should provide only one of "
"alarm ID and alarm name(--alarm-name) "
"to %s an alarm." % action)
if not parsed_args.id and not parsed_args.alarm_name:
msg = (_("You need to specify one of "
"alarm ID and alarm name(--alarm-name) "
"to %s an alarm.") % action)
raise exceptions.CommandError(msg)
class CliAlarmShow(show.ShowOne): class CliAlarmShow(show.ShowOne):
"""Show an alarm""" """Show an alarm"""
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(CliAlarmShow, self).get_parser(prog_name) parser = super(CliAlarmShow, self).get_parser(prog_name)
parser.add_argument("alarm_id", help="ID of an alarm") parser.add_argument("id", nargs='?',
metavar='<ALARM ID>',
help="ID of an alarm")
parser.add_argument("--alarm-name", dest='alarm_name',
metavar='<ALARM NAME>',
help="name of an alarm")
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
alarm = self.app.client.alarm.get(alarm_id=parsed_args.alarm_id) _check_name_and_id(parsed_args, 'query')
if parsed_args.id:
alarm = self.app.client.alarm.get(alarm_id=parsed_args.id)
else:
alarm = _find_alarm_by_name(self.app.client.alarm,
parsed_args.alarm_name)
return self.dict2columns(_format_alarm(alarm)) return self.dict2columns(_format_alarm(alarm))
@@ -323,13 +365,26 @@ class CliAlarmUpdate(CliAlarmCreate):
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(CliAlarmUpdate, self).get_parser(prog_name) parser = super(CliAlarmUpdate, self).get_parser(prog_name)
parser.add_argument("alarm_id", help="ID of the alarm") parser.add_argument("id", nargs='?',
metavar='<ALARM ID>',
help="ID of an alarm")
parser.add_argument("--alarm-name", dest='alarm_name',
metavar='<ALARM NAME>',
help="name of an alarm")
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
_check_name_and_id(parsed_args, 'update')
attributes = self._alarm_from_args(parsed_args) attributes = self._alarm_from_args(parsed_args)
updated_alarm = self.app.client.alarm.update( if parsed_args.id:
alarm_id=parsed_args.alarm_id, alarm_update=attributes) updated_alarm = self.app.client.alarm.update(
alarm_id=parsed_args.id, alarm_update=attributes)
else:
alarm_id = _find_alarm_by_name(self.app.client.alarm,
parsed_args.alarm_name,
return_id=True)
updated_alarm = self.app.client.alarm.update(
alarm_id=alarm_id, alarm_update=attributes)
return self.dict2columns(_format_alarm(updated_alarm)) return self.dict2columns(_format_alarm(updated_alarm))
@@ -338,8 +393,20 @@ class CliAlarmDelete(command.Command):
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(CliAlarmDelete, self).get_parser(prog_name) parser = super(CliAlarmDelete, self).get_parser(prog_name)
parser.add_argument("alarm_id", help="ID of the alarm") parser.add_argument("id", nargs='?',
metavar='<ALARM ID>',
help="ID of an alarm")
parser.add_argument("--alarm-name", dest='alarm_name',
metavar='<ALARM NAME>',
help="name of an alarm")
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
self.app.client.alarm.delete(parsed_args.alarm_id) _check_name_and_id(parsed_args, 'delete')
if parsed_args.id:
self.app.client.alarm.delete(parsed_args.id)
else:
alarm_id = _find_alarm_by_name(self.app.client.alarm,
parsed_args.alarm_name,
return_id=True)
self.app.client.alarm.delete(alarm_id)