# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse from cliff import command from cliff import lister from cliff import show from oslo_serialization import jsonutils from oslo_utils import strutils from oslo_utils import uuidutils from aodhclient import exceptions from aodhclient.i18n import _ from aodhclient import utils ALARM_TYPES = ['event', 'composite', 'gnocchi_resources_threshold', 'gnocchi_aggregation_by_metrics_threshold', 'gnocchi_aggregation_by_resources_threshold'] ALARM_STATES = ['ok', 'alarm', 'insufficient data'] ALARM_SEVERITY = ['low', 'moderate', 'critical'] ALARM_OPERATORS = ['lt', 'le', 'eq', 'ne', 'ge', 'gt'] ALARM_OP_MAP = dict(zip(ALARM_OPERATORS, ('<', '<=', '=', '!=', '>=', '>'))) ALARM_LIST_COLS = ['alarm_id', 'type', 'name', 'state', 'severity', 'enabled'] class CliAlarmList(lister.Lister): """List alarms""" @staticmethod def split_filter_param(param): key, eq_op, value = param.partition('=') if not eq_op: msg = 'Malformed parameter(%s). Use the key=value format.' % param raise ValueError(msg) return key, value def get_parser(self, prog_name): parser = super(CliAlarmList, self).get_parser(prog_name) exclusive_group = parser.add_mutually_exclusive_group() exclusive_group.add_argument("--query", help="Rich query supported by aodh, " "e.g. project_id!=my-id " "user_id=foo or user_id=bar") exclusive_group.add_argument('--filter', dest='filter', metavar='', type=self.split_filter_param, action='append', help='Filter parameters to apply on' ' returned alarms.') parser.add_argument("--limit", type=int, metavar="", help="Number of resources to return " "(Default is server default)") parser.add_argument("--marker", metavar="", help="Last item of the previous listing. " "Return the next results after this value," "the supported marker is alarm_id.") parser.add_argument("--sort", action="append", metavar="", help="Sort of resource attribute, " "e.g. name:asc") return parser def take_action(self, parsed_args): if parsed_args.query: if any([parsed_args.limit, parsed_args.sort, parsed_args.marker]): raise exceptions.CommandError( "Query and pagination options are mutually " "exclusive.") query = jsonutils.dumps( utils.search_query_builder(parsed_args.query)) alarms = utils.get_client(self).alarm.query(query=query) else: filters = dict(parsed_args.filter) if parsed_args.filter else None alarms = utils.get_client(self).alarm.list( filters=filters, sorts=parsed_args.sort, limit=parsed_args.limit, marker=parsed_args.marker) return utils.list2cols(ALARM_LIST_COLS, alarms) def _format_alarm(alarm): if alarm.get('composite_rule'): composite_rule = jsonutils.dumps(alarm['composite_rule'], indent=2) alarm['composite_rule'] = composite_rule return alarm for alarm_type in ALARM_TYPES: if alarm.get('%s_rule' % alarm_type): alarm.update(alarm.pop('%s_rule' % alarm_type)) if alarm["time_constraints"]: alarm["time_constraints"] = jsonutils.dumps(alarm["time_constraints"], sort_keys=True, indent=2) # only works for event alarm if isinstance(alarm.get('query'), list): query_rows = [] for q in alarm['query']: op = ALARM_OP_MAP.get(q['op'], q['op']) query_rows.append('%s %s %s' % (q['field'], op, q['value'])) alarm['query'] = ' AND\n'.join(query_rows) return alarm def _find_alarm_by_name(client, name): # then try to get entity as name query = jsonutils.dumps({"=": {"name": name}}) alarms = client.alarm.query(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(msg) else: return alarms[0] def _find_alarm_id_by_name(client, name): alarm = _find_alarm_by_name(client, name) return alarm['alarm_id'] def _check_name_and_id_coexist(parsed_args, action): if parsed_args.id and parsed_args.name: raise exceptions.CommandError( "You should provide only one of " "alarm ID and alarm name(--name) " "to %s an alarm." % action) def _check_name_and_id_exist(parsed_args, action): if not parsed_args.id and not parsed_args.name: msg = (_("You need to specify one of " "alarm ID and alarm name(--name) " "to %s an alarm.") % action) raise exceptions.CommandError(msg) def _check_name_and_id(parsed_args, action): _check_name_and_id_coexist(parsed_args, action) _check_name_and_id_exist(parsed_args, action) def _add_name_to_parser(parser, required=False): parser.add_argument('--name', metavar='', required=required, help='Name of the alarm') return parser def _add_id_to_parser(parser): parser.add_argument("id", nargs='?', metavar='', help="ID or name of an alarm.") return parser class CliAlarmShow(show.ShowOne): """Show an alarm""" def get_parser(self, prog_name): return _add_name_to_parser( _add_id_to_parser( super(CliAlarmShow, self).get_parser(prog_name))) def take_action(self, parsed_args): _check_name_and_id(parsed_args, 'query') c = utils.get_client(self) if parsed_args.name: alarm = _find_alarm_by_name(c, parsed_args.name) else: if uuidutils.is_uuid_like(parsed_args.id): try: alarm = c.alarm.get(alarm_id=parsed_args.id) except exceptions.NotFound: # Maybe it's a name alarm = _find_alarm_by_name(c, parsed_args.id) else: alarm = _find_alarm_by_name(c, parsed_args.id) return self.dict2columns(_format_alarm(alarm)) class CliAlarmCreate(show.ShowOne): """Create an alarm""" create = True def get_parser(self, prog_name): parser = _add_name_to_parser( super(CliAlarmCreate, self).get_parser(prog_name), required=self.create) parser.add_argument('-t', '--type', metavar='', required=self.create, choices=ALARM_TYPES, help='Type of alarm, should be one of: ' '%s.' % ', '.join(ALARM_TYPES)) parser.add_argument('--project-id', metavar='', help='Project to associate with alarm ' '(configurable by admin users only)') parser.add_argument('--user-id', metavar='', help='User to associate with alarm ' '(configurable by admin users only)') parser.add_argument('--description', metavar='', help='Free text description of the alarm') parser.add_argument('--state', metavar='', choices=ALARM_STATES, help='State of the alarm, one of: ' + str(ALARM_STATES)) parser.add_argument('--severity', metavar='', choices=ALARM_SEVERITY, help='Severity of the alarm, one of: ' + str(ALARM_SEVERITY)) parser.add_argument('--enabled', type=strutils.bool_from_string, metavar='{True|False}', help=('True if alarm evaluation is enabled')) parser.add_argument('--alarm-action', dest='alarm_actions', metavar='', action='append', help=('URL to invoke when state transitions to ' 'alarm. May be used multiple times')) parser.add_argument('--ok-action', dest='ok_actions', metavar='', action='append', help=('URL to invoke when state transitions to ' 'OK. May be used multiple times')) parser.add_argument('--insufficient-data-action', dest='insufficient_data_actions', metavar='', action='append', help=('URL to invoke when state transitions to ' 'insufficient data. May be used multiple ' 'times')) parser.add_argument( '--time-constraint', dest='time_constraints', metavar='