f98f325fc2
- The UI will call the monasca-api more than once to get all metrics when the limit of API is exceeded. - Added compatibility to cassandra and influxdb Change-Id: I15c53c05517a6f8eb0fc29f3adc3667485ee6772 Story: 2004430 Task: 28091
274 lines
11 KiB
Python
274 lines
11 KiB
Python
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
|
# Copyright 2017 FUJITSU LIMITED
|
|
#
|
|
# 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.
|
|
|
|
from itertools import chain
|
|
import json
|
|
|
|
from django.template.loader import get_template
|
|
from django.utils.translation import ugettext as _ # noqa
|
|
|
|
from horizon import exceptions
|
|
from horizon import forms
|
|
from horizon import messages
|
|
|
|
from monitoring.alarmdefs import constants
|
|
from monitoring import api
|
|
|
|
|
|
def _get_metrics_call(request, offset=None):
|
|
return api.monitor.metrics_list(request, offset=offset)\
|
|
if offset else api.monitor.metrics_list(request)
|
|
|
|
|
|
def _get_metrics(request):
|
|
metrics_aggregation = _get_metrics_call(request)
|
|
if not metrics_aggregation:
|
|
return []
|
|
# offset defined as the id of last metric.
|
|
offset = metrics_aggregation[-1]['id']
|
|
while True:
|
|
metrics_batch = _get_metrics_call(request, offset)
|
|
if not metrics_batch:
|
|
break
|
|
metrics_aggregation += metrics_batch
|
|
offset = metrics_batch[-1]['id']
|
|
|
|
return json.dumps(metrics_aggregation)
|
|
|
|
|
|
def _get_notifications(request):
|
|
notifications = api.monitor.notification_list(request)
|
|
return [(notification['id'], notification) for notification in notifications]
|
|
|
|
|
|
class ExpressionWidget(forms.Widget):
|
|
|
|
func = json.dumps(
|
|
[('min', _('min')), ('max', _('max')), ('sum', _('sum')),
|
|
('count', _('count')), ('avg', _('avg')), ('last', _('last'))])
|
|
comparators = [['>', '>'], ['>=', '>='], ['<', '<'], ['<=', '<=']]
|
|
operators = json.dumps([('AND', _('AND')), ('OR', _('OR'))])
|
|
|
|
def __init__(self, initial, attrs=None):
|
|
super(ExpressionWidget, self).__init__(attrs)
|
|
self.initial = initial
|
|
|
|
def render(self, name, value, attrs=None):
|
|
final_attrs = self.build_attrs(attrs, {'name': name})
|
|
t = get_template(constants.TEMPLATE_PREFIX + 'expression_field.html')
|
|
|
|
local_attrs = {
|
|
'func': ExpressionWidget.func,
|
|
'comparators': ExpressionWidget.comparators,
|
|
'operators': ExpressionWidget.operators,
|
|
'metrics': self.metrics
|
|
}
|
|
|
|
local_attrs.update(final_attrs)
|
|
return t.render(local_attrs)
|
|
|
|
|
|
class ExpressionField(forms.CharField):
|
|
|
|
def _get_metrics(self):
|
|
return self._metrics
|
|
|
|
def _set_metrics(self, value):
|
|
self._metrics = self.widget.metrics = value
|
|
|
|
metrics = property(_get_metrics, _set_metrics)
|
|
|
|
|
|
class MatchByWidget(forms.Widget):
|
|
def __init__(self, initial, attrs=None):
|
|
super(MatchByWidget, self).__init__(attrs)
|
|
self.initial = initial
|
|
|
|
def render(self, name, value, attrs=None):
|
|
final_attrs = self.build_attrs(attrs, {'name': name})
|
|
t = get_template(constants.TEMPLATE_PREFIX + 'match_by_field.html')
|
|
|
|
local_attrs = {'service': ''}
|
|
local_attrs.update(final_attrs)
|
|
return t.render(local_attrs)
|
|
|
|
|
|
class NotificationField(forms.MultiValueField):
|
|
def __init__(self, *args, **kwargs):
|
|
super(NotificationField, self).__init__(fields=(), *args, **kwargs)
|
|
|
|
def _get_choices(self):
|
|
return self._choices
|
|
|
|
def _set_choices(self, value):
|
|
# Setting choices also sets the choices on the widget.
|
|
# choices can be any iterable, but we call list() on it because
|
|
# it will be consumed more than once.
|
|
self._choices = self.widget.choices = list(value)
|
|
|
|
choices = property(_get_choices, _set_choices)
|
|
|
|
def compress(self, data_list):
|
|
return data_list
|
|
|
|
def clean(self, value):
|
|
return value
|
|
|
|
|
|
class NotificationCreateWidget(forms.Select):
|
|
def __init__(self, *args, **kwargs):
|
|
super(NotificationCreateWidget, self).__init__(*args, **kwargs)
|
|
|
|
def render(self, name, value, attrs=None, choices=()):
|
|
final_attrs = self.build_attrs(attrs, {'name': name})
|
|
tpl = get_template(constants.TEMPLATE_PREFIX + 'notification_field.html')
|
|
|
|
selected = {}
|
|
for item in value if value else []:
|
|
selected[item['id']] = {'alarm': item['alarm'],
|
|
'ok': item['ok'],
|
|
'undetermined': item['undetermined']}
|
|
data = []
|
|
|
|
for pk, notification in chain(self.choices, choices):
|
|
nt_label = notification['name']
|
|
nt_address = notification['address']
|
|
nt_type = notification['type']
|
|
|
|
if pk in selected:
|
|
actions = selected[pk]
|
|
data.append((pk, nt_label, nt_type, nt_address, actions['alarm'],
|
|
actions['ok'], actions['undetermined'], True))
|
|
else:
|
|
data.append((pk, nt_label, nt_type, nt_address, True, True, True, False))
|
|
|
|
local_attrs = {'data': json.dumps(data)}
|
|
local_attrs.update(final_attrs)
|
|
return tpl.render(local_attrs)
|
|
|
|
def value_from_datadict(self, data, files, name):
|
|
return [{"id": _id} for _id in data.getlist(name)]
|
|
|
|
|
|
class EditAlarmForm(forms.SelfHandlingForm):
|
|
|
|
def __init__(self, request, *args, **kwargs):
|
|
super(EditAlarmForm, self).__init__(request, *args, **kwargs)
|
|
self._init_fields(readOnly=False)
|
|
self.set_notification_choices(request)
|
|
|
|
@classmethod
|
|
def _instantiate(cls, request, *args, **kwargs):
|
|
return cls(request, *args, **kwargs)
|
|
|
|
def _init_fields(self, readOnly=False, create=False, initial=None):
|
|
required = True
|
|
textWidget = None
|
|
choiceWidget = forms.Select
|
|
if create:
|
|
expressionWidget = ExpressionWidget(initial)
|
|
matchByWidget = MatchByWidget(initial)
|
|
notificationWidget = NotificationCreateWidget()
|
|
else:
|
|
expressionWidget = textWidget
|
|
matchByWidget = forms.TextInput(attrs={'readonly': 'readonly'})
|
|
notificationWidget = NotificationCreateWidget()
|
|
|
|
self.fields['name'] = forms.CharField(label=_("Name"),
|
|
required=required,
|
|
max_length=250,
|
|
widget=textWidget,
|
|
help_text=_("An unique name of the alarm."))
|
|
self.fields['expression'] = forms.CharField(label=_("Expression"),
|
|
required=required,
|
|
widget=expressionWidget,
|
|
help_text=_("An alarm expression."))
|
|
self.fields['match_by'] = forms.CharField(label=_("Match by"),
|
|
required=False,
|
|
widget=matchByWidget,
|
|
help_text=_("The metric dimensions used "
|
|
"to create unique alarms."))
|
|
self.fields['description'] = forms.CharField(label=_("Description"),
|
|
required=False,
|
|
widget=textWidget,
|
|
help_text=_("A description of an alarm."))
|
|
sev_choices = [("LOW", _("Low")),
|
|
("MEDIUM", _("Medium")),
|
|
("HIGH", _("High")),
|
|
("CRITICAL", _("Critical"))]
|
|
self.fields['severity'] = forms.ChoiceField(label=_("Severity"),
|
|
choices=sev_choices,
|
|
initial=sev_choices[0],
|
|
widget=choiceWidget,
|
|
required=False,
|
|
help_text=_("Severity of an alarm. "
|
|
"Must be either LOW, MEDIUM, HIGH "
|
|
"or CRITICAL. Default is LOW."))
|
|
if not create:
|
|
self.fields['actions_enabled'] = \
|
|
forms.BooleanField(label=_("Notifications Enabled"),
|
|
required=False,
|
|
initial=True)
|
|
self.fields['notifications'] = NotificationField(
|
|
label=_("Notifications"),
|
|
required=False,
|
|
widget=notificationWidget,
|
|
help_text=_("Notification methods. "
|
|
"Notifications can be sent when an alarm state transition occurs."))
|
|
self.fields['alarm_actions'] = NotificationField(
|
|
label=_("Alarm Actions"),
|
|
widget=forms.MultipleHiddenInput())
|
|
self.fields['ok_actions'] = NotificationField(
|
|
label=_("OK Actions"),
|
|
widget=forms.MultipleHiddenInput())
|
|
self.fields['undetermined_actions'] = NotificationField(
|
|
label=_("Undetermined Actions"),
|
|
widget=forms.MultipleHiddenInput())
|
|
|
|
def set_notification_choices(self, request):
|
|
try:
|
|
notifications = api.monitor.notification_list(request)
|
|
except Exception as e:
|
|
notifications = []
|
|
exceptions.handle(request,
|
|
_('Unable to retrieve notifications: %s') % e)
|
|
notification_choices = [
|
|
(notification['id'], notification) for notification in notifications]
|
|
|
|
self.fields['notifications'].choices = notification_choices
|
|
|
|
def handle(self, request, data):
|
|
try:
|
|
alarm_def = api.monitor.alarmdef_get(request, self.initial['id'])
|
|
api.monitor.alarmdef_update(
|
|
request,
|
|
alarm_id=self.initial['id'],
|
|
severity=data['severity'],
|
|
name=data['name'],
|
|
expression=data['expression'],
|
|
description=data['description'],
|
|
match_by=alarm_def['match_by'],
|
|
actions_enabled=data['actions_enabled'],
|
|
alarm_actions=data['alarm_actions'],
|
|
ok_actions=data['ok_actions'],
|
|
undetermined_actions=data['undetermined_actions'],
|
|
)
|
|
messages.success(request,
|
|
_('Alarm definition has been updated.'))
|
|
except Exception as e:
|
|
exceptions.handle(request, _('%s') % e)
|
|
return False
|
|
return True
|