# (C) Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP # (C) Copyright 2017 SUSE LLC # # 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 binascii from collections import namedtuple from datetime import datetime from datetime import timedelta import itertools import urllib from cassandra.cluster import Cluster from cassandra.query import FETCH_SIZE_UNSET from cassandra.query import SimpleStatement from oslo_config import cfg from oslo_log import log from oslo_utils import timeutils from monasca_api.common.repositories import exceptions from monasca_api.common.repositories import metrics_repository from monasca_common.rest import utils as rest_utils CONF = cfg.CONF LOG = log.getLogger(__name__) LIMIT_CLAUSE = 'limit %s' ALLOW_FILTERING = 'allow filtering' MEASUREMENT_LIST_CQL = ('select time_stamp, value, value_meta ' 'from measurements where %s %s %s %s') METRIC_ID_EQ = 'metric_id = %s' METRIC_ID_IN = 'metric_id in %s' OFFSET_TIME_GT = "and time_stamp > %s" START_TIME_GE = "and time_stamp >= %s" END_TIME_LE = "and time_stamp <= %s" METRIC_LIST_CQL = ('select metric_name, dimensions, metric_id ' 'from metrics where %s %s %s %s %s %s %s %s %s %s') REGION_EQ = 'region = %s' TENANT_EQ = 'and tenant_id = %s' METRIC_NAME_EQ = 'and metric_name = %s' DIMENSIONS_CONTAINS = 'and dimensions contains %s ' DIMENSIONS_NAME_CONTAINS = 'and dimension_names contains %s ' CREATED_TIME_LE = "and created_at <= %s" UPDATED_TIME_GE = "and updated_at >= %s" DIMENSIONS_GT = 'and dimensions > %s' DIMENSION_VALUE_BY_METRIC_CQL = ('select dimension_value as value from metrics_dimensions ' 'where region = ? and tenant_id = ? and metric_name = ? ' 'and dimension_name = ? group by dimension_value') DIMENSION_VALUE_CQL = ('select value from dimensions ' 'where region = ? and tenant_id = ? and name = ? ' 'group by value order by value') DIMENSION_NAME_BY_METRIC_CQL = ('select dimension_name as name from metrics_dimensions where ' 'region = ? and tenant_id = ? and metric_name = ? ' 'group by dimension_name order by dimension_name') DIMENSION_NAME_CQL = ('select name from dimensions where region = ? and tenant_id = ? ' 'group by name allow filtering') METRIC_NAME_BY_DIMENSION_CQL = ('select metric_name from dimensions_metrics where region = ? and ' 'tenant_id = ? and dimension_name = ? and dimension_value = ? ' 'group by metric_name order by metric_name') METRIC_NAME_BY_DIMENSION_OFFSET_CQL = ('select metric_name from dimensions_metrics where region = ? and ' 'tenant_id = ? and dimension_name = ? and dimension_value = ? and ' 'metric_name >= ?' 'group by metric_name order by metric_name') METRIC_NAME_CQL = ('select distinct region, tenant_id, metric_name from metrics_dimensions ' 'where region = ? and tenant_id = ? allow filtering') METRIC_NAME_OFFSET_CQL = ('select distinct region, tenant_id, metric_name from metrics_dimensions ' 'where region = ? and tenant_id = ? and metric_name >= ? allow filtering') METRIC_BY_ID_CQL = ('select region, tenant_id, metric_name, dimensions from measurements ' 'where metric_id = ? limit 1') Metric = namedtuple('metric', 'id name dimensions') ALARM_HISTORY_CQL = ('select tenant_id, alarm_id, time_stamp, metric, new_state, old_state, reason, reason_data, ' 'sub_alarms from alarm_state_history where %s %s %s %s %s') ALARM_ID_EQ = 'and alarm_id = %s' ALARM_ID_IN = 'and alarm_id in %s' ALARM_TENANT_ID_EQ = 'tenant_id = %s' class MetricsRepository(metrics_repository.AbstractMetricsRepository): def __init__(self): try: self.conf = cfg.CONF self.cluster = Cluster(self.conf.cassandra.contact_points) self.session = self.cluster.connect(self.conf.cassandra.keyspace) self.dim_val_by_metric_stmt = self.session.prepare(DIMENSION_VALUE_BY_METRIC_CQL) self.dim_val_stmt = self.session.prepare(DIMENSION_VALUE_CQL) self.dim_name_by_metric_stmt = self.session.prepare(DIMENSION_NAME_BY_METRIC_CQL) self.dim_name_stmt = self.session.prepare(DIMENSION_NAME_CQL) self.metric_name_by_dimension_stmt = self.session.prepare(METRIC_NAME_BY_DIMENSION_CQL) self.metric_name_by_dimension_offset_stmt = self.session.prepare(METRIC_NAME_BY_DIMENSION_OFFSET_CQL) self.metric_name_stmt = self.session.prepare(METRIC_NAME_CQL) self.metric_name_offset_stmt = self.session.prepare(METRIC_NAME_OFFSET_CQL) self.metric_by_id_stmt = self.session.prepare(METRIC_BY_ID_CQL) except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) self.epoch = datetime.utcfromtimestamp(0) def list_dimension_values(self, tenant_id, region, metric_name, dimension_name): try: if metric_name: rows = self.session.execute( self.dim_val_by_metric_stmt, [region, tenant_id, metric_name, dimension_name]) else: rows = self.session.execute( self.dim_val_stmt, [region, tenant_id, dimension_name]) except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) json_dim_value_list = [] if not rows: return json_dim_value_list for row in rows: json_dim_value_list.append({u'dimension_value': row.value}) json_dim_value_list.sort(key=lambda x: x[u'dimension_value']) return json_dim_value_list def list_dimension_names(self, tenant_id, region, metric_name): try: if metric_name: rows = self.session.execute( self.dim_name_by_metric_stmt, [region, tenant_id, metric_name]) ordered = True else: rows = self.session.execute( self.dim_name_stmt, [region, tenant_id]) ordered = False except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) if not rows: return [] json_dim_name_list = [{u'dimension_name': row.name} for row in rows] if not ordered: json_dim_name_list.sort(key=lambda x: x[u'dimension_name']) return json_dim_name_list def list_metrics(self, tenant_id, region, name, dimensions, offset, limit, start_time=None, end_time=None): offset_name = None offset_dimensions = [] names = [] metric_list = [] offset_futures = [] non_offset_futures = [] try: if offset: offset_metric = self._get_metric_by_id(offset) if offset_metric: offset_name = offset_metric.name offset_dimensions = offset_metric.dimensions if not name: names = self._list_metric_names(tenant_id, region, dimensions, offset=offset_name) if names: names = [elem['name'] for elem in names] else: names.append(name) if not names: return metric_list for name in names: if name == offset_name: futures = self._list_metrics_by_name(tenant_id, region, name, dimensions, offset_dimensions, limit, start_time=None, end_time=None) if offset_dimensions and dimensions: offset_futures.extend(futures) else: non_offset_futures.extend(futures) else: non_offset_futures.extend( self._list_metrics_by_name(tenant_id, region, name, dimensions, None, limit, start_time=None, end_time=None)) # manually filter out metrics by the offset dimension for future in offset_futures: rows = future.result() for row in rows: if offset_dimensions >= row.dimensions: continue metric_list.append(self._process_metric_row(row)) for future in non_offset_futures: metric_list.extend((self._process_metric_row(row) for row in future.result())) return metric_list except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) @staticmethod def _process_metric_row(row): dim_map = {} for d in row.dimensions: pair = d.split('\t') dim_map[pair[0]] = pair[1] metric = {'id': binascii.hexlify(bytearray(row.metric_id)), 'name': row.metric_name, 'dimensions': dim_map} return metric def _list_metrics_by_name(self, tenant_id, region, name, dimensions, dimension_offset, limit, start_time=None, end_time=None): or_dimensions = [] sub_dimensions = {} futures = [] if not dimensions: query = self._build_metrics_by_name_query(tenant_id, region, name, dimensions, None, start_time, end_time, dimension_offset, limit) futures.append(self.session.execute_async(query[0], query[1])) return futures wildcard_dimensions = [] for dim_name, dim_value in dimensions.items(): if not dim_value: wildcard_dimensions.append(dim_name) elif '|' in dim_value: def f(val): return {dim_name: val} or_dimensions.append(list(map(f, sorted(dim_value.split('|'))))) else: sub_dimensions[dim_name] = dim_value if or_dimensions: or_dims_list = list(itertools.product(*or_dimensions)) for or_dims_tuple in or_dims_list: extracted_dimensions = sub_dimensions.copy() for dims in iter(or_dims_tuple): for k, v in dims.items(): extracted_dimensions[k] = v query = self._build_metrics_by_name_query(tenant_id, region, name, extracted_dimensions, wildcard_dimensions, start_time, end_time, dimension_offset, limit) futures.append(self.session.execute_async(query[0], query[1])) else: query = self._build_metrics_by_name_query(tenant_id, region, name, sub_dimensions, wildcard_dimensions, start_time, end_time, dimension_offset, limit) futures.append(self.session.execute_async(query[0], query[1])) return futures def _get_metric_by_id(self, metric_id): rows = self.session.execute(self.metric_by_id_stmt, [bytearray.fromhex(metric_id)]) if rows: return Metric(id=metric_id, name=rows[0].metric_name, dimensions=rows[0].dimensions) return None def _build_metrics_by_name_query(self, tenant_id, region, name, dimensions, wildcard_dimensions, start_time, end_time, dim_offset, limit): conditions = [REGION_EQ, TENANT_EQ] params = [region, tenant_id.encode('utf8')] if name: conditions.append(METRIC_NAME_EQ) params.append(name) else: conditions.append('') if dimensions: conditions.append(DIMENSIONS_CONTAINS * len(dimensions)) params.extend( [self._create_dimension_value_entry(dim_name, dim_value) for dim_name, dim_value in dimensions.items()]) else: conditions.append('') if wildcard_dimensions: conditions.append(DIMENSIONS_NAME_CONTAINS * len(wildcard_dimensions)) params.extend(wildcard_dimensions) else: conditions.append('') if dim_offset and not dimensions: # cassandra does not allow using both contains and GT in collection column conditions.append(DIMENSIONS_GT) params.append(dim_offset) else: conditions.append('') if start_time: conditions.append(UPDATED_TIME_GE % start_time) else: conditions.append('') if end_time: conditions.append(CREATED_TIME_LE % end_time) else: conditions.append('') if limit: conditions.append(LIMIT_CLAUSE) params.append(limit) else: conditions.append('') if (not name) or dimensions or wildcard_dimensions or start_time or end_time: conditions.append(ALLOW_FILTERING) else: conditions.append('') return METRIC_LIST_CQL % tuple(conditions), params @staticmethod def _create_dimension_value_entry(name, value): return '%s\t%s' % (name, value) def list_metric_names(self, tenant_id, region, dimensions): return self._list_metric_names(tenant_id, region, dimensions) def _list_metric_names(self, tenant_id, region, dimensions, offset=None): or_dimensions = [] single_dimensions = {} if dimensions: for key, value in dimensions.items(): if not value: continue elif '|' in value: def f(val): return {key: val} or_dimensions.append(list(map(f, sorted(value.split('|'))))) else: single_dimensions[key] = value if or_dimensions: names = [] or_dims_list = list(itertools.product(*or_dimensions)) for or_dims_tuple in or_dims_list: extracted_dimensions = single_dimensions.copy() for dims in iter(or_dims_tuple): for k, v in dims.items(): extracted_dimensions[k] = v names.extend( self._list_metric_names_single_dimension_value(tenant_id, region, extracted_dimensions, offset)) names.sort(key=lambda x: x[u'name']) return names else: names = self._list_metric_names_single_dimension_value(tenant_id, region, single_dimensions, offset) names.sort(key=lambda x: x[u'name']) return names def _list_metric_names_single_dimension_value(self, tenant_id, region, dimensions, offset=None): try: futures = [] if dimensions: for name, value in dimensions.items(): if offset: futures.append(self.session.execute_async(self.metric_name_by_dimension_offset_stmt, [region, tenant_id, name, value, offset])) else: futures.append(self.session.execute_async(self.metric_name_by_dimension_stmt, [region, tenant_id, name, value])) else: if offset: futures.append( self.session.execute_async(self.metric_name_offset_stmt, [region, tenant_id, offset])) else: futures.append(self.session.execute_async(self.metric_name_stmt, [region, tenant_id])) names_list = [] for future in futures: rows = future.result() tmp = set() for row in rows: tmp.add(row.metric_name) names_list.append(tmp) return [{u'name': v} for v in set.intersection(*names_list)] except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) def measurement_list(self, tenant_id, region, name, dimensions, start_timestamp, end_timestamp, offset, limit, merge_metrics_flag, group_by): metrics = self.list_metrics(tenant_id, region, name, dimensions, None, None) if offset: tmp = offset.split("_") if len(tmp) > 1: offset_id = tmp[0] offset_timestamp = tmp[1] else: offset_id = None offset_timestamp = offset else: offset_timestamp = None offset_id = None if not metrics: return None elif len(metrics) > 1: if not merge_metrics_flag and not group_by: raise exceptions.MultipleMetricsException(self.MULTIPLE_METRICS_MESSAGE) try: if len(metrics) > 1 and not group_by: # offset is controlled only by offset_timestamp when the group by option is not enabled count, series_list = self._query_merge_measurements(metrics, dimensions, start_timestamp, end_timestamp, offset_timestamp, limit) return series_list if group_by: if not isinstance(group_by, list): group_by = group_by.split(',') elif len(group_by) == 1: group_by = group_by[0].split(',') if len(metrics) == 1 or group_by[0].startswith('*'): if offset_id: for index, metric in enumerate(metrics): if metric['id'] == offset_id: if index > 0: metrics[0:index] = [] break count, series_list = self._query_measurements(metrics, start_timestamp, end_timestamp, offset_timestamp, limit) return series_list grouped_metrics = self._group_metrics(metrics, group_by, dimensions) if not grouped_metrics or len(grouped_metrics) == 0: return None if offset_id: found_offset = False for outer_index, sublist in enumerate(grouped_metrics): for inner_index, metric in enumerate(sublist): if metric['id'] == offset_id: found_offset = True if inner_index > 0: sublist[0:inner_index] = [] break if found_offset: if outer_index > 0: grouped_metrics[0:outer_index] = [] break remaining = limit series_list = [] for sublist in grouped_metrics: sub_count, results = self._query_merge_measurements(sublist, sublist[0]['dimensions'], start_timestamp, end_timestamp, offset_timestamp, remaining) series_list.extend(results) if remaining: remaining -= sub_count if remaining <= 0: break # offset_timestamp is used only in the first group, reset to None for subsequent groups if offset_timestamp: offset_timestamp = None return series_list except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) def _query_merge_measurements(self, metrics, dimensions, start_timestamp, end_timestamp, offset_timestamp, limit): results = [] for metric in metrics: if limit and len(metrics) > 1: fetch_size = min(limit, max(1000, limit / len(metrics) + 2)) else: fetch_size = None query = self._build_measurement_query(metric['id'], start_timestamp, end_timestamp, offset_timestamp, limit, fetch_size) results.append((metric, iter(self.session.execute_async(query[0], query[1]).result()))) return self._merge_series(results, dimensions, limit) def _query_measurements(self, metrics, start_timestamp, end_timestamp, offset_timestamp, limit): results = [] for index, metric in enumerate(metrics): if index == 0: query = self._build_measurement_query(metric['id'], start_timestamp, end_timestamp, offset_timestamp, limit) else: if limit: fetch_size = min(self.session.default_fetch_size, max(1000, limit / min(index, 4))) else: fetch_size = self.session.default_fetch_size query = self._build_measurement_query(metric['id'], start_timestamp, end_timestamp, None, limit, fetch_size) results.append([metric, iter(self.session.execute_async(query[0], query[1]).result())]) series_list = [] count = 0 for result in results: measurements = [] row = next(result[1], None) while row: measurements.append([self._isotime_msec(row.time_stamp), row.value, rest_utils.from_json(row.value_meta) if row.value_meta else {}]) count += 1 if limit and count >= limit: break row = next(result[1], None) series_list.append({'name': result[0]['name'], 'id': result[0]['id'], 'columns': ['timestamp', 'value', 'value_meta'], 'measurements': measurements, 'dimensions': result[0]['dimensions']}) if limit and count >= limit: break return count, series_list @staticmethod def _build_measurement_query(metric_id, start_timestamp, end_timestamp, offset_timestamp, limit=None, fetch_size=FETCH_SIZE_UNSET): conditions = [METRIC_ID_EQ] params = [bytearray.fromhex(metric_id)] if offset_timestamp: conditions.append(OFFSET_TIME_GT) params.append(offset_timestamp) elif start_timestamp: conditions.append(START_TIME_GE) params.append(int(start_timestamp * 1000)) else: conditions.append('') if end_timestamp: conditions.append(END_TIME_LE) params.append(int(end_timestamp * 1000)) else: conditions.append('') if limit: conditions.append(LIMIT_CLAUSE) params.append(limit) else: conditions.append('') return SimpleStatement(MEASUREMENT_LIST_CQL % tuple(conditions), fetch_size=fetch_size), params def _merge_series(self, series, dimensions, limit): series_list = [] if not series: return series_list measurements = [] top_batch = [] num_series = len(series) for i in range(0, num_series): row = next(series[i][1], None) if row: top_batch.append([i, row.time_stamp, row.value, rest_utils.from_json(row.value_meta) if row.value_meta else {}]) else: num_series -= 1 top_batch.sort(key=lambda m: m[1], reverse=True) count = 0 while (not limit or count < limit) and top_batch: measurements.append([self._isotime_msec(top_batch[num_series - 1][1]), top_batch[num_series - 1][2], top_batch[num_series - 1][3]]) count += 1 row = next(series[top_batch[num_series - 1][0]][1], None) if row: top_batch[num_series - 1] = [top_batch[num_series - 1][0], row.time_stamp, row.value, rest_utils.from_json(row.value_meta) if row.value_meta else {}] top_batch.sort(key=lambda m: m[1], reverse=True) else: num_series -= 1 top_batch.pop() series_list.append({'name': series[0][0]['name'], 'id': series[0][0]['id'], 'columns': ['timestamp', 'value', 'value_meta'], 'measurements': measurements, 'dimensions': dimensions}) return count, series_list @staticmethod def _group_metrics(metrics, group_by, search_by): grouped_metrics = {} for metric in metrics: key = '' display_dimensions = dict(search_by.items()) for name in group_by: # '_' ensures te key with missing dimension is sorted lower value = metric['dimensions'].get(name, '_') if value != '_': display_dimensions[name] = value key = key + '='.join((urllib.quote_plus(name), urllib.quote_plus(value))) + '&' metric['dimensions'] = display_dimensions if key in grouped_metrics: grouped_metrics[key].append(metric) else: grouped_metrics[key] = [metric] grouped_metrics = grouped_metrics.items() grouped_metrics.sort(key=lambda k: k[0]) return [x[1] for x in grouped_metrics] @staticmethod def _isotime_msec(timestamp): """Stringify datetime in ISO 8601 format + millisecond. """ st = timestamp.isoformat() if '.' in st: st = st[:23] + 'Z' else: st += '.000Z' return st.decode('utf8') def metrics_statistics(self, tenant_id, region, name, dimensions, start_timestamp, end_timestamp, statistics, period, offset, limit, merge_metrics_flag, group_by): if not period: period = 300 else: period = int(period) series_list = self.measurement_list(tenant_id, region, name, dimensions, start_timestamp, end_timestamp, offset, None, merge_metrics_flag, group_by) json_statistics_list = [] if not series_list: return json_statistics_list statistics = [stat.lower() for stat in statistics] columns = [u'timestamp'] columns.extend([x for x in ['avg', 'min', 'max', 'count', 'sum'] if x in statistics]) start_time = datetime.utcfromtimestamp(start_timestamp) if end_timestamp: end_time = datetime.utcfromtimestamp(end_timestamp) else: end_time = datetime.utcnow() for series in series_list: if limit <= 0: break measurements = series['measurements'] if not measurements: continue first_measure = measurements[0] first_measure_start_time = MetricsRepository._parse_time_string(first_measure[0]) # skip blank intervals at the beginning, finds the start time of stat period that is not empty stat_start_time = start_time + timedelta( seconds=((first_measure_start_time - start_time).seconds / period) * period) stats_list = [] stats_count = 0 stats_sum = 0 stats_min = stats_max = first_measure[1] for measurement in series['measurements']: time_stamp = MetricsRepository._parse_time_string(measurement[0]) value = measurement[1] if (time_stamp - stat_start_time).seconds >= period: stat = MetricsRepository._create_stat(statistics, stat_start_time, stats_count, stats_sum, stats_min, stats_max) stats_list.append(stat) limit -= 1 if limit <= 0: break # initialize the new stat period stats_sum = value stats_count = 1 stats_min = value stats_max = value stat_start_time += timedelta(seconds=period) else: stats_min = min(stats_min, value) stats_max = max(stats_max, value) stats_count += 1 stats_sum += value if stats_count: stat = MetricsRepository._create_stat(statistics, stat_start_time, stats_count, stats_sum, stats_min, stats_max) stats_list.append(stat) limit -= 1 stats_end_time = stat_start_time + timedelta(seconds=period) - timedelta(milliseconds=1) if stats_end_time > end_time: stats_end_time = end_time statistic = {u'name': name.decode('utf8'), u'id': series['id'], u'dimensions': series['dimensions'], u'columns': columns, u'statistics': stats_list, u'end_time': self._isotime_msec(stats_end_time)} json_statistics_list.append(statistic) return json_statistics_list @staticmethod def _create_stat(statistics, timestamp, stat_count=None, stat_sum=None, stat_min=None, stat_max=None): stat = [MetricsRepository._isotime_msec(timestamp)] if not stat_count: stat.extend([0] * len(statistics)) else: if 'avg' in statistics: stat.append(stat_sum / stat_count) if 'min' in statistics: stat.append(stat_min) if 'max' in statistics: stat.append(stat_max) if 'count' in statistics: stat.append(stat_count) if 'sum' in statistics: stat.append(stat_sum) return stat @staticmethod def _parse_time_string(timestamp): dt = timeutils.parse_isotime(timestamp) dt = timeutils.normalize_time(dt) return dt def alarm_history(self, tenant_id, alarm_id_list, offset, limit, start_timestamp=None, end_timestamp=None): try: json_alarm_history_list = [] if not alarm_id_list: return json_alarm_history_list conditions = [ALARM_TENANT_ID_EQ] params = [tenant_id.encode('utf8')] if len(alarm_id_list) == 1: conditions.append(ALARM_ID_EQ) params.append(alarm_id_list[0]) else: conditions.append(' and alarm_id in ({}) '.format(','.join(['%s'] * len(alarm_id_list)))) for alarm_id in alarm_id_list: params.append(alarm_id) if offset: conditions.append(OFFSET_TIME_GT) params.append(offset) elif start_timestamp: conditions.append(START_TIME_GE) params.append(int(start_timestamp * 1000)) else: conditions.append('') if end_timestamp: conditions.append(END_TIME_LE) params.append(int(end_timestamp * 1000)) else: conditions.append('') if limit: conditions.append(LIMIT_CLAUSE) params.append(limit + 1) else: conditions.append('') rows = self.session.execute(ALARM_HISTORY_CQL % tuple(conditions), params) if not rows: return json_alarm_history_list sorted_rows = sorted(rows, key=lambda row: row.time_stamp) for (tenant_id, alarm_id, time_stamp, metrics, new_state, old_state, reason, reason_data, sub_alarms) in sorted_rows: alarm = {u'timestamp': self._isotime_msec(time_stamp), u'alarm_id': alarm_id, u'metrics': rest_utils.from_json(metrics), u'new_state': new_state, u'old_state': old_state, u'reason': reason, u'reason_data': reason_data, u'sub_alarms': rest_utils.from_json(sub_alarms), u'id': str(int((time_stamp - self.epoch).total_seconds() * 1000))} if alarm[u'sub_alarms']: for sub_alarm in alarm[u'sub_alarms']: sub_expr = sub_alarm['sub_alarm_expression'] metric_def = sub_expr['metric_definition'] sub_expr['metric_name'] = metric_def['name'] sub_expr['dimensions'] = metric_def['dimensions'] del sub_expr['metric_definition'] json_alarm_history_list.append(alarm) return json_alarm_history_list except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) @staticmethod def check_status(): try: cluster = Cluster( CONF.cassandra.contact_points ) session = cluster.connect(CONF.cassandra.keyspace) session.shutdown() except Exception as ex: LOG.exception(str(ex)) return False, str(ex) return True, 'OK'