Refactor to make alarms and notifications as panels
This commit is contained in:
parent
de312c7bb0
commit
b576f6c4d7
monitoring
alarms
__init__.pyconstants.pyforms.pypanel.pytables.py
templates/alarms
_scripts.htmlalarm.htmlalarm_history.htmlalarm_meter.html
urls.pyviews.pyalarms
index.htmlmonitor.htmlmonitorng.htmlnewmonitor.htmlnotifications
0
monitoring/alarms/__init__.py
Normal file
0
monitoring/alarms/__init__.py
Normal file
331
monitoring/alarms/forms.py
Normal file
331
monitoring/alarms/forms.py
Normal file
@ -0,0 +1,331 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 django import forms as django_forms
|
||||
from django.utils.html import escape
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from monitoring import api
|
||||
from monitoring import constants
|
||||
|
||||
|
||||
def get_expression(meter):
|
||||
expr = meter['name']
|
||||
args = None
|
||||
for name, value in meter['dimensions'].items():
|
||||
if name != 'detail':
|
||||
if args:
|
||||
args += ', '
|
||||
else:
|
||||
args = ''
|
||||
args += "%s=%s" % (name, value)
|
||||
return "%s{%s}" % (expr, args)
|
||||
|
||||
|
||||
class SimpleExpressionWidget(django_forms.MultiWidget):
|
||||
def __init__(self, meters=None, attrs=None):
|
||||
choices = [(get_expression(m), get_expression(m)) for m in meters]
|
||||
comparators = [('>', '>'), ('>=', '>='), ('<', '<'), ('<=', '<=')]
|
||||
func = [('min', _('min')), ('max', _('max')), ('sum', _('sum')),
|
||||
('count', _('count')), ('avg', _('avg'))]
|
||||
_widgets = (
|
||||
django_forms.widgets.Select(attrs=attrs, choices=func),
|
||||
django_forms.widgets.Select(attrs=attrs, choices=choices),
|
||||
django_forms.widgets.Select(attrs=attrs, choices=comparators),
|
||||
django_forms.widgets.TextInput(attrs=attrs),
|
||||
)
|
||||
super(SimpleExpressionWidget, self).__init__(_widgets, attrs)
|
||||
|
||||
def decompress(self, expr):
|
||||
return [None, None, None]
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
return ''.join(rendered_widgets)
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
values = [
|
||||
widget.value_from_datadict(data, files, name + '_%s' % i)
|
||||
for i, widget in enumerate(self.widgets)]
|
||||
try:
|
||||
expression = '%s(%s)%s%s' % (values[0],
|
||||
values[1],
|
||||
values[2],
|
||||
values[3])
|
||||
except ValueError:
|
||||
return ''
|
||||
else:
|
||||
return expression
|
||||
|
||||
|
||||
class NotificationTableWidget(forms.Widget):
|
||||
FIELD_ID_IDX = 0
|
||||
FIELD_NAME_IDX = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.fields = kwargs.pop('fields')
|
||||
super(NotificationTableWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
def render(self, name, value, attrs):
|
||||
output = '<table class="table table-condensed"><thead><tr>'
|
||||
for field in self.fields:
|
||||
output += '<th>%s</th>' % unicode(field[self.FIELD_NAME_IDX])
|
||||
output += '</tr></thead>'
|
||||
if value:
|
||||
for notification in value:
|
||||
output += "<tr>"
|
||||
for field in self.fields:
|
||||
field_value = notification[field[self.FIELD_ID_IDX]]
|
||||
output += '<td>%s</td>' % escape(field_value)
|
||||
output += "</tr>"
|
||||
output += '</table>'
|
||||
return format_html(output)
|
||||
|
||||
|
||||
class NotificationField(forms.MultiValueField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NotificationField, self).__init__(*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=()):
|
||||
output = '<table id="notification_table" ' + \
|
||||
'class="table table-condensed">'
|
||||
output += '<thead><tr><th>%s</th></tr></thead>' % \
|
||||
unicode(_("Name"))
|
||||
if value:
|
||||
idx = 1
|
||||
for notification in value:
|
||||
output += '<tr><td>'
|
||||
output += ('<select id="id_notifications_%d" ' +
|
||||
'name="notifications_%d"> ') % (idx, idx)
|
||||
options = self.render_options(
|
||||
choices,
|
||||
[notification['notification_id']])
|
||||
if options:
|
||||
output += options
|
||||
output += '</select>'
|
||||
output += '</td></tr>'
|
||||
idx += 1
|
||||
else:
|
||||
output += '<tr><td>'
|
||||
output += '<select id="id_notifications_1" '
|
||||
output += 'name="notifications_1"> '
|
||||
options = self.render_options(choices, [value])
|
||||
if options:
|
||||
output += options
|
||||
output += '</select>'
|
||||
output += '</td></tr>'
|
||||
output += '</table>'
|
||||
label = unicode(_("+ Add more"))
|
||||
output += '<a href="" id="add_notification_button">%s</a>' % (label)
|
||||
return format_html(output)
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
notifications = []
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
notification_id = "%s_%d" % (name, i)
|
||||
if notification_id in data:
|
||||
if len(data[notification_id]) > 0:
|
||||
notifications.append({"notification_id":
|
||||
data[notification_id]})
|
||||
else:
|
||||
break
|
||||
return notifications
|
||||
|
||||
|
||||
class BaseAlarmForm(forms.SelfHandlingForm):
|
||||
@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
|
||||
textAreaWidget = forms.Textarea(attrs={'class': 'large-text-area'})
|
||||
readOnlyTextInput = forms.TextInput(attrs={'readonly': 'readonly'})
|
||||
choiceWidget = forms.Select
|
||||
if readOnly:
|
||||
required = False
|
||||
textWidget = readOnlyTextInput
|
||||
choiceWidget = readOnlyTextInput
|
||||
textAreaWidget = forms.Textarea(attrs={'readonly': 'readonly',
|
||||
'class': 'large-text-area'
|
||||
})
|
||||
expressionWidget = textAreaWidget
|
||||
else:
|
||||
if create:
|
||||
meters = api.monitor.metrics_list(self.request)
|
||||
if initial and 'service' in initial and \
|
||||
initial['service'] != 'all':
|
||||
service = initial['service']
|
||||
meters = [m for m in meters
|
||||
if m.setdefault('dimensions', {}).
|
||||
setdefault('service', '') == service]
|
||||
expressionWidget = SimpleExpressionWidget(meters=meters)
|
||||
else:
|
||||
expressionWidget = textAreaWidget
|
||||
|
||||
if create:
|
||||
notificationWidget = NotificationCreateWidget()
|
||||
else:
|
||||
notificationWidget = NotificationTableWidget(
|
||||
fields=[('name', _('Name')),
|
||||
('type', _('Type')),
|
||||
('address', _('Address')), ])
|
||||
|
||||
self.fields['name'] = forms.CharField(label=_("Name"),
|
||||
required=required,
|
||||
max_length=250,
|
||||
widget=textWidget)
|
||||
self.fields['expression'] = forms.CharField(label=_("Expression"),
|
||||
required=required,
|
||||
widget=expressionWidget)
|
||||
self.fields['description'] = forms.CharField(label=_("Description"),
|
||||
required=False,
|
||||
widget=textAreaWidget)
|
||||
sev_choices = [("LOW", _("Low")),
|
||||
("MEDIUM", _("Medium")),
|
||||
("HIGH", _("High")),
|
||||
("CRITICAL", _("Critical"))]
|
||||
self.fields['severity'] = forms.ChoiceField(label=_("Severity"),
|
||||
choices=sev_choices,
|
||||
widget=choiceWidget,
|
||||
required=False)
|
||||
self.fields['state'] = forms.CharField(label=_("State"),
|
||||
required=False,
|
||||
widget=textWidget)
|
||||
self.fields['actions_enabled'] = \
|
||||
forms.BooleanField(label=_("Notifications Enabled"),
|
||||
required=False,
|
||||
initial=True)
|
||||
self.fields['notifications'] = NotificationField(
|
||||
label=_("Notifications"),
|
||||
required=False,
|
||||
widget=notificationWidget)
|
||||
|
||||
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['name'])
|
||||
for notification in notifications]
|
||||
if notification_choices:
|
||||
if len(notification_choices) > 1:
|
||||
notification_choices.insert(
|
||||
0, ("", unicode(_("Select Notification"))))
|
||||
else:
|
||||
notification_choices.insert(
|
||||
0, ("", unicode(_("No notifications available."))))
|
||||
|
||||
self.fields['notifications'].choices = notification_choices
|
||||
|
||||
|
||||
class CreateAlarmForm(BaseAlarmForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateAlarmForm, self).__init__(request, *args, **kwargs)
|
||||
super(CreateAlarmForm, self)._init_fields(readOnly=False, create=True,
|
||||
initial=kwargs['initial'])
|
||||
super(CreateAlarmForm, self).set_notification_choices(request)
|
||||
self.fields.pop('state')
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
alarm_actions = [notification.get('notification_id')
|
||||
for notification in data['notifications']]
|
||||
api.monitor.alarm_create(
|
||||
request,
|
||||
name=data['name'],
|
||||
expression=data['expression'],
|
||||
description=data['description'],
|
||||
severity=data['severity'],
|
||||
alarm_actions=alarm_actions)
|
||||
messages.success(request,
|
||||
_('Alarm has been created successfully.'))
|
||||
except Exception as e:
|
||||
exceptions.handle(request, _('Unable to create the alarm: %s') % e)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class DetailAlarmForm(BaseAlarmForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(DetailAlarmForm, self).__init__(request, *args, **kwargs)
|
||||
super(DetailAlarmForm, self)._init_fields(readOnly=True)
|
||||
|
||||
def handle(self, request, data):
|
||||
return True
|
||||
|
||||
|
||||
class EditAlarmForm(BaseAlarmForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(EditAlarmForm, self).__init__(request, *args, **kwargs)
|
||||
super(EditAlarmForm, self)._init_fields(readOnly=False)
|
||||
super(EditAlarmForm, self).set_notification_choices(request)
|
||||
self.fields.pop('state')
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
alarm_actions = []
|
||||
if data['notifications']:
|
||||
alarm_actions = [notification.get('notification_id')
|
||||
for notification in data['notifications']]
|
||||
api.monitor.alarm_update(
|
||||
request,
|
||||
alarm_id=self.initial['id'],
|
||||
actions_enabled=self.initial['actions_enabled'],
|
||||
state=self.initial['state'],
|
||||
severity=data['severity'],
|
||||
name=data['name'],
|
||||
expression=data['expression'],
|
||||
description=data['description'],
|
||||
alarm_actions=alarm_actions,
|
||||
)
|
||||
messages.success(request,
|
||||
_('Alarm has been edited successfully.'))
|
||||
except Exception as e:
|
||||
exceptions.handle(request, _('Unable to edit the alarm: %s') % e)
|
||||
return False
|
||||
return True
|
178
monitoring/alarms/tables.py
Normal file
178
monitoring/alarms/tables.py
Normal file
@ -0,0 +1,178 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 logging
|
||||
|
||||
from django import template
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from . import constants
|
||||
from monitoring import api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
STATUS = ["OK", "WARNING", "CRITICAL", "UNKNOWN"]
|
||||
|
||||
|
||||
def get_status(index):
|
||||
if index < len(STATUS):
|
||||
return STATUS[index]
|
||||
else:
|
||||
return "UNKNOWN: %d" % index
|
||||
|
||||
|
||||
def show_status(data):
|
||||
status = data
|
||||
img_tag = '<img src="%s" title="%s"/>'
|
||||
if status == 'CRITICAL':
|
||||
return img_tag % (constants.CRITICAL_ICON, status)
|
||||
if status in ('LOW', 'MEDIUM', 'HIGH'):
|
||||
return img_tag % (constants.WARNING_ICON, status)
|
||||
if status == 'OK':
|
||||
return img_tag % (constants.OK_ICON, status)
|
||||
if status == 'UNKNOWN' or status == 'UNDETERMINED':
|
||||
return img_tag % (constants.UNKNOWN_ICON, status)
|
||||
return status
|
||||
|
||||
|
||||
def show_severity(data):
|
||||
severity = data['severity']
|
||||
state = data['state']
|
||||
if state == 'ALARM':
|
||||
return severity
|
||||
else:
|
||||
return state
|
||||
|
||||
|
||||
def show_service(data):
|
||||
if any(data['expression_data']['dimensions']):
|
||||
dimensions = data['expression_data']['dimensions']
|
||||
if 'service' in dimensions:
|
||||
return str(data['expression_data']['dimensions']['service'])
|
||||
return ""
|
||||
|
||||
|
||||
def show_host(data):
|
||||
if any(data['expression_data']['dimensions']):
|
||||
dimensions = data['expression_data']['dimensions']
|
||||
if 'hostname' in dimensions:
|
||||
return str(data['expression_data']['dimensions']['hostname'])
|
||||
return ""
|
||||
|
||||
|
||||
class ShowAlarmHistory(tables.LinkAction):
|
||||
name = 'history'
|
||||
verbose_name = _('Show History')
|
||||
url = constants.URL_PREFIX + 'history'
|
||||
classes = ('btn-edit',)
|
||||
|
||||
|
||||
class ShowAlarmMeters(tables.LinkAction):
|
||||
name = 'meters'
|
||||
verbose_name = _('Show Meters')
|
||||
url = constants.URL_PREFIX + 'meters'
|
||||
classes = ('btn-edit',)
|
||||
|
||||
|
||||
class CreateAlarm(tables.LinkAction):
|
||||
name = "create_alarm"
|
||||
verbose_name = _("Create Alarm")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
|
||||
def get_link_url(self):
|
||||
return reverse(constants.URL_PREFIX + 'alarm_create',
|
||||
args=(self.table.kwargs['service'],))
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class EditAlarm(tables.LinkAction):
|
||||
name = "edit_alarm"
|
||||
verbose_name = _("Edit Alarm")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
|
||||
def get_link_url(self, datum):
|
||||
return reverse(constants.URL_PREFIX + 'alarm_edit',
|
||||
args=(self.table.kwargs['service'], datum['id'], ))
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class DeleteAlarm(tables.DeleteAction):
|
||||
name = "delete_alarm"
|
||||
verbose_name = _("Delete Alarm")
|
||||
data_type_singular = _("Alarm")
|
||||
data_type_plural = _("Alarms")
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
api.monitor.alarm_delete(request, obj_id)
|
||||
|
||||
|
||||
class AlarmsTable(tables.DataTable):
|
||||
status = tables.Column(transform=show_severity, verbose_name=_('Status'),
|
||||
status_choices={(show_status('OK'), True)},
|
||||
filters=[show_status, template.defaultfilters.safe])
|
||||
target = tables.Column('name', verbose_name=_('Name'),
|
||||
link=constants.URL_PREFIX + 'alarm_detail',
|
||||
link_classes=('ajax-modal',))
|
||||
host = tables.Column(transform=show_host, verbose_name=_('Host'))
|
||||
service = tables.Column(transform=show_service, verbose_name=_('Service'))
|
||||
state = tables.Column('state', verbose_name=_('State'))
|
||||
expression = tables.Column('expression', verbose_name=_('Expression'))
|
||||
enabled = tables.Column('actions_enabled',
|
||||
verbose_name=_('Notifications Enabled'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return obj['id']
|
||||
|
||||
class Meta:
|
||||
name = "alarms"
|
||||
verbose_name = _("Alarms")
|
||||
row_actions = (ShowAlarmHistory,
|
||||
ShowAlarmMeters,
|
||||
EditAlarm,
|
||||
DeleteAlarm,
|
||||
)
|
||||
table_actions = (CreateAlarm, )
|
||||
|
||||
|
||||
class AlarmHistoryTable(tables.DataTable):
|
||||
status = tables.Column('Status', verbose_name=_('Status'),
|
||||
status_choices={(show_status('OK'), True)},
|
||||
filters=[show_status, template.defaultfilters.safe])
|
||||
target = tables.Column('Host', verbose_name=_('Host'))
|
||||
name = tables.Column('Service', verbose_name=_('Service'))
|
||||
lastCheck = tables.Column('Last_Check', verbose_name=_('Last Check'))
|
||||
time = tables.Column('Duration', verbose_name=_('Duration'))
|
||||
detail = tables.Column('Status_Information',
|
||||
verbose_name=_('Status_Information'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return obj['Last_Check'] + obj['Service']
|
||||
|
||||
class Meta:
|
||||
name = "users"
|
||||
verbose_name = _("Alarm History")
|
||||
row_actions = (ShowAlarmMeters,)
|
@ -8,7 +8,7 @@
|
||||
{% load url from future %}
|
||||
{% block main %}
|
||||
<div style="padding: 3px;">
|
||||
<a href="{% url 'horizon:overcloud:monitoring:alarm' 'all'%}" class="btn btn-small btn-run showspinner">{% trans 'All Alarms' %}</a>
|
||||
<a href="{% url 'horizon:overcloud:alarms:alarm' 'all'%}" class="btn btn-small btn-run showspinner">{% trans 'All Alarms' %}</a>
|
||||
</div>
|
||||
{% include 'overcloud/monitoring/monitor.html' %}
|
||||
{% include 'overcloud/alarms/monitor.html' %}
|
||||
{% endblock %}
|
@ -52,7 +52,7 @@
|
||||
{{ group.name }}
|
||||
</div>
|
||||
{% for service in group.services %}
|
||||
<a href="{% url 'horizon:overcloud:monitoring:alarm' service.name %}" class="showspinner">
|
||||
<a href="{% url 'horizon:overcloud:alarms:alarm' service.name %}" class="showspinner">
|
||||
<div class="base-chicklet {{ service.class }}">
|
||||
<div>
|
||||
<div class = "status-icon-holder" >
|
@ -40,7 +40,4 @@ urlpatterns = patterns(
|
||||
url(r'^alarm/(?P<service>[^/]+)/(?P<id>[^/]+)/alarm_edit/$',
|
||||
views.AlarmEditView.as_view(),
|
||||
name='alarm_edit'),
|
||||
url(r'^notification_create$',
|
||||
views.NotificationCreateView.as_view(),
|
||||
name='notification_create'),
|
||||
)
|
371
monitoring/alarms/views.py
Normal file
371
monitoring/alarms/views.py
Normal file
@ -0,0 +1,371 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 logging
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import json
|
||||
import random
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse_lazy, reverse # noqa
|
||||
from django.template import defaultfilters as filters
|
||||
from django.http import HttpResponse # noqa
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
|
||||
from monitoring import api
|
||||
from .tables import AlarmsTable
|
||||
from .tables import AlarmHistoryTable
|
||||
from .tables import show_service
|
||||
from .tables import show_severity
|
||||
from . import forms as alarm_forms
|
||||
from . import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SAMPLE = [{'name': _('Platform Services'),
|
||||
'services': [{'name': 'MaaS',
|
||||
'display': _('MaaS')},
|
||||
{'name': 'DBaaS',
|
||||
'display': _('DBaaS')},
|
||||
{'name': 'LBaaS',
|
||||
'display': _('LBaaS')},
|
||||
{'name': 'DNSaaS',
|
||||
'display': _('DNSaaS')},
|
||||
{'name': 'MSGaaS',
|
||||
'display': _('MSGaaS')},
|
||||
]},
|
||||
{'name': _('The OverCloud Services'),
|
||||
'services': [{'name': 'nova',
|
||||
'display': _('Nova')},
|
||||
{'name': 'swift',
|
||||
'display': _('Swift')},
|
||||
{'name': 'bock',
|
||||
'display': _('Cinder')},
|
||||
{'name': 'glance',
|
||||
'display': _('Glance')},
|
||||
{'name': 'quantum',
|
||||
'display': _('Neutron')},
|
||||
{'name': 'mysql',
|
||||
'display': _('MySQL')},
|
||||
{'name': 'rabbitmq',
|
||||
'display': _('RabbitMQ')},
|
||||
{'name': 'mini-mon',
|
||||
'display': _('Monitoring')},
|
||||
]},
|
||||
{'name': _('The UnderCloud Services'),
|
||||
'services': [{'name': 'nova',
|
||||
'display': _('Nova')},
|
||||
{'name': 'swift',
|
||||
'display': _('Cinder')},
|
||||
{'name': 'glance',
|
||||
'display': _('Glance')},
|
||||
{'name': 'horizon',
|
||||
'display': _('Horizon')},
|
||||
]},
|
||||
{'name': _('Network Services'),
|
||||
'services': [{'name': 'dhcp',
|
||||
'display': _('DHCP')},
|
||||
{'name': 'dns',
|
||||
'display': _('DNS')},
|
||||
{'name': 'dns-servers',
|
||||
'display': _('DNS Servers')},
|
||||
{'name': 'http',
|
||||
'display': _('http')},
|
||||
{'name': 'web_proxy',
|
||||
'display': _('Web Proxy')},
|
||||
]}
|
||||
]
|
||||
|
||||
|
||||
def get_icon(status):
|
||||
if status == 'chicklet-success':
|
||||
return constants.OK_ICON
|
||||
if status == 'chicklet-error':
|
||||
return constants.CRITICAL_ICON
|
||||
if status == 'chicklet-warning':
|
||||
return constants.WARNING_ICON
|
||||
if status == 'chicklet-unknown':
|
||||
return constants.UNKNOWN_ICON
|
||||
if status == 'chicklet-notfound':
|
||||
return constants.NOTFOUND_ICON
|
||||
|
||||
|
||||
priorities = [
|
||||
{'status': 'chicklet-success', 'severity': 'OK'},
|
||||
{'status': 'chicklet-unknown', 'severity': 'UNDETERMINED'},
|
||||
{'status': 'chicklet-warning', 'severity': 'LOW'},
|
||||
{'status': 'chicklet-warning', 'severity': 'MEDIUM'},
|
||||
{'status': 'chicklet-warning', 'severity': 'HIGH'},
|
||||
{'status': 'chicklet-error', 'severity': 'CRITICAL'},
|
||||
]
|
||||
index_by_severity = {d['severity']: i for i, d in enumerate(priorities)}
|
||||
|
||||
|
||||
def get_status(alarms):
|
||||
if not alarms:
|
||||
return 'chicklet-notfound'
|
||||
status_index = 0
|
||||
for a in alarms:
|
||||
severity = show_severity(a)
|
||||
severity_index = index_by_severity[severity]
|
||||
status_index = max(status_index, severity_index)
|
||||
return priorities[status_index]['status']
|
||||
|
||||
|
||||
def generate_status(request):
|
||||
alarms = api.monitor.alarm_list(request)
|
||||
alarms_by_service = {}
|
||||
for a in alarms:
|
||||
service = show_service(a)
|
||||
service_alarms = alarms_by_service.setdefault(service, [])
|
||||
service_alarms.append(a)
|
||||
for row in SAMPLE:
|
||||
for service in row['services']:
|
||||
service_alarms = alarms_by_service.get(service['name'], [])
|
||||
service['class'] = get_status(service_alarms)
|
||||
service['icon'] = get_icon(service['class'])
|
||||
return SAMPLE
|
||||
|
||||
|
||||
class IndexView(TemplateView):
|
||||
template_name = constants.TEMPLATE_PREFIX + 'index.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(IndexView, self).get_context_data(**kwargs)
|
||||
context["date"] = datetime.datetime.utcnow()
|
||||
context["service_groups"] = generate_status(self.request)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class StatusView(TemplateView):
|
||||
template_name = ""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
ret = {
|
||||
'series': generate_status(self.request),
|
||||
'settings': {}
|
||||
}
|
||||
|
||||
return HttpResponse(json.dumps(ret),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class AlarmServiceView(tables.DataTableView):
|
||||
table_class = AlarmsTable
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarm.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.service = kwargs['service']
|
||||
del kwargs['service']
|
||||
return super(AlarmServiceView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get_data(self):
|
||||
results = []
|
||||
try:
|
||||
if self.service == 'all':
|
||||
results = api.monitor.alarm_list(self.request)
|
||||
else:
|
||||
results = api.monitor.alarm_list_by_service(
|
||||
self.request,
|
||||
self.service)
|
||||
except:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
return results
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AlarmServiceView, self).get_context_data(**kwargs)
|
||||
context["service"] = self.service
|
||||
return context
|
||||
|
||||
|
||||
class AlarmCreateView(forms.ModalFormView):
|
||||
form_class = alarm_forms.CreateAlarmForm
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarms/create.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.service = kwargs['service']
|
||||
return super(AlarmCreateView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get_initial(self):
|
||||
return {"service": self.service}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AlarmCreateView, self).get_context_data(**kwargs)
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
context["action_url"] = reverse(constants.URL_PREFIX + 'alarm_create', args=(self.service,))
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy(constants.URL_PREFIX + 'alarm', args=(self.service,))
|
||||
|
||||
|
||||
def transform_alarm_data(obj):
|
||||
return obj
|
||||
return {'id': getattr(obj, 'id', None),
|
||||
'name': getattr(obj, 'name', None),
|
||||
'expression': getattr(obj, 'expression', None),
|
||||
'state': filters.title(getattr(obj, 'state', None)),
|
||||
'severity': filters.title(getattr(obj, 'severity', None)),
|
||||
'actions_enabled': filters.title(getattr(obj, 'actions_enabled',
|
||||
None)),
|
||||
'notifications': getattr(obj, 'alarm_actions', None), }
|
||||
|
||||
|
||||
class AlarmDetailView(forms.ModalFormView):
|
||||
form_class = alarm_forms.DetailAlarmForm
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarms/detail.html'
|
||||
|
||||
def get_object(self):
|
||||
id = self.kwargs['id']
|
||||
try:
|
||||
if hasattr(self, "_object"):
|
||||
return self._object
|
||||
self._object = None
|
||||
self._object = api.monitor.alarm_get(self.request, id)
|
||||
notifications = []
|
||||
# Fetch the notification object for each alarm_actions
|
||||
for id in self._object["alarm_actions"]:
|
||||
try:
|
||||
notification = api.monitor.notification_get(
|
||||
self.request,
|
||||
id)
|
||||
notifications.append(notification)
|
||||
except exceptions.NOT_FOUND:
|
||||
msg = _("Notification %s has already been deleted.") % id
|
||||
notifications.append({"id": id,
|
||||
"name": unicode(msg),
|
||||
"type": "",
|
||||
"address": ""})
|
||||
self._object["notifications"] = notifications
|
||||
return self._object
|
||||
except Exception:
|
||||
redirect = reverse(constants.URL_PREFIX + 'alarm')
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve alarm details.'),
|
||||
redirect=redirect)
|
||||
return None
|
||||
|
||||
def get_initial(self):
|
||||
self.alarm = self.get_object()
|
||||
return transform_alarm_data(self.alarm)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AlarmDetailView, self).get_context_data(**kwargs)
|
||||
context["alarm"] = self.alarm
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return "d"
|
||||
|
||||
|
||||
class AlarmEditView(forms.ModalFormView):
|
||||
form_class = alarm_forms.EditAlarmForm
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarms/edit.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.service = kwargs['service']
|
||||
del kwargs['service']
|
||||
return super(AlarmEditView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get_object(self):
|
||||
id = self.kwargs['id']
|
||||
try:
|
||||
if hasattr(self, "_object"):
|
||||
return self._object
|
||||
self._object = None
|
||||
self._object = api.monitor.alarm_get(self.request, id)
|
||||
notifications = []
|
||||
# Fetch the notification object for each alarm_actions
|
||||
for id in self._object["alarm_actions"]:
|
||||
try:
|
||||
notification = api.monitor.notification_get(
|
||||
self.request,
|
||||
id)
|
||||
notifications.append(notification)
|
||||
except exceptions.NOT_FOUND:
|
||||
msg = _("Notification %s has already been deleted.") % id
|
||||
notifications.append({"id": id,
|
||||
"name": unicode(msg),
|
||||
"type": "",
|
||||
"address": ""})
|
||||
self._object["notifications"] = notifications
|
||||
return self._object
|
||||
except Exception:
|
||||
redirect = reverse(constants.URL_PREFIX + 'alarm')
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve alarm details.'),
|
||||
redirect=redirect)
|
||||
return None
|
||||
|
||||
def get_initial(self):
|
||||
self.alarm = self.get_object()
|
||||
return transform_alarm_data(self.alarm)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AlarmEditView, self).get_context_data(**kwargs)
|
||||
id = self.kwargs['id']
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
context["action_url"] = reverse(constants.URL_PREFIX + 'alarm_edit',
|
||||
args=(self.service, id,))
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy(constants.URL_PREFIX + 'alarm', args=(self.service,))
|
||||
|
||||
|
||||
class AlarmHistoryView(tables.DataTableView):
|
||||
table_class = AlarmHistoryTable
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarm_history.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(AlarmHistoryView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get_data(self):
|
||||
# to be implemented
|
||||
results = []
|
||||
|
||||
return results
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AlarmHistoryView, self).get_context_data(**kwargs)
|
||||
return context
|
||||
|
||||
|
||||
class AlarmMeterView(TemplateView):
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarm_meter.html'
|
||||
|
||||
|
||||
def get_random_status():
|
||||
distribution = [
|
||||
{'prob': .04, 'value': 'chicklet-error'},
|
||||
{'prob': .04, 'value': 'chicklet-warning'},
|
||||
{'prob': .04, 'value': 'chicklet-unknown'},
|
||||
{'prob': .04, 'value': 'chicklet-notfound'},
|
||||
{'prob': 1.0, 'value': 'chicklet-success'},
|
||||
]
|
||||
num = random.random()
|
||||
for dist in distribution:
|
||||
if num < dist["prob"]:
|
||||
return dist["value"]
|
||||
num = num - dist["prob"]
|
||||
return distribution[len(distribution) - 1]["value"]
|
48
monitoring/notifications/constants.py
Normal file
48
monitoring/notifications/constants.py
Normal file
@ -0,0 +1,48 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 django.core import validators
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
|
||||
class NotificationType(object):
|
||||
SMS = "SMS"
|
||||
EMAIL = "EMAIL"
|
||||
|
||||
CHOICES = [(EMAIL, _("Email")),
|
||||
(SMS, _("SMS")), ]
|
||||
|
||||
@staticmethod
|
||||
def get_label(key):
|
||||
for choice in NotificationType.CHOICES:
|
||||
if choice[0] == key:
|
||||
return choice[1]
|
||||
return key
|
||||
|
||||
PHONE_VALIDATOR = validators.RegexValidator(
|
||||
regex=r"^\+[()0-9 ]{5,20}$",
|
||||
message=_("Address must contain a valid phone number."))
|
||||
EMAIL_VALIDATOR = validators.EmailValidator(
|
||||
message=_("Address must contain a valid email address."))
|
||||
|
||||
URL_PREFIX = 'horizon:overcloud:alarms:'
|
||||
TEMPLATE_PREFIX = 'overcloud/alarms/'
|
||||
|
||||
CRITICAL_ICON = '/static/monitoring/img/critical-icon.png'
|
||||
WARNING_ICON = '/static/monitoring/img/warning-icon.png'
|
||||
OK_ICON = '/static/monitoring/img/ok-icon.png'
|
||||
UNKNOWN_ICON = '/static/monitoring/img/unknown-icon.png'
|
||||
NOTFOUND_ICON = '/static/monitoring/img/notfound-icon.png'
|
@ -21,25 +21,6 @@ from . import views
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^status', views.StatusView.as_view(), name='status'),
|
||||
url(r'^alarm/(?P<service>[^/]+)/$',
|
||||
views.AlarmServiceView.as_view(),
|
||||
name='alarm'),
|
||||
url(r'^history/(?P<service>[^/]+)$',
|
||||
views.AlarmHistoryView.as_view(),
|
||||
name='history'),
|
||||
url(r'^meters/(?P<service>[^/]+)$',
|
||||
views.AlarmMeterView.as_view(),
|
||||
name='meters'),
|
||||
url(r'^alarm/(?P<service>[^/]+)/create$',
|
||||
views.AlarmCreateView.as_view(),
|
||||
name='alarm_create'),
|
||||
url(r'^(?P<id>[^/]+)/alarm_detail/$',
|
||||
views.AlarmDetailView.as_view(),
|
||||
name='alarm_detail'),
|
||||
url(r'^alarm/(?P<service>[^/]+)/(?P<id>[^/]+)/alarm_edit/$',
|
||||
views.AlarmEditView.as_view(),
|
||||
name='alarm_edit'),
|
||||
url(r'^notification_create$',
|
||||
views.NotificationCreateView.as_view(),
|
||||
name='notification_create'),
|
||||
|
Loading…
Reference in New Issue
Block a user