diff --git a/aodhclient/exceptions.py b/aodhclient/exceptions.py index a0999a0..01645d2 100644 --- a/aodhclient/exceptions.py +++ b/aodhclient/exceptions.py @@ -47,6 +47,10 @@ class MutipleMeaningException(object): """An mixin for exception that can be enhanced by reading the details""" +class CommandError(Exception): + pass + + class BadRequest(ClientException): """HTTP 400 - Bad request: you sent some malformed data.""" http_status = 400 @@ -110,6 +114,10 @@ class RateLimit(RetryAfterException): message = "Rate limit" +class NoUniqueMatch(Exception): + pass + + class NotImplemented(ClientException): """HTTP 501 - Not Implemented: diff --git a/aodhclient/tests/functional/test_alarm.py b/aodhclient/tests/functional/test_alarm.py index 55bfa6c..311ec38 100644 --- a/aodhclient/tests/functional/test_alarm.py +++ b/aodhclient/tests/functional/test_alarm.py @@ -73,6 +73,21 @@ class AodhClientTest(base.ClientTestBase): self.assertEqual('ev_alarm1', alarm_show['name']) 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 result = self.aodh('alarm', params="list") self.assertIn(ALARM_ID, @@ -117,12 +132,12 @@ class AodhClientTest(base.ClientTestBase): # CREATE 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 " "--project-id %s" % PROJECT_ID)) alarm = self.details_multiple(result)[0] 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('5.0', alarm['threshold']) @@ -144,17 +159,18 @@ class AodhClientTest(base.ClientTestBase): # CREATE FAIL 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 " "--project-id %s" % PROJECT_ID), fail_ok=True, merge_stderr=True) 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 self.assertRaises(exceptions.CommandFailed, self.aodh, u'alarm', - params=(u"create --type threshold --name alarm1 " + params=(u"create --type threshold --name alarm_th " "--project-id %s" % PROJECT_ID)) # UPDATE @@ -172,10 +188,26 @@ class AodhClientTest(base.ClientTestBase): 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('alarm1', alarm_show['name']) + 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 + 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 result = self.aodh('alarm', params="list") self.assertIn(ALARM_ID, @@ -185,14 +217,14 @@ class AodhClientTest(base.ClientTestBase): for alarm_list in self.parser.listing(result): self.assertEqual(sorted(output_colums), sorted(alarm_list.keys())) if alarm_list["alarm_id"] == ALARM_ID: - self.assertEqual('alarm1', alarm_list['name']) + self.assertEqual('alarm_th', alarm_list['name']) # LIST WITH QUERY result = self.aodh('alarm', params=("list --query project_id=%s" % PROJECT_ID)) alarm_list = self.parser.listing(result)[0] self.assertEqual(ALARM_ID, alarm_list["alarm_id"]) - self.assertEqual('alarm1', alarm_list['name']) + self.assertEqual('alarm_th', alarm_list['name']) # DELETE 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('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 result = self.aodh('alarm', params="list") self.assertIn(alarm_id, @@ -323,7 +369,7 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase): result = self.aodh(u'alarm', params=(u"create " "--type gnocchi_resources_threshold " - "--name alarm1 --metric cpu_util " + "--name alarm_gn1 --metric cpu_util " "--threshold 80 " "--resource-id %s --resource-type instance " "--aggregation-method last " @@ -331,7 +377,7 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase): % (RESOURCE_ID, PROJECT_ID))) alarm = self.details_multiple(result)[0] ALARM_ID = alarm['alarm_id'] - self.assertEqual('alarm1', alarm['name']) + self.assertEqual('alarm_gn1', alarm['name']) self.assertEqual('cpu_util', alarm['metric']) self.assertEqual('80.0', alarm['threshold']) self.assertEqual('last', alarm['aggregation_method']) @@ -343,7 +389,7 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase): result = self.aodh(u'alarm', params=(u"create " "--type gnocchi_resources_threshold " - "--name alarm1 --metric cpu_util " + "--name alarm_gn1 --metric cpu_util " "--threshold 80 " "--resource-id %s --resource-type instance " "--aggregation-method last " @@ -351,7 +397,8 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase): % (RESOURCE_ID, PROJECT_ID)), fail_ok=True, merge_stderr=True) 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 self.assertRaises(exceptions.CommandFailed, @@ -379,13 +426,31 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase): 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('alarm1', alarm_show['name']) + 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 + 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 result = self.aodh('alarm', params="list") self.assertIn(ALARM_ID, @@ -395,14 +460,14 @@ class AodhClientGnocchiRulesTest(base.ClientTestBase): for alarm_list in self.parser.listing(result): self.assertEqual(sorted(output_colums), sorted(alarm_list.keys())) if alarm_list["alarm_id"] == ALARM_ID: - self.assertEqual('alarm1', alarm_list['name']) + self.assertEqual('alarm_gn1', alarm_list['name']) # LIST WITH QUERY result = self.aodh('alarm', params=("list --query project_id=%s" % PROJECT_ID)) alarm_list = self.parser.listing(result)[0] self.assertEqual(ALARM_ID, alarm_list["alarm_id"]) - self.assertEqual('alarm1', alarm_list['name']) + self.assertEqual('alarm_gn1', alarm_list['name']) # DELETE result = self.aodh('alarm', params="delete %s" % ALARM_ID) diff --git a/aodhclient/v2/alarm_cli.py b/aodhclient/v2/alarm_cli.py index 4f7c9e9..6c0b209 100644 --- a/aodhclient/v2/alarm_cli.py +++ b/aodhclient/v2/alarm_cli.py @@ -19,6 +19,8 @@ from cliff import show from oslo_serialization import jsonutils from oslo_utils import strutils +from aodhclient import exceptions +from aodhclient.i18n import _ from aodhclient import utils ALARM_TYPES = ['threshold', 'event', 'composite', @@ -65,16 +67,56 @@ def _format_alarm(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): """Show an alarm""" def get_parser(self, 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='', + help="ID of an alarm") + parser.add_argument("--alarm-name", dest='alarm_name', + metavar='', + help="name of an alarm") return parser 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)) @@ -323,13 +365,26 @@ class CliAlarmUpdate(CliAlarmCreate): def get_parser(self, 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='', + help="ID of an alarm") + parser.add_argument("--alarm-name", dest='alarm_name', + metavar='', + help="name of an alarm") return parser def take_action(self, parsed_args): + _check_name_and_id(parsed_args, 'update') attributes = self._alarm_from_args(parsed_args) - updated_alarm = self.app.client.alarm.update( - alarm_id=parsed_args.alarm_id, alarm_update=attributes) + if parsed_args.id: + 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)) @@ -338,8 +393,20 @@ class CliAlarmDelete(command.Command): def get_parser(self, 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='', + help="ID of an alarm") + parser.add_argument("--alarm-name", dest='alarm_name', + metavar='', + help="name of an alarm") return parser 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)