Retire monasca-ui repository
This repository is being retired as part of the Monasca project retirement. The project content has been replaced with a retirement notice. Needed-By: I3cb522ce8f51424b64e93c1efaf0dfd1781cd5ac Change-Id: Idf7496644af9c20b807a0b9367d0bcd07966d371 Signed-off-by: Goutham Pacha Ravi <gouthampravi@gmail.com>
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
- project:
|
||||
templates:
|
||||
- check-requirements
|
||||
- horizon-non-primary-django-jobs
|
||||
- openstack-cover-jobs-horizon
|
||||
- openstack-python3-jobs-horizon
|
||||
@@ -1,19 +0,0 @@
|
||||
The source repository for this project can be found at:
|
||||
|
||||
https://opendev.org/openstack/monasca-ui
|
||||
|
||||
Pull requests submitted through GitHub are not monitored.
|
||||
|
||||
To start contributing to OpenStack, follow the steps in the contribution guide
|
||||
to set up and use Gerrit:
|
||||
|
||||
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
|
||||
|
||||
Bugs should be filed on Storyboard:
|
||||
|
||||
https://storyboard.openstack.org/#!/project/875
|
||||
|
||||
For more specific information about contributing to this repository, see the
|
||||
Monasca contributor guide:
|
||||
|
||||
https://docs.openstack.org/monasca-api/latest/contributor/contributing.html
|
||||
153
README.rst
153
README.rst
@@ -1,148 +1,9 @@
|
||||
==========
|
||||
Monasca UI
|
||||
==========
|
||||
This project is no longer maintained.
|
||||
|
||||
.. image:: https://governance.openstack.org/tc/badges/monasca-ui.svg
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
Monasca UI is implemented as a Horizon plugin that adds panels to
|
||||
Horizon. It is installed into devstack by the monasca-api plugin.
|
||||
|
||||
DevStack Deployment Set Up
|
||||
==========================
|
||||
|
||||
- ``cd /opt/stack/horizon``
|
||||
- Install OpenStack upper-constraints requirements
|
||||
``pip install -c https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt -r requirements.txt``
|
||||
- Clone Monasca UI:
|
||||
``git clone https://opendev.org/openstack/monasca-ui.git``
|
||||
- Add ``git+https://opendev.org/openstack/monasca-ui.git`` to
|
||||
``requirements.txt``.
|
||||
- Install Monasca UI required packages:
|
||||
``pip install -r requirements.txt`` (monasca-client packages will be installed)
|
||||
- Edit ``openstack_dashboard/settings.py`` to include the following two
|
||||
lines:
|
||||
|
||||
- ``import monitoring.enabled``
|
||||
- ``monitoring.enabled,`` (Add this line to the
|
||||
``settings_utils.update_dashboards`` list)
|
||||
|
||||
- Link Monasca UI into Horizon:
|
||||
|
||||
::
|
||||
|
||||
ln -sf $(pwd)/../monasca-ui/monitoring/enabled/_50_admin_add_monitoring_panel.py \
|
||||
$(pwd)/openstack_dashboard/enabled/_50_admin_add_monitoring_panel.py
|
||||
ln -sf $(pwd)/../monasca-ui/monitoring/conf/monitoring_policy.yaml \
|
||||
$(pwd)/openstack_dashboard/conf/monitoring_policy.yaml
|
||||
ln -sfF $(pwd)/../monasca-ui/monitoring $(pwd)/monitoring
|
||||
|
||||
- Collect static files, run tests
|
||||
|
||||
::
|
||||
|
||||
python manage.py collectstatic --noinput
|
||||
python manage.py compress
|
||||
./run_tests.sh
|
||||
|
||||
- Restart apache service ``service apache2 restart``
|
||||
|
||||
Development Environment Set Up
|
||||
==============================
|
||||
|
||||
Get the Code
|
||||
------------
|
||||
|
||||
::
|
||||
|
||||
git clone https://opendev.org/openstack/monasca-ui.git # clone monasca-ui
|
||||
git clone https://opendev.org/openstack/horizon.git # clone horizon
|
||||
git clone https://github.com/monasca/grafana.git # clone grafana
|
||||
git clone https://github.com/openstack/monasca-grafana-datasource.git # clone grafana plugins
|
||||
|
||||
Set up Horizon
|
||||
--------------
|
||||
|
||||
Since Monasca UI is a Horizon plugin, the first step is to get their
|
||||
development environment set up.
|
||||
|
||||
::
|
||||
|
||||
cd horizon
|
||||
./run_tests.sh
|
||||
cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
|
||||
|
||||
Pro Tip: Make sure you have Horizon running correctly before proceeding.
|
||||
For more details visit: https://docs.openstack.org/horizon/latest/#setup
|
||||
|
||||
Set up Monasca-UI
|
||||
-----------------
|
||||
|
||||
- Edit ``openstack_dashboard/local/local_settings.py`` to modify the
|
||||
``OPENSTACK_HOST`` IP address to point to devstack.
|
||||
- Add ``monasca-client`` to ``requirements.txt``. Get the latest
|
||||
version from: https://pypi.org/project/python-monascaclient
|
||||
- Link monasca into Horizon:
|
||||
|
||||
::
|
||||
|
||||
ln -sf $(pwd)/../monasca-ui/monitoring/enabled/_50_admin_add_monitoring_panel.py \
|
||||
$(pwd)/openstack_dashboard/enabled/_50_admin_add_monitoring_panel.py
|
||||
ln -sf $(pwd)/../monasca-ui/monitoring/conf/monitoring_policy.yaml \
|
||||
$(pwd)/openstack_dashboard/conf/monitoring_policy.yaml
|
||||
ln -sfF $(pwd)/../monasca-ui/monitoring $(pwd)/monitoring
|
||||
./run_tests #load monasca-client into virtualenv
|
||||
|
||||
Set up Grafana 4.1
|
||||
------------------
|
||||
|
||||
- The grafana4 branch of grafana is stable, as is master in
|
||||
monasca-grafana-datasource.
|
||||
- Copy ``monasca-grafana-datasource/`` into
|
||||
``grafana/plugins/monasca-grafana-datasource/``.
|
||||
- Use the grafana docs to build and deploy grafana:
|
||||
|
||||
- https://grafana.com/docs/project/building_from_source/
|
||||
- https://grafana.com/docs/installation/configuration/
|
||||
|
||||
- Copy ``monasca-ui/grafana-dashboards/*`` to ``/public/dashboards/``
|
||||
in your grafana deployment.
|
||||
- Set ``GRAFANA_URL`` in the Horizon settings.
|
||||
|
||||
Start Server
|
||||
------------
|
||||
|
||||
::
|
||||
|
||||
./run_tests.sh --runserver
|
||||
|
||||
Style checks
|
||||
------------
|
||||
|
||||
To check if the code follows python coding style, run the following
|
||||
command from the root directory of this project:
|
||||
|
||||
::
|
||||
|
||||
$ tox -e pep8
|
||||
|
||||
Coverage checks
|
||||
---------------
|
||||
|
||||
To measure the code coverage, run the following command from the root
|
||||
directory of this project:
|
||||
|
||||
::
|
||||
|
||||
$ tox -e cover
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
To run all the unit test cases, run the following command from the root
|
||||
directory of this project:
|
||||
|
||||
::
|
||||
|
||||
$ tox -e py3
|
||||
For any further questions, please email openstack-discuss@lists.openstack.org
|
||||
or join #openstack-dev on OFTC.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[python: **.py]
|
||||
[django: **/templates/**.html]
|
||||
@@ -1,2 +0,0 @@
|
||||
[javascript: **.js]
|
||||
[angular: **/static/**.html]
|
||||
4
debian/changelog
vendored
4
debian/changelog
vendored
@@ -1,4 +0,0 @@
|
||||
monitoring-plugin (0.0.1) precise; urgency=low
|
||||
|
||||
* Initial Package creation
|
||||
|
||||
1
debian/compat
vendored
1
debian/compat
vendored
@@ -1 +0,0 @@
|
||||
7
|
||||
15
debian/control
vendored
15
debian/control
vendored
@@ -1,15 +0,0 @@
|
||||
Source: monitoring
|
||||
Section: python
|
||||
Priority: optional
|
||||
Maintainer: HPCloud Monitoring <hpcs-mon@hp.com>
|
||||
Build-Depends: debhelper (>= 7),
|
||||
python (>= 2.6.6-3~),
|
||||
python-setuptools
|
||||
Standards-Version: 3.9.3
|
||||
X-Python-Version: >= 2.6
|
||||
|
||||
Package: monitoring-plugin
|
||||
Architecture: all
|
||||
Section: python
|
||||
Depends: ${misc:Depends}, ${python:Depends}, libpython2.7, python-pkg-resources, python-pbr, python-monclient
|
||||
Description:Monitoring Plugin for Horizon
|
||||
4
debian/copyright
vendored
4
debian/copyright
vendored
@@ -1,4 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Files: *
|
||||
Copyright: 2014, HP
|
||||
License: Proprietary
|
||||
4
debian/rules
vendored
4
debian/rules
vendored
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --with python2
|
||||
@@ -1,99 +0,0 @@
|
||||
/* global _ */
|
||||
|
||||
/*
|
||||
* Complex scripted dashboard
|
||||
* This script generates a dashboard object that Grafana can load. It also takes a number of user
|
||||
* supplied URL parameters (in the ARGS variable)
|
||||
*
|
||||
* Return a dashboard object, or a function
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// accessible variables in this scope
|
||||
var window, document, ARGS, $, jQuery, moment, kbn;
|
||||
|
||||
// Setup some variables
|
||||
var dashboard;
|
||||
|
||||
// All url parameters are available via the ARGS object
|
||||
var ARGS;
|
||||
|
||||
// Setup the metric dimensions.
|
||||
var dimensions = [];
|
||||
|
||||
for (var key in ARGS) {
|
||||
var isDimParam = key.startsWith("dim_");
|
||||
if (isDimParam) {
|
||||
var value = ARGS[key];
|
||||
var dim = {
|
||||
"key": key.substring(4),
|
||||
"value": value
|
||||
};
|
||||
dimensions.push(dim);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Intialize a skeleton with nothing but a rows array and service object
|
||||
dashboard = {
|
||||
rows : [],
|
||||
};
|
||||
|
||||
// Set a title
|
||||
dashboard.title = 'Alarm drilldown';
|
||||
|
||||
// Set default time
|
||||
// time can be overridden in the url using from/to parameters, but this is
|
||||
// handled automatically in grafana core during dashboard initialization
|
||||
dashboard.time = {
|
||||
from: "now-6h",
|
||||
to: "now"
|
||||
};
|
||||
|
||||
var rows = 1;
|
||||
var metricName = '';
|
||||
var hostname = '';
|
||||
|
||||
if(!_.isUndefined(ARGS.rows)) {
|
||||
rows = parseInt(ARGS.rows, 10);
|
||||
}
|
||||
|
||||
if(!_.isUndefined(ARGS.metric)) {
|
||||
metricName = ARGS.metric;
|
||||
}
|
||||
|
||||
if(!_.isUndefined(ARGS.hostname)) {
|
||||
hostname = ARGS.hostname;
|
||||
}
|
||||
|
||||
for (var i = 0; i < rows; i++) {
|
||||
|
||||
dashboard.rows.push({
|
||||
title: 'Chart',
|
||||
height: '300px',
|
||||
panels: [
|
||||
{
|
||||
title: metricName,
|
||||
type: 'graph',
|
||||
span: 12,
|
||||
fill: 1,
|
||||
linewidth: 2,
|
||||
targets: [
|
||||
{
|
||||
"aggregator": "avg",
|
||||
"alias": hostname,
|
||||
"dimensions": dimensions,
|
||||
"metric": metricName,
|
||||
"period": "300",
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
shared: true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
return dashboard;
|
||||
23
manage.py
23
manage.py
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
||||
"monitoring.test.settings")
|
||||
execute_from_command_line(sys.argv)
|
||||
@@ -1,17 +0,0 @@
|
||||
# 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.
|
||||
|
||||
|
||||
URL_PREFIX = 'horizon:monitoring:alarmdefs:'
|
||||
TEMPLATE_PREFIX = 'monitoring/alarmdefs/'
|
||||
@@ -1,279 +0,0 @@
|
||||
# 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.encoding import force_str
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
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', force_str(_('min'))), ('max', force_str(_('max'))),
|
||||
('sum', force_str(_('sum'))), ('count', force_str(_('count'))),
|
||||
('avg', force_str(_('avg'))), ('last', force_str(_('last')))]
|
||||
)
|
||||
comparators = [['>', '>'], ['>=', '>='], ['<', '<'], ['<=', '<=']]
|
||||
operators = json.dumps(
|
||||
[('AND', force_str(_('AND'))), ('OR', force_str(_('OR')))]
|
||||
)
|
||||
|
||||
def __init__(self, initial, attrs=None):
|
||||
super(ExpressionWidget, self).__init__(attrs)
|
||||
self.initial = initial
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=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, renderer=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=(), renderer=None):
|
||||
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:
|
||||
notifications = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve notifications.'))
|
||||
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:
|
||||
exceptions.handle(request,
|
||||
_('Unable to update alarm definition.'))
|
||||
return False
|
||||
return True
|
||||
@@ -1,27 +0,0 @@
|
||||
# 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.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from monitoring import dashboard
|
||||
|
||||
|
||||
class AlarmDefinitions(horizon.Panel):
|
||||
name = _("Alarm Definitions")
|
||||
slug = 'alarmdefs'
|
||||
|
||||
|
||||
dashboard.Monitoring.register(AlarmDefinitions)
|
||||
@@ -1,110 +0,0 @@
|
||||
# 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.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from monitoring.alarmdefs import constants
|
||||
from monitoring import api
|
||||
|
||||
|
||||
class CreateAlarm(tables.LinkAction):
|
||||
name = "create_alarm"
|
||||
verbose_name = _("Create Alarm Definition")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
icon = "plus"
|
||||
policy_rules = (("alarm", "alarm:create"),)
|
||||
ajax = True
|
||||
|
||||
def get_link_url(self):
|
||||
return reverse(constants.URL_PREFIX + 'alarm_create', args=())
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class EditAlarm(tables.LinkAction):
|
||||
name = "edit_alarm"
|
||||
verbose_name = _("Edit Alarm Definition")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
|
||||
def get_link_url(self, datum):
|
||||
return reverse(constants.URL_PREFIX + 'alarm_edit', args=(datum['id'], ))
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class DeleteAlarm(tables.DeleteAction):
|
||||
name = "delete_alarm"
|
||||
verbose_name = _("Delete Alarm Definition")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
"Delete Alarm Definition",
|
||||
"Delete Alarm Definitions",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
"Deleted Alarm Definition",
|
||||
"Deleted Alarm Definitions",
|
||||
count
|
||||
)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
api.monitor.alarmdef_delete(request, obj_id)
|
||||
|
||||
|
||||
class AlarmsFilterAction(tables.FilterAction):
|
||||
def filter(self, table, alarms, filter_string):
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [alarm for alarm in alarms
|
||||
if q in alarm['name'].lower()]
|
||||
|
||||
|
||||
class AlarmsTable(tables.DataTable):
|
||||
target = tables.Column('name', verbose_name=_('Name'),
|
||||
link=constants.URL_PREFIX + 'alarm_detail',
|
||||
)
|
||||
description = tables.Column('description', verbose_name=_('Description'))
|
||||
enabled = tables.Column('actions_enabled',
|
||||
verbose_name=_('Notifications Enabled'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return obj['id']
|
||||
|
||||
def get_object_display(self, obj):
|
||||
return obj['name']
|
||||
|
||||
class Meta(object):
|
||||
name = "alarms"
|
||||
verbose_name = _("Alarm Definitions")
|
||||
row_actions = (EditAlarm,
|
||||
DeleteAlarm,
|
||||
)
|
||||
table_actions = (CreateAlarm,
|
||||
AlarmsFilterAction,
|
||||
DeleteAlarm,
|
||||
)
|
||||
@@ -1,23 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block help_message %}
|
||||
<div class="clearfix center-block">
|
||||
<p class="text-info">
|
||||
{% blocktrans %}
|
||||
The Name field is used to identify the alarm definition.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="text-info">
|
||||
{% blocktrans %}
|
||||
The Description field can be used to describe alarm definition's
|
||||
purpose.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="text-info">
|
||||
{% blocktrans %}
|
||||
The Severity field allows to specify the importance of alarm
|
||||
definition.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,19 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block help_message %}
|
||||
<div class="clearfix">
|
||||
<p class="text-info">
|
||||
{% blocktrans %}
|
||||
Each alarm definition is defined by its expression composed out of
|
||||
mathematical function, metric, time, times ,threshold and comparator for
|
||||
metric's value and the threshold. Time is the number of seconds for the measurement
|
||||
to be done. They can only be in a multiple of 60. Times is how many times in a row
|
||||
that expression must be true before triggering the alarm. Both time and times are
|
||||
optional and default to 60 and 1 respectively. Additionally it is possible to narrow
|
||||
evaluation of the alarm to certain entities by choosing their
|
||||
dimensions. The deterministic alarms never enter UNDETERMINED state.
|
||||
Use them for metrics that are received sporadically.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,18 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block help_message %}
|
||||
<div class="clearfix">
|
||||
<p class="text-info">
|
||||
{% blocktrans %}
|
||||
The Notifications field contains the list of Notifications that should
|
||||
be sent when transitioning to another state.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="text-info">
|
||||
{% blocktrans %}
|
||||
If for some transitions notifications should not be sent they can be
|
||||
disabled.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,47 +0,0 @@
|
||||
{% load i18n sizeformat %}
|
||||
|
||||
<div class="info detail">
|
||||
<h4>{% trans "Info" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ alarm.name|default:_("None") }}</dd>
|
||||
<dt>{% trans "Description" %}</dt>
|
||||
<dd>{{ alarm.description }}</dd>
|
||||
<dt>{% trans "Expression" %}</dt>
|
||||
<dd>{{ alarm.expression }}</dd>
|
||||
<dt>{% trans "Deterministic" %}</dt>
|
||||
<dd>{{ alarm.deterministic }}</dd>
|
||||
<dt>{% trans "Severity" %}</dt>
|
||||
<dd>{{ alarm.severity }}</dd>
|
||||
<dt>{% trans "Notifications Enabled" %}</dt>
|
||||
<dd>{{ alarm.actions_enabled }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="specs detail">
|
||||
<h4>{% trans "Notifications" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Type" %}</th>
|
||||
<th>{% trans "Address" %}</th>
|
||||
<th>{% trans "Alarm" %}</th>
|
||||
<th>{% trans "OK" %}</th>
|
||||
<th>{% trans "Undetermined" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for row in alarm.notifications %}
|
||||
<tr>
|
||||
<td>{{ row.name }}</td>
|
||||
<td>{{ row.type }}</td>
|
||||
<td>{{ row.address }}</td>
|
||||
<td>{{ row.alarm }}</td>
|
||||
<td>{{ row.ok }}</td>
|
||||
<td>{{ row.undetermined }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}edit_alarm_form{% endblock %}
|
||||
{% block form_action %}{{ action_url }}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Edit Alarm Definition" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<h3>{% trans "Description" %}:</h3>
|
||||
<p>{% blocktrans %}
|
||||
The Name field is used to identify the alarm definition.
|
||||
{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}
|
||||
The Expression field which if true, triggers a notification to be sent.
|
||||
See <a href="https://github.com/openstack/monasca-api/blob/master/docs/monasca-api-spec.md#alarm-definition-expressions" target="_blank">Alarm Expressions</a> for how to write an expression.
|
||||
{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}
|
||||
The Notifications field contains the list of Notifications that should be sent when transitioning to another ALARM state.
|
||||
{% endblocktrans %}</p>
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{{ cancel_url }}" class="btn btn-default cancel">{% trans "Cancel" %}</a>
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Save" %}" />
|
||||
{% endblock %}
|
||||
@@ -1,35 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load compress %}
|
||||
|
||||
{% block title %}{% trans 'Alarm Definitions' %}{% endblock %}
|
||||
{% block css %}
|
||||
{% include "_stylesheets.html" %}
|
||||
<link href='{{ STATIC_URL }}monitoring/css/ng-tags-input.css' type="text/css" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item-truncate">{% trans "Monitoring" %}</li>
|
||||
<li class="breadcrumb-item-truncate active">{% trans "Alarm Definitions" %}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Alarm Definitions") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if prev_page_offset != None %}
|
||||
<a href="?page_offset={{ prev_page_offset }}" class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Previous Page' %}</a>
|
||||
{% endif %}
|
||||
{% if page_offset %}
|
||||
<a href="?page_offset={{ page_offset}}" class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Next Page' %}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,23 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Alarm Definition Details' %}{% endblock %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item-truncate">{% trans "Monitoring" %}</li>
|
||||
<li class="breadcrumb-item-truncate"><a href="/dashboard/monitoring/alarmdefs/">{% trans "Alarm Definitions" %}</a></li>
|
||||
<li class="breadcrumb-item-truncate active">{{ alarm.name|default:_("None") }}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Alarm Definition Details") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% include 'monitoring/alarmdefs/_detail.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Edit Alarm Definitions' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title="Edit Alarm Definitions" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'monitoring/alarmdefs/_edit.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,4 +0,0 @@
|
||||
<mon-alarm-expression metrics="{{ metrics|default:'[]' }}"
|
||||
functions="{{ func }}"
|
||||
comparators="{{ comparators }}"
|
||||
operators="{{ operators }}"></mon-alarm-expression>
|
||||
@@ -1,15 +0,0 @@
|
||||
{% load i18n %}
|
||||
<div ng-controller="alarmMatchByController as ctrl">
|
||||
<input type="hidden" name="{{ name }}" id="id_{{ name }}"/>
|
||||
<tags-input id="dimkey-chooser"
|
||||
ng-model="ctrl.matchByTags"
|
||||
placeholder="{% trans 'Add a match by' %}"
|
||||
add-from-autocomplete-only="true"
|
||||
on-tag-added="ctrl.saveDimKey()"
|
||||
on-tag-removed="ctrl.saveDimKey()">
|
||||
<auto-complete source="ctrl.possibleDimKeys($query)"
|
||||
max-results-to-show="30"
|
||||
min-length="1">
|
||||
</auto-complete>
|
||||
</tags-input>
|
||||
</div>
|
||||
@@ -1,51 +0,0 @@
|
||||
{% load i18n %}
|
||||
<div ng-controller="alarmNotificationFieldController as ctrl" ng-init="ctrl.init('{{ data }}')">
|
||||
|
||||
<div ng-if="ctrl.empty">
|
||||
{% trans 'No notifications available.' %} (<a href="{% url 'horizon:monitoring:notifications:index' %}">{% trans 'Add' %}</a>)
|
||||
</div>
|
||||
|
||||
<table ng-if="ctrl.list.length" class="table table-condensed" id="notification_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="name">{% trans 'Name' %}</th>
|
||||
<th class="type">{% trans 'Type' %}</th>
|
||||
<th class="address">{% trans 'Address' %}</th>
|
||||
<th class="alarm">{% trans 'Alarm' %}</th>
|
||||
<th class="ok">{% trans 'OK' %}</th>
|
||||
<th class="undetermined">{% trans 'Undetermined' %}</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="notify in ctrl.list">
|
||||
<td class="name">{$ notify.name $}</td>
|
||||
<td class="type">{$ notify.type $}</td>
|
||||
<td class="address">{$ notify.address $}</td>
|
||||
<td class="alarm">
|
||||
<input type="checkbox" ng-model="notify.alarm">
|
||||
<input type="hidden" name="alarm_actions" value="{$ notify.id $}" ng-if="notify.alarm"/>
|
||||
</td>
|
||||
<td class="ok">
|
||||
<input type="checkbox" ng-model="notify.ok">
|
||||
<input type="hidden" name="ok_actions" value="{$ notify.id $}" ng-if="notify.ok"/>
|
||||
</td>
|
||||
<td class="undetermined">
|
||||
<input type="checkbox" ng-model="notify.undetermined" ng-disabled="ctrl.isDeterministic">
|
||||
<input type="hidden" name="undetermined_actions" value="{$ notify.id $}" ng-if="notify.undetermined"/>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" title="{% trans 'Remove' %}" ng-click="ctrl.remove(notify.id)">X</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div ng-if="ctrl.select.options.length">
|
||||
<select ng-model="ctrl.select.model" ng-options="item.id as item.label for item in ctrl.select.options">
|
||||
<option value="">{% trans "Select Notification" %}</option>
|
||||
</select>
|
||||
<a href="#" title="{% trans 'Add' %}" class="btn" ng-click="ctrl.add()">{% trans 'Add' %}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,15 +0,0 @@
|
||||
<noscript><h3>{{ step }}</h3></noscript>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- hide if window gets very small to save some space -->
|
||||
<div class="col-sm-12 col-md-12 col-lg-12 hidden-xs">
|
||||
{{ step.get_help_text }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,114 +0,0 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2016-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 django.urls import reverse
|
||||
from unittest.mock import patch
|
||||
|
||||
from monitoring.alarmdefs import constants
|
||||
from monitoring.alarmdefs import views
|
||||
from monitoring.alarmdefs import workflows
|
||||
from monitoring.test import helpers
|
||||
|
||||
|
||||
INDEX_URL = reverse(constants.URL_PREFIX + 'index')
|
||||
CREATE_URL = reverse(constants.URL_PREFIX + 'alarm_create', args=())
|
||||
DETAIL_URL = reverse(constants.URL_PREFIX + 'alarm_detail', args=('12345',))
|
||||
EDIT_URL = reverse(constants.URL_PREFIX + 'alarm_edit', args=('12345',))
|
||||
|
||||
|
||||
class AlarmDefinitionsTest(helpers.TestCase):
|
||||
def test_alarmdefs_get(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['alarmdef_list'],
|
||||
'alarmdef_list.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertEqual(mock.alarmdef_list.call_count, 2)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/alarmdefs/alarm.html')
|
||||
|
||||
def test_alarmdefs_create(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['notification_list', 'metrics_list'],
|
||||
'notification_list.return_value': [],
|
||||
'metrics_list.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(CREATE_URL)
|
||||
self.assertEqual(mock.notification_list.call_count, 1)
|
||||
self.assertEqual(mock.metrics_list.call_count, 1)
|
||||
|
||||
workflow = res.context['workflow']
|
||||
self.assertTemplateUsed(res, views.AlarmCreateView.template_name)
|
||||
self.assertEqual(res.context['workflow'].name,
|
||||
workflows.AlarmDefinitionWorkflow.name)
|
||||
|
||||
self.assertEqual(
|
||||
[repr(step) for step in workflow.steps],
|
||||
['<SetDetailsStep: setalarmdefinitionaction>',
|
||||
'<SetExpressionStep: setalarmdefinitionexpressionaction>',
|
||||
'<SetNotificationsStep: setalarmnotificationsaction>'])
|
||||
|
||||
# verify steps
|
||||
step = workflow.get_step('setalarmdefinitionaction')
|
||||
self.assertIsNotNone(step)
|
||||
|
||||
step = workflow.get_step('setalarmdefinitionexpressionaction')
|
||||
self.assertIsNotNone(step)
|
||||
|
||||
step = workflow.get_step('setalarmnotificationsaction')
|
||||
self.assertIsNotNone(step)
|
||||
|
||||
self.assertContains(res, '<select class="form-control" '
|
||||
'id="id_severity"')
|
||||
|
||||
self.assertContains(res, '<mon-alarm-expression')
|
||||
|
||||
self.assertContains(res, '<input type="hidden" name="alarm_actions"')
|
||||
self.assertContains(res, '<input type="hidden" name="ok_actions"')
|
||||
self.assertContains(res, '<input type="hidden" '
|
||||
'name="undetermined_actions"')
|
||||
|
||||
def test_alarmdefs_detail(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['alarmdef_get'],
|
||||
'alarmdef_get.return_value': {
|
||||
'alarm_actions': [],
|
||||
'ok_actions': [],
|
||||
'undetermined_actions': [],
|
||||
'match_by': [],
|
||||
}
|
||||
}) as mock:
|
||||
res = self.client.get(DETAIL_URL)
|
||||
self.assertEqual(mock.alarmdef_get.call_count, 1)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/alarmdefs/_detail.html')
|
||||
|
||||
def test_alarmdefs_edit(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['alarmdef_get'],
|
||||
'alarmdef_get.return_value': {
|
||||
'alarm_actions': [],
|
||||
'ok_actions': [],
|
||||
'undetermined_actions': [],
|
||||
'match_by': [],
|
||||
}
|
||||
}) as mock:
|
||||
res = self.client.get(EDIT_URL)
|
||||
self.assertEqual(mock.alarmdef_get.call_count, 1)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/alarmdefs/_edit.html')
|
||||
@@ -1,29 +0,0 @@
|
||||
# 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.urls import re_path
|
||||
|
||||
from monitoring.alarmdefs import views
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', views.IndexView.as_view(), name='index'),
|
||||
re_path(r'^alarm/create$',
|
||||
views.AlarmCreateView.as_view(),
|
||||
name='alarm_create'),
|
||||
re_path(r'^(?P<id>[^/]+)/alarm_detail/$',
|
||||
views.AlarmDetailView.as_view(),
|
||||
name='alarm_detail'),
|
||||
re_path(r'^alarm/(?P<id>[^/]+)/alarm_edit/$',
|
||||
views.AlarmEditView.as_view(),
|
||||
name='alarm_edit')
|
||||
]
|
||||
@@ -1,264 +0,0 @@
|
||||
# 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.contrib import messages
|
||||
from django.core.paginator import EmptyPage
|
||||
from django.core.paginator import Paginator
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon.utils import functions as utils
|
||||
from horizon import workflows
|
||||
|
||||
from monascaclient import exc
|
||||
from monitoring.alarmdefs import constants
|
||||
from monitoring.alarmdefs import forms as alarm_forms
|
||||
from monitoring.alarmdefs import tables as alarm_tables
|
||||
from monitoring.alarmdefs import workflows as alarm_workflows
|
||||
from monitoring import api
|
||||
|
||||
from openstack_dashboard import policy
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
PREV_PAGE_LIMIT = 100
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = alarm_tables.AlarmsTable
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarm.html'
|
||||
|
||||
def get_data(self):
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
results = []
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
limit = utils.get_page_size(self.request)
|
||||
try:
|
||||
results = api.monitor.alarmdef_list(self.request, page_offset, limit)
|
||||
paginator = Paginator(results, limit)
|
||||
results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
results = paginator.page(paginator.num_pages)
|
||||
except Exception as ex:
|
||||
LOG.exception(str(ex))
|
||||
messages.error(self.request, _("Could not retrieve alarm definitions"))
|
||||
|
||||
return results
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(IndexView, self).get_context_data(**kwargs)
|
||||
num_results = 0
|
||||
contacts = []
|
||||
prev_page_stack = []
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
|
||||
if 'prev_page_stack' in self.request.session:
|
||||
prev_page_stack = self.request.session['prev_page_stack']
|
||||
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
prev_page_stack = []
|
||||
else:
|
||||
page_offset = int(page_offset)
|
||||
|
||||
limit = utils.get_page_size(self.request)
|
||||
try:
|
||||
# To judge whether there is next page, get limit + 1
|
||||
results = api.monitor.alarmdef_list(self.request, page_offset,
|
||||
limit + 1)
|
||||
num_results = len(results)
|
||||
paginator = Paginator(results, limit)
|
||||
contacts = paginator.page(1)
|
||||
except EmptyPage:
|
||||
contacts = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarm definitions"))
|
||||
return context
|
||||
|
||||
context["contacts"] = contacts
|
||||
|
||||
if num_results < limit + 1:
|
||||
context["page_offset"] = None
|
||||
else:
|
||||
context["page_offset"] = page_offset + limit
|
||||
|
||||
if page_offset in prev_page_stack:
|
||||
index = prev_page_stack.index(page_offset)
|
||||
prev_page_stack = prev_page_stack[0:index]
|
||||
|
||||
prev_page_offset = prev_page_stack[-1] if prev_page_stack else None
|
||||
if prev_page_offset is not None:
|
||||
context["prev_page_offset"] = prev_page_offset
|
||||
|
||||
if len(prev_page_stack) > PREV_PAGE_LIMIT:
|
||||
del prev_page_stack[0]
|
||||
prev_page_stack.append(page_offset)
|
||||
self.request.session['prev_page_stack'] = prev_page_stack
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class AlarmCreateView(workflows.WorkflowView):
|
||||
workflow_class = alarm_workflows.AlarmDefinitionWorkflow
|
||||
|
||||
|
||||
def transform_alarm_data(obj):
|
||||
obj['match_by'] = ','.join(obj['match_by'])
|
||||
return obj
|
||||
|
||||
|
||||
class AlarmDetailView(TemplateView):
|
||||
template_name = constants.TEMPLATE_PREFIX + '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.alarmdef_get(self.request, id)
|
||||
notifications = []
|
||||
# Fetch the notification object for each alarm_actions
|
||||
all_actions = set(self._object["alarm_actions"] +
|
||||
self._object["ok_actions"] +
|
||||
self._object["undetermined_actions"])
|
||||
for id in all_actions:
|
||||
try:
|
||||
notification = api.monitor.notification_get(
|
||||
self.request,
|
||||
id)
|
||||
notification['alarm'] = False
|
||||
notification['ok'] = False
|
||||
notification['undetermined'] = False
|
||||
notifications.append(notification)
|
||||
# except exceptions.NOT_FOUND:
|
||||
except exc.HttpError:
|
||||
msg = _("Notification %s has already been deleted.") % id
|
||||
notifications.append({"id": id,
|
||||
"name": str(msg),
|
||||
"type": "",
|
||||
"address": ""})
|
||||
|
||||
for notification in notifications:
|
||||
if notification['id'] in self._object["alarm_actions"]:
|
||||
notification['alarm'] = True
|
||||
if notification['id'] in self._object["ok_actions"]:
|
||||
notification['ok'] = True
|
||||
if notification['id'] in self._object["undetermined_actions"]:
|
||||
notification['undetermined'] = True
|
||||
|
||||
self._object["notifications"] = notifications
|
||||
return self._object
|
||||
except Exception:
|
||||
redirect = self.get_success_url()
|
||||
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):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(AlarmDetailView, self).get_context_data(**kwargs)
|
||||
self.get_initial()
|
||||
context["alarm"] = self.alarm
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy(constants.URL_PREFIX + 'index')
|
||||
|
||||
|
||||
class AlarmEditView(forms.ModalFormView):
|
||||
form_class = alarm_forms.EditAlarmForm
|
||||
template_name = constants.TEMPLATE_PREFIX + 'edit.html'
|
||||
|
||||
def get_object(self):
|
||||
id = self.kwargs['id']
|
||||
try:
|
||||
if hasattr(self, "_object"):
|
||||
return self._object
|
||||
self._object = None
|
||||
self._object = api.monitor.alarmdef_get(self.request, id)
|
||||
notifications = []
|
||||
# Fetch the notification object for each alarm_actions
|
||||
all_actions = set(self._object["alarm_actions"] +
|
||||
self._object["ok_actions"] +
|
||||
self._object["undetermined_actions"])
|
||||
for id in all_actions:
|
||||
try:
|
||||
notification = api.monitor.notification_get(
|
||||
self.request,
|
||||
id)
|
||||
notification['alarm'] = False
|
||||
notification['ok'] = False
|
||||
notification['undetermined'] = False
|
||||
notifications.append(notification)
|
||||
# except exceptions.NOT_FOUND:
|
||||
except exc.HttpError:
|
||||
msg = _("Notification %s has already been deleted.") % id
|
||||
messages.warning(self.request, msg)
|
||||
|
||||
for notification in notifications:
|
||||
if notification['id'] in self._object["alarm_actions"]:
|
||||
notification['alarm'] = True
|
||||
if notification['id'] in self._object["ok_actions"]:
|
||||
notification['ok'] = True
|
||||
if notification['id'] in self._object["undetermined_actions"]:
|
||||
notification['undetermined'] = True
|
||||
|
||||
del self._object["alarm_actions"]
|
||||
del self._object["ok_actions"]
|
||||
del self._object["undetermined_actions"]
|
||||
|
||||
self._object["notifications"] = notifications
|
||||
return self._object
|
||||
except Exception:
|
||||
redirect = self.get_success_url()
|
||||
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):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
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=(id,))
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy(constants.URL_PREFIX + 'index',
|
||||
args=())
|
||||
@@ -1,230 +0,0 @@
|
||||
# Copyright 2016 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 django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.debug import sensitive_variables
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon.utils import memoized
|
||||
from horizon import workflows
|
||||
|
||||
from monitoring.alarmdefs import constants
|
||||
from monitoring.alarmdefs import forms as ad_forms
|
||||
from monitoring import api
|
||||
|
||||
|
||||
class SetAlarmNotificationsAction(workflows.Action):
|
||||
notifications = ad_forms.NotificationField(
|
||||
label=_('Notifications'),
|
||||
required=False,
|
||||
widget=ad_forms.NotificationCreateWidget(),
|
||||
help_text=_('Notification methods. '
|
||||
'Notifications can be sent when an alarm '
|
||||
'state transition occurs.'))
|
||||
|
||||
alarm_actions = ad_forms.NotificationField(
|
||||
label=_("Alarm Actions"),
|
||||
required=False,
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
ok_actions = ad_forms.NotificationField(
|
||||
label=_("OK Actions"),
|
||||
required=False,
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
undetermined_actions = ad_forms.NotificationField(
|
||||
label=_("Undetermined Actions"),
|
||||
required=False,
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
name = _('Notifications')
|
||||
help_text_template = ("monitoring/alarmdefs/"
|
||||
"_create_ad_notification_help.html")
|
||||
|
||||
def __init__(self, request, context, *args, **kwargs):
|
||||
super(SetAlarmNotificationsAction, self).__init__(
|
||||
request, context, *args, **kwargs
|
||||
)
|
||||
try:
|
||||
notifications = ad_forms._get_notifications(request)
|
||||
self.fields['notifications'].choices = notifications
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve notifications.'))
|
||||
|
||||
|
||||
_SEVERITY_CHOICES = [("LOW", _("Low")),
|
||||
("MEDIUM", _("Medium")),
|
||||
("HIGH", _("High")),
|
||||
("CRITICAL", _("Critical"))]
|
||||
|
||||
|
||||
class SetAlarmDefinitionAction(workflows.Action):
|
||||
name = forms.CharField(label=_('Name'),
|
||||
required=True,
|
||||
max_length=250,
|
||||
help_text=_('An unique name of the alarm.'))
|
||||
|
||||
description = forms.CharField(label=_('Description'),
|
||||
required=False,
|
||||
help_text=_('A description of an alarm.'))
|
||||
|
||||
severity = forms.ChoiceField(label=_('Severity'),
|
||||
choices=_SEVERITY_CHOICES,
|
||||
initial=_SEVERITY_CHOICES[0],
|
||||
widget=forms.SelectWidget,
|
||||
required=False,
|
||||
help_text=_('Severity of an alarm. Must be '
|
||||
'either LOW, MEDIUM, HIGH '
|
||||
'or CRITICAL. Default is LOW.'))
|
||||
|
||||
class Meta(object):
|
||||
name = _('Details')
|
||||
help_text_template = ("monitoring/alarmdefs/"
|
||||
"_create_ad_details_help.html")
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SetAlarmDefinitionAction, self).clean()
|
||||
|
||||
alarm_def_name = cleaned_data.get('name', '').strip()
|
||||
if not alarm_def_name:
|
||||
return
|
||||
is_name_valid = self._is_alarm_def_name_unique_validator(
|
||||
alarm_def_name)
|
||||
if not is_name_valid:
|
||||
self.add_error('name',
|
||||
_('Alarm definition with %s name already exists')
|
||||
% alarm_def_name)
|
||||
|
||||
def _is_alarm_def_name_unique_validator(self, value):
|
||||
try:
|
||||
ret = self._get_alarm_def_by_name(value)
|
||||
return not (ret and len(ret))
|
||||
except Exception:
|
||||
exceptions.handle(request=self.request,
|
||||
message=_('Failed to validate name'),
|
||||
ignore=True)
|
||||
return True
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_alarm_def_by_name(self, value):
|
||||
return api.monitor.alarmdef_get_by_name(self.request, value)
|
||||
|
||||
|
||||
class SetAlarmDefinitionExpressionAction(workflows.Action):
|
||||
expression = ad_forms.ExpressionField(label=_("Expression"),
|
||||
required=True,
|
||||
widget=ad_forms.ExpressionWidget(''),
|
||||
help_text=_(
|
||||
'An alarm expression.'))
|
||||
|
||||
match_by = forms.CharField(label=_('Match by'),
|
||||
required=False,
|
||||
widget=ad_forms.MatchByWidget(''),
|
||||
help_text=_('The metric dimensions used '
|
||||
'to create unique alarms.'))
|
||||
|
||||
class Meta(object):
|
||||
name = _('Expression')
|
||||
help_text_template = ("monitoring/alarmdefs/"
|
||||
"_create_ad_expression_help.html")
|
||||
|
||||
def __init__(self, request, context, *args, **kwargs):
|
||||
super(SetAlarmDefinitionExpressionAction, self).__init__(request,
|
||||
context,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
try:
|
||||
self.fields['expression'].metrics = ad_forms._get_metrics(request)
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Unable to retrieve metrics'))
|
||||
|
||||
|
||||
class SetDetailsStep(workflows.Step):
|
||||
action_class = SetAlarmDefinitionAction
|
||||
contributes = ('name', 'description', 'severity')
|
||||
template_name = 'monitoring/alarmdefs/workflow_step.html'
|
||||
|
||||
|
||||
class SetExpressionStep(workflows.Step):
|
||||
action_class = SetAlarmDefinitionExpressionAction
|
||||
contributes = ('expression', 'match_by')
|
||||
template_name = 'monitoring/alarmdefs/workflow_step.html'
|
||||
|
||||
def contribute(self, data, context):
|
||||
context = (super(SetExpressionStep, self)
|
||||
.contribute(data, context))
|
||||
|
||||
if 'expression' in data and data['expression']:
|
||||
context['expression'] = data['expression'].strip()
|
||||
|
||||
if 'match_by' in data and data['match_by']:
|
||||
context['match_by'] = context['match_by'].split(',')
|
||||
else:
|
||||
context['match_by'] = []
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class SetNotificationsStep(workflows.Step):
|
||||
action_class = SetAlarmNotificationsAction
|
||||
contributes = ('alarm_actions', 'ok_actions', 'undetermined_actions')
|
||||
template_name = 'monitoring/alarmdefs/workflow_step.html'
|
||||
|
||||
|
||||
class AlarmDefinitionWorkflow(workflows.Workflow):
|
||||
slug = 'create_alarm_definition'
|
||||
name = _('Create Alarm Definition')
|
||||
finalize_button_name = _('Create Alarm Definition')
|
||||
success_message = _('Alarm definition %s has been created')
|
||||
failure_message = _('Unable to create alarm definition %s')
|
||||
success_url = constants.URL_PREFIX + 'index'
|
||||
wizard = True
|
||||
default_steps = (
|
||||
SetDetailsStep,
|
||||
SetExpressionStep,
|
||||
SetNotificationsStep
|
||||
)
|
||||
|
||||
def format_status_message(self, message):
|
||||
name = self.context.get('name', _('Unknown name'))
|
||||
return message % name
|
||||
|
||||
@sensitive_variables('alarm_actions',
|
||||
'ok_actions',
|
||||
'undetermined_actions')
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
api.monitor.alarmdef_create(
|
||||
request,
|
||||
name=context['name'],
|
||||
expression=context['expression'],
|
||||
description=context['description'],
|
||||
severity=context['severity'],
|
||||
match_by=context['match_by'],
|
||||
alarm_actions=context['alarm_actions'],
|
||||
ok_actions=context['ok_actions'],
|
||||
undetermined_actions=context['undetermined_actions'],
|
||||
)
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to create alarm definition.'),
|
||||
escalate=True)
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -1,25 +0,0 @@
|
||||
# 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.conf import settings
|
||||
|
||||
URL_PREFIX = 'horizon:monitoring:alarms:'
|
||||
TEMPLATE_PREFIX = 'monitoring/alarms/'
|
||||
|
||||
prefix = settings.STATIC_URL or ''
|
||||
CRITICAL_ICON = prefix + 'monitoring/img/critical-icon.png'
|
||||
WARNING_ICON = prefix + 'monitoring/img/warning-icon.png'
|
||||
OK_ICON = prefix + 'monitoring/img/ok-icon.png'
|
||||
UNKNOWN_ICON = prefix + 'monitoring/img/unknown-icon.png'
|
||||
NOTFOUND_ICON = prefix + 'monitoring/img/notfound-icon.png'
|
||||
@@ -1,303 +0,0 @@
|
||||
# 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.
|
||||
|
||||
import re
|
||||
|
||||
from django import forms as django_forms
|
||||
from django.template.loader import get_template
|
||||
from django.utils import html
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from monitoring.alarms import constants
|
||||
from monitoring import api
|
||||
|
||||
|
||||
class ExpressionWidget(forms.Widget):
|
||||
def __init__(self, initial, attrs):
|
||||
super(ExpressionWidget, self).__init__(attrs)
|
||||
self.initial = initial
|
||||
|
||||
def render(self, name, value, attrs):
|
||||
final_attrs = self.build_attrs(attrs, {'name': name})
|
||||
if value:
|
||||
dim = value
|
||||
else:
|
||||
if 'all' in self.initial['service']:
|
||||
dim = ''
|
||||
else:
|
||||
dim = next(("%s=%s" % (k, v) for k, v in self.initial.items()), '')
|
||||
t = get_template(constants.TEMPLATE_PREFIX + 'expression_field.html')
|
||||
local_attrs = {'service': dim}
|
||||
local_attrs.update(final_attrs)
|
||||
|
||||
return t.render(local_attrs)
|
||||
|
||||
|
||||
class SimpleExpressionWidget(django_forms.MultiWidget):
|
||||
def __init__(self, initial, attrs=None):
|
||||
comparators = [('>', '>'), ('>=', '>='), ('<', '<'), ('<=', '<=')]
|
||||
func = [('min', _('min')), ('max', _('max')), ('sum', _('sum')),
|
||||
('count', _('count')), ('avg', _('avg'))]
|
||||
_widgets = (
|
||||
django_forms.widgets.Select(attrs=attrs, choices=func),
|
||||
ExpressionWidget(initial, attrs={}),
|
||||
django_forms.widgets.Select(attrs=attrs, choices=comparators),
|
||||
django_forms.widgets.TextInput(),
|
||||
)
|
||||
super(SimpleExpressionWidget, self).__init__(_widgets, attrs)
|
||||
|
||||
def decompress(self, expr):
|
||||
if expr:
|
||||
return re.search(r'^(\w+)\((.*)\) ([<>=]*) (.*)$', expr).groups()
|
||||
else:
|
||||
return [None, 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 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=()):
|
||||
output = '<table id="notification_table" ' + \
|
||||
'class="table table-condensed">'
|
||||
output += '<thead><tr><th>%s</th></tr></thead>' % \
|
||||
str(_("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['id']])
|
||||
if options:
|
||||
output += options
|
||||
output += '</select>'
|
||||
output += '<td><a href="" id="remove_notif_button">X</a></td>'
|
||||
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><a href="" id="remove_notif_button">X</a></td>'
|
||||
output += '</td></tr>'
|
||||
output += '</table>'
|
||||
label = str(_("+ Add more"))
|
||||
output += '<a href="" id="add_notification_button">%s</a>' % (label)
|
||||
return html.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({"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'})
|
||||
choiceWidget = forms.Select
|
||||
if create:
|
||||
expressionWidget = SimpleExpressionWidget(initial)
|
||||
notificationWidget = NotificationCreateWidget()
|
||||
else:
|
||||
expressionWidget = textAreaWidget
|
||||
notificationWidget = NotificationCreateWidget()
|
||||
|
||||
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:
|
||||
notifications = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve notifications.'))
|
||||
notification_choices = [(notification['id'], notification['name'])
|
||||
for notification in notifications]
|
||||
if notification_choices:
|
||||
if len(notification_choices) > 1:
|
||||
notification_choices.insert(
|
||||
0, ("", str(_("Select Notification"))))
|
||||
else:
|
||||
notification_choices.insert(
|
||||
0, ("", str(_("No notifications available."))))
|
||||
|
||||
self.fields['notifications'].choices = notification_choices
|
||||
|
||||
def clean_expression(self):
|
||||
data = self.cleaned_data['expression']
|
||||
value = data.split(' ')[2]
|
||||
if not value.isdigit():
|
||||
raise forms.ValidationError("Value must be an integer")
|
||||
|
||||
# Always return the cleaned data, whether you have changed it or
|
||||
# not.
|
||||
return data
|
||||
|
||||
|
||||
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('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,
|
||||
ok_actions=alarm_actions,
|
||||
undetermined_actions=alarm_actions,
|
||||
)
|
||||
messages.success(request,
|
||||
_('Alarm has been created successfully.'))
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Unable to create the alarm.'))
|
||||
return False
|
||||
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('id')
|
||||
for notification in data['notifications']]
|
||||
api.monitor.alarm_update(
|
||||
request,
|
||||
alarm_id=self.initial['id'],
|
||||
state=self.initial['state'],
|
||||
severity=data['severity'],
|
||||
name=data['name'],
|
||||
expression=data['expression'],
|
||||
description=data['description'],
|
||||
actions_enabled=data['actions_enabled'],
|
||||
alarm_actions=alarm_actions,
|
||||
ok_actions=alarm_actions,
|
||||
undetermined_actions=alarm_actions,
|
||||
)
|
||||
messages.success(request,
|
||||
_('Alarm has been edited successfully.'))
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Unable to edit the alarm.'))
|
||||
return False
|
||||
return True
|
||||
@@ -1,27 +0,0 @@
|
||||
# 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.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from monitoring import dashboard
|
||||
|
||||
|
||||
class Alarms(horizon.Panel):
|
||||
name = _("Alarms")
|
||||
slug = 'alarms'
|
||||
|
||||
|
||||
dashboard.Monitoring.register(Alarms)
|
||||
@@ -1,288 +0,0 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2016 Cray Inc. All rights reserved.
|
||||
#
|
||||
# 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 json
|
||||
|
||||
from django.conf import settings
|
||||
from django import template
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from monitoring.alarms import constants
|
||||
from monitoring import api
|
||||
from monitoring.overview import constants as ov_constants
|
||||
|
||||
|
||||
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.upper()
|
||||
img_tag = '<img src="{img}" title="{label}" class="status-icon" />{label}'
|
||||
if status == 'CRITICAL':
|
||||
return img_tag.format(img=constants.CRITICAL_ICON, label=status)
|
||||
if status in ('LOW', 'MEDIUM', 'HIGH'):
|
||||
return img_tag.format(img=constants.WARNING_ICON, label=status)
|
||||
if status == 'OK':
|
||||
return img_tag.format(img=constants.OK_ICON, label=status)
|
||||
if status == 'UNKNOWN' or status == 'UNDETERMINED':
|
||||
return img_tag.format(img=constants.UNKNOWN_ICON, label=status)
|
||||
return status
|
||||
|
||||
|
||||
def show_severity(data):
|
||||
severity = data['alarm_definition']['severity']
|
||||
state = data['state']
|
||||
if state == 'ALARM':
|
||||
return severity.upper()
|
||||
else:
|
||||
return state.upper()
|
||||
|
||||
|
||||
def show_alarm_id(data):
|
||||
return data['id']
|
||||
|
||||
|
||||
def show_metric_names(data):
|
||||
names = set(metric['name'] for metric in data['metrics'])
|
||||
return ', '.join(names)
|
||||
|
||||
|
||||
def show_def_name(data):
|
||||
return data['alarm_definition']['name']
|
||||
|
||||
|
||||
def show_def_severity(data):
|
||||
return data['alarm_definition']['severity']
|
||||
|
||||
|
||||
def show_metric_dimensions(data):
|
||||
if len(data['metrics']) > 1:
|
||||
commondimensions = data['metrics'][0]['dimensions']
|
||||
for metric in data['metrics'][1:]:
|
||||
for k in tuple(commondimensions):
|
||||
if k not in metric['dimensions'].keys() or \
|
||||
commondimensions[k] != metric['dimensions'][k]:
|
||||
del commondimensions[k]
|
||||
return ','.join(["%s=%s" % (n, v) for n, v
|
||||
in commondimensions.items()])
|
||||
else:
|
||||
return ','.join(["%s=%s" % (n, v) for n, v
|
||||
in data['metrics'][0]['dimensions'].items()])
|
||||
|
||||
|
||||
def get_service(data):
|
||||
if len(data['metrics']) == 1 and 'service'in\
|
||||
data['metrics'][0]['dimensions']:
|
||||
return data['metrics'][0]['dimensions']['service']
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
class ShowAlarmHistory(tables.LinkAction):
|
||||
name = 'history'
|
||||
verbose_name = _('Show History')
|
||||
classes = ('btn-edit',)
|
||||
|
||||
def get_link_url(self, datum):
|
||||
return reverse(constants.URL_PREFIX + 'history',
|
||||
args=(datum['alarm_definition']['id'], datum['id'], ))
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class CreateAlarm(tables.LinkAction):
|
||||
name = "create_alarm"
|
||||
verbose_name = _("Create Alarm")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("alarm", "alarm:create"),)
|
||||
ajax = True
|
||||
|
||||
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 GraphMetric(tables.LinkAction):
|
||||
name = "graph_alarm"
|
||||
verbose_name = _("Graph Metric")
|
||||
icon = "dashboard"
|
||||
|
||||
def render(self):
|
||||
self.attrs['target'] = 'newtab'
|
||||
return super(self, GraphMetric).render()
|
||||
|
||||
def get_link_url(self, datum):
|
||||
url = ''
|
||||
query = ''
|
||||
self.attrs['target'] = '_blank'
|
||||
try:
|
||||
region = self.table.request.user.services_region
|
||||
grafana_url = getattr(settings, 'GRAFANA_URL').get(region, '')
|
||||
url = grafana_url + \
|
||||
'/dashboard/script/drilldown.js'
|
||||
metric = datum['metrics'][0]['name']
|
||||
dimensions = datum['metrics'][0].get('dimensions', {})
|
||||
query = "?metric=%s" % metric
|
||||
for key, value in dimensions.items():
|
||||
query += "&%s=%s" % (key, value)
|
||||
except AttributeError:
|
||||
# Catches case where Grafana 2 is not enabled.
|
||||
name = datum['metrics'][0]['name']
|
||||
threshold = json.dumps(datum['metrics'])
|
||||
endpoint = str(reverse_lazy(ov_constants.URL_PREFIX + 'proxy'))
|
||||
endpoint = self.table.request.build_absolute_uri(endpoint)
|
||||
url = '/grafana/index.html#/dashboard/script/detail.js'
|
||||
query = "?name=%s&threshold=%s&api=%s" % \
|
||||
(name, threshold, endpoint)
|
||||
return url + query
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return (getattr(settings, 'GRAFANA_URL', None) is not None and
|
||||
datum['metrics'])
|
||||
|
||||
|
||||
class ShowAlarmDefinition(tables.LinkAction):
|
||||
name = "show_alarm_definition"
|
||||
verbose_name = _("Show Alarm Definition")
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
url = 'horizon:monitoring:alarmdefs:alarm_detail'
|
||||
args = (datum['alarm_definition']['id'],)
|
||||
return reverse_lazy(url, args=args)
|
||||
|
||||
|
||||
class DeleteAlarm(tables.DeleteAction):
|
||||
name = "delete_alarm"
|
||||
verbose_name = _("Delete Alarm")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
"Delete Alarm",
|
||||
"Delete Alarms",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
"Deleted Alarm",
|
||||
"Deleted Alarms",
|
||||
count
|
||||
)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
api.monitor.alarm_delete(request, obj_id)
|
||||
|
||||
|
||||
class AlarmsFilterAction(tables.LinkAction):
|
||||
name = "filter"
|
||||
verbose_name = _("Filter Alarms")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("alarm", "alarm:filter"),)
|
||||
ajax = True
|
||||
|
||||
def get_link_url(self):
|
||||
return reverse(constants.URL_PREFIX + 'alarm_filter', args=())
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class AlarmsTable(tables.DataTable):
|
||||
state = tables.Column(transform=show_severity, verbose_name=_('Status'),
|
||||
status_choices={(show_status('OK'), True)},
|
||||
filters=[show_status, template.defaultfilters.safe])
|
||||
name = tables.Column(transform=show_def_name, verbose_name=_('Name'))
|
||||
alarmId = tables.Column(transform=show_alarm_id, verbose_name=_('Alarm Id'))
|
||||
metrics = tables.Column(transform=show_metric_names,
|
||||
verbose_name=_('Metric Names'))
|
||||
dimensions = tables.Column(transform=show_metric_dimensions,
|
||||
verbose_name=_('Metric Dimensions'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return obj['id']
|
||||
|
||||
def get_object_display(self, obj):
|
||||
return obj['id']
|
||||
|
||||
class Meta(object):
|
||||
name = "alarms"
|
||||
verbose_name = _("Alarms")
|
||||
row_actions = (GraphMetric,
|
||||
ShowAlarmHistory,
|
||||
ShowAlarmDefinition,
|
||||
DeleteAlarm,
|
||||
)
|
||||
table_actions = (AlarmsFilterAction,
|
||||
DeleteAlarm,
|
||||
)
|
||||
|
||||
|
||||
def show_timestamp(data):
|
||||
return data['timestamp'].replace('.000Z', '').replace('T', ' ')
|
||||
|
||||
|
||||
class AlarmHistoryTable(tables.DataTable):
|
||||
timestamp = tables.Column(transform=show_timestamp,
|
||||
verbose_name=_('Timestamp'),
|
||||
attrs={"data-type": "timestamp"})
|
||||
old_state = tables.Column('old_state', verbose_name=_('Old State'))
|
||||
new_state = tables.Column('new_state', verbose_name=_('New State'))
|
||||
alarmDimensions = tables.Column(transform=show_metric_dimensions,
|
||||
verbose_name=_('Alarm Metric Dimensions'))
|
||||
reason = tables.Column('reason', verbose_name=_('Reason'))
|
||||
# reason_data = tables.Column('reason_data', verbose_name=_('Reason Data'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return obj['alarm_id'] + obj['timestamp']
|
||||
|
||||
class Meta(object):
|
||||
name = "history"
|
||||
verbose_name = _("Alarm History")
|
||||
@@ -1,67 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-header %}{% trans "Filter Alarms" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<script>
|
||||
function isEmpty(str) {
|
||||
return (!str || 0 === str.length);
|
||||
}
|
||||
|
||||
function sendID() {
|
||||
var location = "{{ alarm_url }}";
|
||||
var filterID = document.forms["form0"]["filterID"].value;
|
||||
if (!isEmpty(filterID)) {
|
||||
window.location = location + "id=" + filterID;
|
||||
} else {
|
||||
window.location = location;
|
||||
}
|
||||
}
|
||||
|
||||
function sendDimensions() {
|
||||
var dimension = document.forms["form1"]["dimension"].value;
|
||||
var metric = document.forms["form1"]["metric"].value;
|
||||
var location = "{{ alarm_url }}";
|
||||
var param = '';
|
||||
if (!isEmpty(metric)) {
|
||||
param = 'metric=' + metric;
|
||||
if (!isEmpty(dimension)) {
|
||||
param = param + ',';
|
||||
}
|
||||
}
|
||||
if (!isEmpty(dimension)) {
|
||||
param = param + dimension;
|
||||
}
|
||||
if (!isEmpty(param)) {
|
||||
param = btoa(param).replace(/\+/g, '-').replace(/\//g, '_');
|
||||
location = location + 'b64:' + param;
|
||||
}
|
||||
window.location = location;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class = "filter_form" >
|
||||
<form></form>
|
||||
|
||||
<form method="GET" class="form-group" style="width:400px" onsubmit="sendID()" name="form0">
|
||||
<h3>{% trans 'Alarm ID' %}</h3>
|
||||
{% trans 'Value' %}: <input class="form-control" name="filterID" placeholder="54fdec2f-ae34-4dba-9c85-7b4ad67eea03">
|
||||
<button type="submit" class="btn btn-default">{% trans 'Submit' %}</button>
|
||||
</form>
|
||||
<hr>
|
||||
<hr>
|
||||
<form method="GET" class="form-group" style="width:400px" onsubmit="sendDimensions()" name="form1">
|
||||
<h3>{% trans 'Dimension(s)' %}</h3>
|
||||
{% trans 'Key' %}[={% trans 'Value' %}]
|
||||
<input class="form-control" name="dimension" placeholder="hostname=devstack, service=mysql" data-toggle="tooltip" data-placement="right" title="Example: hostname=devstack, service=mysql">
|
||||
<br />
|
||||
{% trans 'Metric Name' %}: <input class="form-control" name="metric" placeholder="http_status">
|
||||
<button type="submit" class="btn btn-default">{% trans 'Submit' %}</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
{% endblock %}
|
||||
@@ -1,34 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Alarms' %}{% endblock %}
|
||||
{% block page_header %}
|
||||
{% if service != 'all'%}
|
||||
<div class="page-header">
|
||||
<h2>{% trans 'Alarms for ' %} {{ service }}</h2>
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("All Alarms") %}
|
||||
{% endif %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item-truncate">{% trans "Monitoring" %}</li>
|
||||
<li class="breadcrumb-item-truncate active">{% trans "Alarms" %}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if prev_page_offset != None %}
|
||||
<a href="?page_offset={{ prev_page_offset }}" class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Previous Page' %}</a>
|
||||
{% endif %}
|
||||
{% if page_offset %}
|
||||
<a href="?page_offset={{ page_offset}}" class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Next Page' %}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,79 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Alarm History' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
<div class="page-header">
|
||||
<h2>{% trans 'Alarm History' %}</h2>
|
||||
</div>
|
||||
<h4>{% trans "Alarm Details" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ alarm.alarm_definition.name|default:_("None") }}</dd>
|
||||
<dt>{% trans "Associated Metrics" %}</dt>
|
||||
<dd><ul>
|
||||
{% for metric in alarm.metrics %}
|
||||
{% if metric.dimensions %}
|
||||
<li>
|
||||
{{ metric.name }}
|
||||
{
|
||||
{% for k, v in metric.dimensions.items %}
|
||||
{{ k }}={{ v }}
|
||||
{% if not forloop.last %}
|
||||
{{ ',' }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
</li>
|
||||
{% else %}
|
||||
<li>{{ metric.name }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul></dd>
|
||||
</dl>
|
||||
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="row" ng-controller="timestampPickerController">
|
||||
<div class="col-sm-3">
|
||||
<select id="timestampFormatDropdownAlarmHistory"
|
||||
name="ts_mode"
|
||||
class="form-control"
|
||||
ng-model="currentFormat"
|
||||
ng-init="setUp('{{ timestamp_selected | safe }}')">
|
||||
<option value="">{% trans "---Please select---" %}</option>
|
||||
{% for key,label in timestamp_formats %}
|
||||
<option value="{{ key }}" title="{{ label }}">{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<span class="label label-primary"
|
||||
ng-if="currentOffset">{$ currentOffset $}h</span>
|
||||
</div>
|
||||
|
||||
{{ table.render }}
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if prev_page_offset != None %}
|
||||
<a href="?page_offset={{ prev_page_offset }}&ts_mode={{ timestamp_selected }}&ts_offset={{ timestamp_offset|urlencode }}"
|
||||
class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Previous Page' %}</a>
|
||||
{% endif %}
|
||||
{% if page_offset %}
|
||||
<a href="?page_offset={{ page_offset }}&ts_mode={{ timestamp_selected }}&ts_offset={{ timestamp_offset|urlencode }}"
|
||||
class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Next Page' %}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<style>
|
||||
.status-icon {
|
||||
vertical-align: top;
|
||||
margin-right: 2px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Measurements for Alarms' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title="Measurements that Triggered Alarm" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block manage_overview %}
|
||||
{% block page_title %}
|
||||
<div class="page_title table_header">
|
||||
<div>
|
||||
<h3>
|
||||
<a href="{% url 'horizon:noc_tools:service_health:index' %}" class="showspinner">
|
||||
{% trans "Service Health" %}</a> : {{ region_name }} - >
|
||||
{% trans "Service :" %} {{ service }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="table_actions">
|
||||
<a href="{% url 'horizon:noc_tools:service_health:index' %}" class="close showspinner">×</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_title %}
|
||||
{% endblock %}
|
||||
{% block main %}
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
||||
<script type="text/javascript">
|
||||
google.load("visualization", "1", {packages:["corechart"]});
|
||||
google.setOnLoadCallback(drawChart);
|
||||
function drawChart() {
|
||||
var data = google.visualization.arrayToDataTable([
|
||||
['Time', 'Response Time', 'Threshold'],
|
||||
['8:42', 1000, 1200],
|
||||
['8:43', 1170, 1200],
|
||||
['8:44', 1190, 1200],
|
||||
['8:45', 1230, 1200]
|
||||
]);
|
||||
|
||||
var options = {
|
||||
title: 'API Response Time'
|
||||
};
|
||||
|
||||
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
|
||||
chart.draw(data, options);
|
||||
}
|
||||
</script>
|
||||
<div id="chart_div" style="width: 900px; height: 500px;"></div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title="Filter Alarms" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'monitoring/alarms/_filter.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,68 +0,0 @@
|
||||
# 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.urls import reverse
|
||||
from unittest.mock import patch
|
||||
|
||||
from monitoring.alarms import constants
|
||||
from monitoring.alarms import tables
|
||||
from monitoring.test import helpers
|
||||
|
||||
|
||||
INDEX_URL = reverse(
|
||||
constants.URL_PREFIX + 'index')
|
||||
ALARMS_URL_BY_DIMENSION = reverse(
|
||||
constants.URL_PREFIX + 'alarm', args=('nova',))
|
||||
ALARMS_URL = reverse(
|
||||
constants.URL_PREFIX + 'alarm', args=('all',))
|
||||
|
||||
|
||||
class AlarmsTest(helpers.TestCase):
|
||||
|
||||
def test_alarms_get_by_dimension(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['alarm_list_by_dimension'],
|
||||
'alarm_list_by_dimension.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(ALARMS_URL_BY_DIMENSION)
|
||||
self.assertEqual(mock.alarm_list_by_dimension.call_count, 2)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/alarms/alarm.html')
|
||||
|
||||
def test_alarms_get(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['alarm_list'],
|
||||
'alarm_list.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(ALARMS_URL)
|
||||
self.assertEqual(mock.alarm_list.call_count, 2)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/alarms/alarm.html')
|
||||
|
||||
def test_metric_conversion_single(self):
|
||||
res = tables.show_metric_names({"metrics": [{"name": "mem.used_bytes"}]})
|
||||
self.assertEqual(res, "mem.used_bytes")
|
||||
|
||||
def test_metric_conversion_multiple(self):
|
||||
res = tables.show_metric_names({"metrics": [{"name": "mem.used_bytes"},
|
||||
{"name": "mem.total_bytes"}]})
|
||||
table_res = res.split(', ')
|
||||
self.assertEqual(len(table_res), 2)
|
||||
self.assertTrue("mem.used_bytes" in table_res)
|
||||
self.assertTrue("mem.total_bytes" in table_res)
|
||||
|
||||
def test_metric_conversion_unique(self):
|
||||
res = tables.show_metric_names({"metrics": [{"name": "mem.used_bytes"},
|
||||
{"name": "mem.used_bytes"}]})
|
||||
self.assertEqual(res, "mem.used_bytes")
|
||||
@@ -1,32 +0,0 @@
|
||||
# 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.urls import re_path
|
||||
|
||||
from monitoring.alarms import views
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', views.IndexView.as_view(), name='index'),
|
||||
re_path(r'^alarm/filter/$',
|
||||
views.AlarmFilterView.as_view(),
|
||||
name='alarm_filter'),
|
||||
re_path(r'^alarm/(?P<service>[^/]+)/$',
|
||||
views.AlarmServiceView.as_view(),
|
||||
name='alarm'),
|
||||
re_path(r'^alarm/$',
|
||||
views.AlarmServiceView.as_view(),
|
||||
name='alarm_all'),
|
||||
re_path(r'^history/(?P<name>[^/]+)/(?P<id>[^/]+)$',
|
||||
views.AlarmHistoryView.as_view(),
|
||||
name='history')
|
||||
]
|
||||
@@ -1,402 +0,0 @@
|
||||
# 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 base64
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.paginator import EmptyPage
|
||||
from django.core.paginator import Paginator
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import View
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon.utils import functions as utils
|
||||
|
||||
from monitoring.alarms import constants
|
||||
from monitoring.alarms import forms as alarm_forms
|
||||
from monitoring.alarms import tables as alarm_tables
|
||||
from monitoring import api
|
||||
|
||||
from openstack_dashboard import policy
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
SERVICES = getattr(settings, 'MONITORING_SERVICES', [])
|
||||
|
||||
PREV_PAGE_LIMIT = 100
|
||||
|
||||
|
||||
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)}
|
||||
|
||||
alarm_history_default_ts_format = 'utc'
|
||||
alarm_history_ts_formats = (
|
||||
('utc', _('UTC'),),
|
||||
('bl', _('Browser local'),),
|
||||
)
|
||||
|
||||
default_service = 'all'
|
||||
|
||||
|
||||
def get_status(alarms):
|
||||
if not alarms:
|
||||
return 'chicklet-notfound'
|
||||
status_index = 0
|
||||
for a in alarms:
|
||||
severity = alarm_tables.show_severity(a)
|
||||
severity_index = index_by_severity.get(severity, None)
|
||||
status_index = max(status_index, severity_index)
|
||||
return priorities[status_index]['status']
|
||||
|
||||
|
||||
def generate_status(request):
|
||||
try:
|
||||
alarms = api.monitor.alarm_list(request)
|
||||
except Exception as e:
|
||||
messages.error(request,
|
||||
_('Unable to list alarms: %s') % str(e))
|
||||
alarms = []
|
||||
alarms_by_service = {}
|
||||
for a in alarms:
|
||||
service = alarm_tables.show_service(a)
|
||||
service_alarms = alarms_by_service.setdefault(service, [])
|
||||
service_alarms.append(a)
|
||||
for row in SERVICES:
|
||||
row['name'] = str(row['name'])
|
||||
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'])
|
||||
service['display'] = str(service['display'])
|
||||
return SERVICES
|
||||
|
||||
|
||||
class IndexView(View):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return redirect(constants.URL_PREFIX + 'alarm', service='all')
|
||||
|
||||
|
||||
class AlarmServiceView(tables.DataTableView):
|
||||
table_class = alarm_tables.AlarmsTable
|
||||
template_name = constants.TEMPLATE_PREFIX + 'alarm.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
|
||||
if 'service' in kwargs:
|
||||
self.service = kwargs['service']
|
||||
del kwargs['service']
|
||||
else:
|
||||
self.service = default_service
|
||||
|
||||
return super(AlarmServiceView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get_data(self):
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
contacts = []
|
||||
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
|
||||
limit = utils.get_page_size(self.request)
|
||||
if self.service == default_service:
|
||||
try:
|
||||
results = api.monitor.alarm_list(self.request, page_offset,
|
||||
limit)
|
||||
paginator = Paginator(results, limit)
|
||||
contacts = paginator.page(1)
|
||||
except EmptyPage:
|
||||
contacts = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
return contacts
|
||||
else:
|
||||
if self.service[:2] == 'id':
|
||||
try:
|
||||
name, value = self.service.split("=")
|
||||
results = [api.monitor.alarm_show(self.request, value)]
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
results = []
|
||||
return results
|
||||
else:
|
||||
try:
|
||||
if self.service[:3] == 'b64':
|
||||
name, value = self.service.split(":")
|
||||
self.service = base64.urlsafe_b64decode(str(value)).decode('utf-8')
|
||||
results = api.monitor.alarm_list_by_dimension(self.request,
|
||||
self.service,
|
||||
page_offset,
|
||||
limit)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
results = []
|
||||
return results
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(AlarmServiceView, self).get_context_data(**kwargs)
|
||||
results = []
|
||||
num_results = 0 # make sure variable is set
|
||||
prev_page_stack = []
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
|
||||
if 'prev_page_stack' in self.request.session:
|
||||
prev_page_stack = self.request.session['prev_page_stack']
|
||||
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
prev_page_stack = []
|
||||
else:
|
||||
page_offset = int(page_offset)
|
||||
limit = utils.get_page_size(self.request)
|
||||
if self.service == 'all':
|
||||
try:
|
||||
# To judge whether there is next page, get limit + 1
|
||||
results = api.monitor.alarm_list(self.request, page_offset,
|
||||
limit + 1)
|
||||
num_results = len(results)
|
||||
paginator = Paginator(results, limit)
|
||||
results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
results = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
else:
|
||||
if self.service[:2] == 'id':
|
||||
try:
|
||||
name, value = self.service.split("=")
|
||||
results = [api.monitor.alarm_show(self.request, value)]
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
results = []
|
||||
else:
|
||||
try:
|
||||
# To judge whether there is next page, get limit + 1
|
||||
results = api.monitor.alarm_list_by_dimension(self.request,
|
||||
self.service,
|
||||
page_offset,
|
||||
limit + 1)
|
||||
num_results = len(results)
|
||||
paginator = Paginator(results, limit)
|
||||
results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
results = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve alarms"))
|
||||
results = []
|
||||
|
||||
context["contacts"] = results
|
||||
context["service"] = self.service
|
||||
|
||||
if num_results < limit + 1:
|
||||
context["page_offset"] = None
|
||||
else:
|
||||
context["page_offset"] = page_offset + limit
|
||||
|
||||
if page_offset in prev_page_stack:
|
||||
index = prev_page_stack.index(page_offset)
|
||||
prev_page_stack = prev_page_stack[0:index]
|
||||
|
||||
prev_page_offset = prev_page_stack[-1] if prev_page_stack else None
|
||||
if prev_page_offset is not None:
|
||||
context["prev_page_offset"] = prev_page_offset
|
||||
|
||||
if len(prev_page_stack) > PREV_PAGE_LIMIT:
|
||||
del prev_page_stack[0]
|
||||
prev_page_stack.append(page_offset)
|
||||
self.request.session['prev_page_stack'] = prev_page_stack
|
||||
|
||||
return context
|
||||
|
||||
|
||||
def transform_alarm_history(results, name, ts_mode, ts_offset):
|
||||
new_list = []
|
||||
|
||||
def get_ts_val(val):
|
||||
if ts_mode == 'bl':
|
||||
offset = int((ts_offset or '0').replace('+', ''))
|
||||
dt_val = parse_datetime(val) + timedelta(hours=offset)
|
||||
dt_val_formatter = dt_val.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
|
||||
return dt_val_formatter.replace('000Z', '')
|
||||
elif ts_mode != 'utc':
|
||||
raise ValueError('%s is not supported timestamp format' % ts_mode)
|
||||
else:
|
||||
return val # utc case
|
||||
|
||||
for item in results:
|
||||
new_list.append({'alarm_id': item['alarm_id'],
|
||||
'name': name,
|
||||
'old_state': item['old_state'],
|
||||
'new_state': item['new_state'],
|
||||
'timestamp': get_ts_val(item['timestamp']),
|
||||
'reason': item['reason'],
|
||||
'metrics': item['metrics'],
|
||||
'reason_data': item['reason_data']})
|
||||
return new_list
|
||||
|
||||
|
||||
class AlarmHistoryView(tables.DataTableView):
|
||||
table_class = alarm_tables.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):
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
ts_mode = self.request.GET.get('ts_mode')
|
||||
ts_offset = self.request.GET.get('ts_offset')
|
||||
|
||||
contacts = []
|
||||
object_id = self.kwargs['id']
|
||||
name = self.kwargs['name']
|
||||
|
||||
if not ts_mode:
|
||||
ts_mode = alarm_history_default_ts_format
|
||||
if not page_offset:
|
||||
page_offset = 0
|
||||
limit = utils.get_page_size(self.request)
|
||||
try:
|
||||
results = api.monitor.alarm_history(self.request, object_id, page_offset, limit)
|
||||
paginator = Paginator(results, limit)
|
||||
contacts = paginator.page(1)
|
||||
except EmptyPage:
|
||||
contacts = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request,
|
||||
_("Could not retrieve alarm history for %s") % object_id)
|
||||
|
||||
try:
|
||||
return transform_alarm_history(contacts, name, ts_mode, ts_offset)
|
||||
except ValueError as err:
|
||||
LOG.warning('Failed to transform alarm history due to %s' %
|
||||
err.message)
|
||||
messages.warning(self.request, _('Failed to present alarm '
|
||||
'history'))
|
||||
return []
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(AlarmHistoryView, self).get_context_data(**kwargs)
|
||||
|
||||
object_id = kwargs['id']
|
||||
ts_mode = self.request.GET.get('ts_mode')
|
||||
ts_offset = self.request.GET.get('ts_offset')
|
||||
|
||||
try:
|
||||
alarm = api.monitor.alarm_get(self.request, object_id)
|
||||
except Exception:
|
||||
messages.error(self.request,
|
||||
_("Could not retrieve alarm for %s") % object_id)
|
||||
context['alarm'] = alarm
|
||||
|
||||
num_results = 0
|
||||
contacts = []
|
||||
prev_page_stack = []
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
limit = utils.get_page_size(self.request)
|
||||
if 'prev_page_stack' in self.request.session:
|
||||
prev_page_stack = self.request.session['prev_page_stack']
|
||||
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
prev_page_stack = []
|
||||
try:
|
||||
# To judge whether there is next page, get limit + 1
|
||||
results = api.monitor.alarm_history(self.request, object_id, page_offset,
|
||||
limit + 1)
|
||||
num_results = len(results)
|
||||
paginator = Paginator(results, limit)
|
||||
contacts = paginator.page(1)
|
||||
except EmptyPage:
|
||||
contacts = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request,
|
||||
_("Could not retrieve alarm history for %s") % object_id)
|
||||
return context
|
||||
|
||||
context["contacts"] = contacts
|
||||
context['timestamp_formats'] = alarm_history_ts_formats
|
||||
context['timestamp_selected'] = ts_mode or ''
|
||||
context['timestamp_offset'] = ts_offset or 0
|
||||
|
||||
if num_results < limit + 1:
|
||||
context["page_offset"] = None
|
||||
else:
|
||||
context["page_offset"] = contacts.object_list[-1]["id"]
|
||||
|
||||
if page_offset in prev_page_stack:
|
||||
index = prev_page_stack.index(page_offset)
|
||||
prev_page_stack = prev_page_stack[0:index]
|
||||
|
||||
prev_page_offset = prev_page_stack[-1] if prev_page_stack else None
|
||||
if prev_page_offset is not None:
|
||||
context["prev_page_offset"] = prev_page_offset
|
||||
|
||||
if len(prev_page_stack) > PREV_PAGE_LIMIT:
|
||||
del prev_page_stack[0]
|
||||
prev_page_stack.append(str(page_offset))
|
||||
self.request.session['prev_page_stack'] = prev_page_stack
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class AlarmFilterView(forms.ModalFormView):
|
||||
template_name = constants.TEMPLATE_PREFIX + 'filter.html'
|
||||
form_class = alarm_forms.CreateAlarmForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(AlarmFilterView, self).get_context_data(**kwargs)
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
context["action_url"] = reverse(constants.URL_PREFIX + 'alarm_filter',
|
||||
args=())
|
||||
context["alarm_url"] = reverse_lazy(constants.URL_PREFIX + 'alarm_all',
|
||||
args=())
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy(constants.URL_PREFIX + 'index',
|
||||
args=())
|
||||
@@ -1,5 +0,0 @@
|
||||
from monitoring.api import monitor
|
||||
|
||||
__all__ = [
|
||||
"monitor"
|
||||
]
|
||||
@@ -1,111 +0,0 @@
|
||||
# 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 horizon import exceptions
|
||||
from horizon.utils import memoized
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
from monascaclient import client as mon_client
|
||||
from monitoring.config import local_settings as settings
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
INSECURE = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
CACERT = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
|
||||
KEYSTONE_SERVICE = 'identity'
|
||||
MONITORING_SERVICE = getattr(settings, 'MONITORING_SERVICE_TYPE', 'monitoring')
|
||||
|
||||
VERSIONS = base.APIVersionManager(
|
||||
MONITORING_SERVICE,
|
||||
preferred_version=getattr(settings,
|
||||
'OPENSTACK_API_VERSIONS',
|
||||
{}).get(MONITORING_SERVICE, 2.0)
|
||||
)
|
||||
VERSIONS.load_supported_version(2.0, {'client': mon_client, 'version': '2_0'})
|
||||
|
||||
|
||||
def _get_endpoint(request):
|
||||
try:
|
||||
endpoint = base.url_for(request,
|
||||
service_type=settings.MONITORING_SERVICE_TYPE,
|
||||
endpoint_type=settings.MONITORING_ENDPOINT_TYPE)
|
||||
except exceptions.ServiceCatalogException:
|
||||
endpoint = 'http://127.0.0.1:8070/v2.0'
|
||||
LOG.warning('Monasca API location could not be found in Service '
|
||||
'Catalog, using default: {0}'.format(endpoint))
|
||||
return endpoint
|
||||
|
||||
|
||||
def _get_auth_params_from_request(request):
|
||||
"""Extracts the properties from the request object needed by the monascaclient call below.
|
||||
|
||||
These will be used to memoize the calls to monascaclient
|
||||
"""
|
||||
|
||||
LOG.debug('Extracting intel from request')
|
||||
return (
|
||||
request.user.user_domain_id,
|
||||
request.user.token.id,
|
||||
request.user.tenant_id,
|
||||
request.user.token.project.get('domain_id'),
|
||||
base.url_for(request, MONITORING_SERVICE),
|
||||
base.url_for(request, KEYSTONE_SERVICE)
|
||||
)
|
||||
|
||||
|
||||
def _get_to_verify(insecure, cacert):
|
||||
to_verify = cacert
|
||||
|
||||
if insecure:
|
||||
to_verify = False
|
||||
|
||||
return to_verify
|
||||
|
||||
|
||||
@memoized.memoized
|
||||
def monascaclient(request, version=None):
|
||||
|
||||
(
|
||||
user_domain_id,
|
||||
token_id,
|
||||
project_id,
|
||||
project_domain_id,
|
||||
monasca_url,
|
||||
auth_url
|
||||
) = _get_auth_params_from_request(request)
|
||||
|
||||
# NOTE(trebskit) this is bit hacky, we should
|
||||
# go straight into using numbers as version representation
|
||||
version = (VERSIONS.get_active_version()['version']
|
||||
if not version else version)
|
||||
|
||||
LOG.debug('Monasca::Client <Url: %s> <Version: %s>'
|
||||
% (monasca_url, version))
|
||||
|
||||
to_verify = _get_to_verify(INSECURE, CACERT)
|
||||
|
||||
c = mon_client.Client(api_version=version,
|
||||
token=token_id,
|
||||
project_id=project_id,
|
||||
user_domain_id=user_domain_id,
|
||||
project_domain_id=project_domain_id,
|
||||
verify=to_verify,
|
||||
auth_url=auth_url,
|
||||
endpoint=monasca_url)
|
||||
return c
|
||||
@@ -1,202 +0,0 @@
|
||||
# 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 oslo_log import log
|
||||
|
||||
from openstack_dashboard.contrib.developer.profiler import api as profiler
|
||||
|
||||
from monitoring.api import client
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_list(request, offset=0, limit=10000, marker=None, paginate=False):
|
||||
result = client.monascaclient(request).alarms.list(offset=offset,
|
||||
limit=limit,
|
||||
sort_by='alarm_definition_name')
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_list_by_dimension(request, dimensions, offset=0, limit=10000,
|
||||
marker=None, paginate=False):
|
||||
dim_dict = {}
|
||||
metric = None
|
||||
dimensions = dimensions.split(",")
|
||||
for item in dimensions:
|
||||
if '=' in item:
|
||||
name, value = item.split('=')
|
||||
if name == 'metric':
|
||||
metric = value
|
||||
else:
|
||||
dim_dict[name] = value
|
||||
else:
|
||||
dim_dict[item] = None
|
||||
if metric:
|
||||
result = client.monascaclient(request).alarms.list(offset=offset,
|
||||
limit=limit,
|
||||
metric_dimensions=dim_dict,
|
||||
metric_name=metric)
|
||||
else:
|
||||
result = client.monascaclient(request).alarms.list(offset=offset,
|
||||
limit=limit,
|
||||
metric_dimensions=dim_dict)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_show(request, alarm_id):
|
||||
result = client.monascaclient(request).alarms.get(alarm_id=alarm_id)
|
||||
return result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_delete(request, alarm_id):
|
||||
return client.monascaclient(request).alarms.delete(alarm_id=alarm_id)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_history(request, alarm_id, offset=0, limit=10000):
|
||||
result = client.monascaclient(request).alarms.history(alarm_id=alarm_id,
|
||||
offset=offset,
|
||||
limit=limit)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_get(request, alarm_id):
|
||||
return client.monascaclient(request).alarms.get(alarm_id=alarm_id)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarm_patch(request, **kwargs):
|
||||
return client.monascaclient(request).alarms.patch(**kwargs)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_list(request, offset=0, limit=10000, marker=None, paginate=False):
|
||||
result = client.monascaclient(request).alarm_definitions.list(offset=offset,
|
||||
limit=limit,
|
||||
sort_by='name')
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_list_by_service(request, service_name, marker=None,
|
||||
paginate=False):
|
||||
service_dim = {'service': service_name}
|
||||
result = client.monascaclient(request).alarm_definitions.list(
|
||||
dimensions=service_dim)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_delete(request, alarm_id):
|
||||
return client.monascaclient(request).alarm_definitions.delete(
|
||||
alarm_id=alarm_id)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_history(request, alarm_id):
|
||||
return client.monascaclient(request).alarm_definitions.history(
|
||||
alarm_id=alarm_id)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_get(request, alarm_id):
|
||||
return client.monascaclient(request).alarm_definitions.get(alarm_id=alarm_id)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_get_by_name(request, name):
|
||||
return client.monascaclient(request).alarm_definitions.list(
|
||||
name=name,
|
||||
limit=1
|
||||
)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_create(request, **kwargs):
|
||||
return client.monascaclient(request).alarm_definitions.create(**kwargs)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_update(request, **kwargs):
|
||||
return client.monascaclient(request).alarm_definitions.update(**kwargs)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def alarmdef_patch(request, **kwargs):
|
||||
return client.monascaclient(request).alarm_definitions.patch(**kwargs)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def notification_list(request, offset=0, limit=10000, marker=None,
|
||||
paginate=False):
|
||||
result = client.monascaclient(request).notifications.list(offset=offset,
|
||||
limit=limit,
|
||||
sort_by='name')
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def notification_delete(request, notification_id):
|
||||
return client.monascaclient(request).notifications.delete(
|
||||
notification_id=notification_id)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def notification_get(request, notification_id):
|
||||
return (client.monascaclient(request).notifications.
|
||||
get(notification_id=notification_id))
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def notification_create(request, **kwargs):
|
||||
return client.monascaclient(request).notifications.create(**kwargs)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def notification_update(request, notification_id, **kwargs):
|
||||
return (client.monascaclient(request).notifications.
|
||||
update(notification_id=notification_id, **kwargs))
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def notification_type_list(request, **kwargs):
|
||||
result = client.monascaclient(request).notificationtypes.list(**kwargs)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def metrics_list(request, **kwargs):
|
||||
result = client.monascaclient(request).metrics.list(**kwargs)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def metrics_measurement_list(request, **kwargs):
|
||||
result = client.monascaclient(request).metrics.list_measurements(**kwargs)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def metrics_stat_list(request, **kwargs):
|
||||
result = client.monascaclient(request).metrics.list_statistics(**kwargs)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def metrics_dimension_value_list(request, **kwargs):
|
||||
result = client.monascaclient(request).metrics.list_dimension_values(**kwargs)
|
||||
return result['elements'] if type(result) is dict else result
|
||||
@@ -1,4 +0,0 @@
|
||||
"monasca_user_role": "role:monasca-user"
|
||||
"default": "@"
|
||||
"monitoring:monitoring": "rule:monasca_user_role"
|
||||
"monitoring:kibana_access": "rule:monasca_user_role"
|
||||
@@ -1,121 +0,0 @@
|
||||
# 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.conf import settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Service group names (global across all projects):
|
||||
MONITORING_SERVICES_GROUPS = [
|
||||
{'name': _('OpenStack Services'), 'groupBy': 'service'},
|
||||
{'name': _('Servers'), 'groupBy': 'hostname'}
|
||||
]
|
||||
|
||||
# Services being monitored
|
||||
MONITORING_SERVICES = getattr(
|
||||
settings,
|
||||
'MONITORING_SERVICES_GROUPS',
|
||||
MONITORING_SERVICES_GROUPS
|
||||
)
|
||||
|
||||
#
|
||||
# Per project service groups. If in this form,
|
||||
# '*' will be applied to all projects not explicitly listed.
|
||||
#
|
||||
# Note the above form (flat) is supported for backward compatibility.
|
||||
#
|
||||
# MONITORING_SERVICES_GROUPS = [
|
||||
# {'admin': [
|
||||
# {'name': _('OpenStack Services'), 'groupBy': 'service'},
|
||||
# {'name': _('Servers'), 'groupBy': 'hostname'}]},
|
||||
# {'*': [
|
||||
# {'name': _('Services'), 'groupBy': 'service'},
|
||||
# {'name': _('Instances'), 'groupBy': 'hostname'}]},
|
||||
# ]
|
||||
|
||||
MONITORING_SERVICE_VERSION = getattr(
|
||||
settings, 'MONITORING_SERVICE_VERSION', '2_0'
|
||||
)
|
||||
MONITORING_SERVICE_TYPE = getattr(
|
||||
settings, 'MONITORING_SERVICE_TYPE', 'monitoring'
|
||||
)
|
||||
MONITORING_ENDPOINT_TYPE = getattr(
|
||||
# NOTE(trebskit) # will default to OPENSTACK_ENDPOINT_TYPE
|
||||
settings, 'MONITORING_ENDPOINT_TYPE', None
|
||||
)
|
||||
|
||||
# Grafana button titles/file names (global across all projects):
|
||||
GRAFANA_LINKS = []
|
||||
DASHBOARDS = getattr(settings, 'GRAFANA_LINKS', GRAFANA_LINKS)
|
||||
|
||||
#
|
||||
# Horizon will link to the grafana home page when using Grafana2.
|
||||
# For any Grafana version additional links to specific dashboards can be
|
||||
# created in two formats.
|
||||
# Flat:
|
||||
# GRAFANA_LINKS = [ {'title': _('Dashboard'), 'path': 'openstack', 'raw': False} ]
|
||||
#
|
||||
# Per project: '*' will be applied to all projects not explicitly listed.
|
||||
# GRAFANA_LINKS = [
|
||||
# {'admin': [
|
||||
# {'title': _('Dashboard'), 'path': 'openstack', 'raw': False}]},
|
||||
# {'*': [
|
||||
# {'title': _('OpenStack Dashboard'), 'path': 'project', 'raw': False}]}
|
||||
# ]
|
||||
#
|
||||
# If GRAFANA_URL is specified, the dashboard file name/raw URL must be
|
||||
# specified through the 'path' attribute as shown above.
|
||||
#
|
||||
# Flat:
|
||||
# GRAFANA_LINKS = [ {'title': _('Dashboard'), 'fileName': 'openstack.json', 'raw': False} ]
|
||||
#
|
||||
# GRAFANA_LINKS = [
|
||||
# {'admin': [
|
||||
# {'fileName': _('Dashboard'), 'fileName': 'openstack.json', 'raw': False}]},
|
||||
# {'*': [
|
||||
# {'title': _('OpenStack Dashboard'), 'fileName': 'project.json': False}]}
|
||||
# ]
|
||||
#
|
||||
# If GRAFANA_URL is unspecified the dashboard file name must be specified
|
||||
# through the fileName attribute.
|
||||
#
|
||||
# Both with and without GRAFANA_URL, the links have an optional 'raw' attribute
|
||||
# which defaults to False if unspecified. If it is False, the value of 'path'
|
||||
# (or 'fileName', respectively) is interpreted as a dashboard name and a link
|
||||
# to the dashboard based on the dashboard's name will be generated. If it is
|
||||
# True, the value of 'path' or 'fileName' will be treated as a URL to be used
|
||||
# verbatim.
|
||||
|
||||
|
||||
|
||||
GRAFANA_URL = getattr(settings, 'GRAFANA_URL', None)
|
||||
|
||||
# If GRAFANA_URL is specified, an additional link will be shown that points to
|
||||
# Grafana's list of dashboards. If you do not wish this, set SHOW_GRAFANA_HOME
|
||||
# to False (by default this setting is True and the link will thus be shown).
|
||||
|
||||
SHOW_GRAFANA_HOME = getattr(settings, 'SHOW_GRAFANA_HOME', True)
|
||||
|
||||
ENABLE_LOG_MANAGEMENT_BUTTON = getattr(settings, 'ENABLE_LOG_MANAGEMENT_BUTTON', True)
|
||||
ENABLE_EVENT_MANAGEMENT_BUTTON = getattr(settings, 'ENABLE_EVENT_MANAGEMENT_BUTTON', False)
|
||||
|
||||
KIBANA_POLICY_RULE = getattr(settings, 'KIBANA_POLICY_RULE',
|
||||
'monitoring:kibana_access')
|
||||
KIBANA_POLICY_SCOPE = getattr(settings, 'KIBANA_POLICY_SCOPE',
|
||||
'monitoring')
|
||||
KIBANA_HOST = getattr(settings, 'KIBANA_HOST', 'http://192.168.10.6:5601/')
|
||||
|
||||
OPENSTACK_SSL_NO_VERIFY = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
OPENSTACK_SSL_CACERT = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
|
||||
POLICY_FILES = getattr(settings, 'POLICY_FILES', {})
|
||||
POLICY_FILES.update({'monitoring': 'monitoring_policy.yaml',}) # noqa
|
||||
setattr(settings, 'POLICY_FILES', POLICY_FILES)
|
||||
@@ -1,30 +0,0 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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.utils.translation import gettext_lazy as _
|
||||
from monitoring.config import local_settings as settings
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Monitoring(horizon.Dashboard):
|
||||
name = _("Monitoring")
|
||||
slug = "monitoring"
|
||||
panels = ('overview', 'alarmdefs', 'alarms', 'notifications',)
|
||||
default_panel = 'overview'
|
||||
policy_rules = (("monitoring", "monitoring:monitoring"),)
|
||||
permissions = (('openstack.services.' + settings.MONITORING_SERVICE_TYPE),)
|
||||
|
||||
|
||||
horizon.register(Monitoring)
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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.
|
||||
|
||||
PANEL = 'notifications'
|
||||
PANEL_DASHBOARD = 'monitoring'
|
||||
REMOVE_PANEL = True
|
||||
@@ -1,45 +0,0 @@
|
||||
# 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 monascaclient import exc
|
||||
|
||||
DASHBOARD = "monitoring"
|
||||
|
||||
# A list of applications to be added to INSTALLED_APPS.
|
||||
ADD_INSTALLED_APPS = ['monitoring']
|
||||
|
||||
# A list of angular modules to be added as dependencies to horizon app.
|
||||
ADD_ANGULAR_MODULES = ['monitoringApp']
|
||||
|
||||
# A list of javascript files to be included for all pages
|
||||
ADD_JS_FILES = ['monitoring/js/app.js',
|
||||
'monitoring/js/filters.js',
|
||||
'monitoring/js/controllers.js',
|
||||
'monitoring/js/directives.js',
|
||||
'monitoring/js/services.js',
|
||||
'monitoring/js/ng-tags-input.js']
|
||||
|
||||
ADD_SCSS_FILES = [
|
||||
'monitoring/css/alarm-create.scss']
|
||||
|
||||
# A dictionary of exception classes to be added to HORIZON['exceptions'].
|
||||
_RECOVERABLE_ERRORS = (exc.UnprocessableEntity, exc.Conflict,
|
||||
exc.BadRequest, exc.ConnectionError,
|
||||
exc.Forbidden, exc.InternalServerError)
|
||||
_NOT_FOUND_ERRORS = (exc.NotFound,)
|
||||
_UNAUTHORIZED_ERRORS = (exc.Unauthorized,)
|
||||
|
||||
ADD_EXCEPTIONS = {
|
||||
'recoverable': _RECOVERABLE_ERRORS,
|
||||
'not_found': _NOT_FOUND_ERRORS,
|
||||
'unauthorized': _UNAUTHORIZED_ERRORS,
|
||||
}
|
||||
@@ -1,576 +0,0 @@
|
||||
# Frank Kloeker <eumel@arcor.de>, 2018. #zanata
|
||||
# Robert Simai <robert.simai@suse.com>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2022-07-06 20:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-04-23 02:51+0000\n"
|
||||
"Last-Translator: Robert Simai <robert.simai@suse.com>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Name dient zur Identifizierung der Alarmdefinition."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" If for some transitions notifications should not be sent they can "
|
||||
"be\n"
|
||||
" disabled.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Wenn für einige Übergänge keine Benachrichtigungen gesendet werden "
|
||||
"sollen,\n"
|
||||
" können sie deaktiviert werden."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Description field can be used to describe alarm definition's\n"
|
||||
" purpose.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Beschreibung kann verwendet werden, um den Zweck der "
|
||||
"Alarmdefinition zu beschreiben."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Name dient zur Identifizierung der Alarmdefinition."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that "
|
||||
"should\n"
|
||||
" be sent when transitioning to another state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Benachrichtigungen enthält die Liste der Benachrichtigungen, "
|
||||
"die beim\n"
|
||||
" Übergang in einen anderen Status gesendet werden sollen."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Severity field allows to specify the importance of alarm\n"
|
||||
" definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Schweregrad ermöglicht es, die Wichtigkeit der Alarmdefinition "
|
||||
"anzugeben."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" A non-zero value in the period field indicates how frequently a "
|
||||
"notification\n"
|
||||
" should be resent (only valid for webhook urls).\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Ein Wert ungleich Null im Zeitraumfeld gibt an, wie häufig eine "
|
||||
"Benachrichtigung\n"
|
||||
" erneut gesendet werden soll (nur gültig für Webhook-URLs)."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Address field indicates the email address, URL, or PagerDuty "
|
||||
"service\n"
|
||||
" key to be notified.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Adresse zeigt die E-Mail-Adresse, URL oder den PagerDuty-\n"
|
||||
" Dienstschlüssel an, der benachrichtigt werden soll."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Expression field which if true, triggers a notification to be "
|
||||
"sent.\n"
|
||||
" See <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> for how to write an expression.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Expression-Feld, das, wenn es zutrifft, eine Benachrichtigung "
|
||||
"auslöst, die\n"
|
||||
" gesendet werden soll. Informationen zum Schreiben eines Ausdrucks "
|
||||
"finden Sie\n"
|
||||
" unter <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarmausdrücke</a> ."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the notification method.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Name wird verwendet, um die Benachrichtigungsmethode zu\n"
|
||||
" identifizieren."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that should "
|
||||
"be sent when transitioning to another ALARM state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Benachrichtigungen enthält die Liste der Benachrichtigungen, "
|
||||
"die beim\n"
|
||||
" Übergang in einen anderen ALARM-Status gesendet werden sollen."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Type field indicates how the notification is sent when an alarm is "
|
||||
"triggered.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Das Feld Typ gibt an, wie die Benachrichtigung gesendet wird, wenn ein "
|
||||
"Alarm\n"
|
||||
" ausgelöst wird."
|
||||
|
||||
msgid "+ Add more"
|
||||
msgstr "+ Fügen Sie weitere hinzu"
|
||||
|
||||
msgid "---Please select---"
|
||||
msgstr "---Bitte auswählen---"
|
||||
|
||||
msgid "A description of an alarm."
|
||||
msgstr "Eine Beschreibung eines Alarms."
|
||||
|
||||
msgid "A descriptive name of the notification method."
|
||||
msgstr "Ein beschreibender Name der Benachrichtigungsmethode."
|
||||
|
||||
msgid "AND"
|
||||
msgstr "UND"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Hinzufügen"
|
||||
|
||||
msgid "Add a match by"
|
||||
msgstr "Fügen Sie eine Übereinstimmung hinzu"
|
||||
|
||||
msgid "Address"
|
||||
msgstr "Adresse"
|
||||
|
||||
msgid "Address must contain a valid URL address."
|
||||
msgstr "Die Adresse muss eine gültige URL-Adresse enthalten."
|
||||
|
||||
msgid "Address must contain a valid email address."
|
||||
msgstr "Die Adresse muss eine gültige E-Mail-Adresse enthalten."
|
||||
|
||||
msgid "Alarm"
|
||||
msgstr "Alarm"
|
||||
|
||||
msgid "Alarm Actions"
|
||||
msgstr "Alarmaktionen"
|
||||
|
||||
msgid "Alarm Definition Details"
|
||||
msgstr "Details zur Alarmdefinition"
|
||||
|
||||
msgid "Alarm Definitions"
|
||||
msgstr "Alarmdefinitionen"
|
||||
|
||||
msgid "Alarm Details"
|
||||
msgstr "Alarmdetails"
|
||||
|
||||
msgid "Alarm History"
|
||||
msgstr "Alarmhistorie"
|
||||
|
||||
msgid "Alarm ID"
|
||||
msgstr "Alarm-ID"
|
||||
|
||||
msgid "Alarm Id"
|
||||
msgstr "Alarm-ID"
|
||||
|
||||
msgid "Alarm Metric Dimensions"
|
||||
msgstr "Alarm Metrische Abmessungen"
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition %s has been created"
|
||||
msgstr "Die Alarmdefinition %s wurde erstellt"
|
||||
|
||||
msgid "Alarm definition has been updated."
|
||||
msgstr "Die Alarmdefinition wurde aktualisiert."
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition with %s name already exists"
|
||||
msgstr "Die Alarmdefinition mit dem Namen %s ist bereits vorhanden"
|
||||
|
||||
msgid "Alarm has been created successfully."
|
||||
msgstr "Der Alarm wurde erfolgreich erstellt."
|
||||
|
||||
msgid "Alarm has been edited successfully."
|
||||
msgstr "Der Alarm wurde erfolgreich bearbeitet."
|
||||
|
||||
msgid "Alarms"
|
||||
msgstr "Alarm"
|
||||
|
||||
msgid "Alarms for "
|
||||
msgstr "Alarme für"
|
||||
|
||||
msgid "All Alarms"
|
||||
msgstr "Alle Alarme"
|
||||
|
||||
msgid "An alarm expression."
|
||||
msgstr "Ein Alarmausdruck."
|
||||
|
||||
msgid "An unique name of the alarm."
|
||||
msgstr "Ein eindeutiger Name des Alarms."
|
||||
|
||||
msgid "Associated Metrics"
|
||||
msgstr "Verbundene Metriken"
|
||||
|
||||
msgid "Browser local"
|
||||
msgstr "Browser lokal"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Stornieren"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
msgid "Could not retrieve alarm definitions"
|
||||
msgstr "Alarmdefinitionen konnten nicht abgerufen werden"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm for %s"
|
||||
msgstr "Der Alarm für %s konnte nicht abgerufen werden"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm history for %s"
|
||||
msgstr "Der Alarmverlauf für %s konnte nicht abgerufen werden"
|
||||
|
||||
msgid "Could not retrieve alarms"
|
||||
msgstr "Alarme konnten nicht abgerufen werden"
|
||||
|
||||
msgid "Could not retrieve notifications"
|
||||
msgstr "Benachrichtigungen konnten nicht abgerufen werden"
|
||||
|
||||
msgid "Create Alarm"
|
||||
msgstr "Alarm erstellen"
|
||||
|
||||
msgid "Create Alarm Definition"
|
||||
msgstr "Alarmdefinition erstellen"
|
||||
|
||||
msgid "Create Notification"
|
||||
msgstr "Benachrichtigung erstellen"
|
||||
|
||||
msgid "Create Notification Method"
|
||||
msgstr "Benachrichtigungsmethode erstellen"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Kritisch"
|
||||
|
||||
msgid "Delete Alarm"
|
||||
msgid_plural "Delete Alarms"
|
||||
msgstr[0] "Lösche Alarm"
|
||||
msgstr[1] "Lösche Alarme"
|
||||
|
||||
msgid "Delete Alarm Definition"
|
||||
msgid_plural "Delete Alarm Definitions"
|
||||
msgstr[0] "Lösche Alarmdefinition"
|
||||
msgstr[1] "Lösche Alarmdefinitionen"
|
||||
|
||||
msgid "Delete Notification"
|
||||
msgid_plural "Delete Notifications"
|
||||
msgstr[0] "Lösche Notifikation"
|
||||
msgstr[1] "Lösche Notifikationen"
|
||||
|
||||
msgid "Deleted Alarm"
|
||||
msgid_plural "Deleted Alarms"
|
||||
msgstr[0] "Gelöschter Alarm"
|
||||
msgstr[1] "Gelöschte Alarme"
|
||||
|
||||
msgid "Deleted Alarm Definition"
|
||||
msgid_plural "Deleted Alarm Definitions"
|
||||
msgstr[0] "Gelöschte Alarmdefinition"
|
||||
msgstr[1] "Gelöschte Alarmdefinitionen"
|
||||
|
||||
msgid "Deleted Notification"
|
||||
msgid_plural "Deleted Notifications"
|
||||
msgstr[0] "Gelöschte Notifikation"
|
||||
msgstr[1] "Gelöschte Notifikationen"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "Einzelheiten"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministisch"
|
||||
|
||||
msgid "Dimension(s)"
|
||||
msgstr "Maß(e)"
|
||||
|
||||
msgid "Edit Alarm"
|
||||
msgstr "Alarm bearbeiten"
|
||||
|
||||
msgid "Edit Alarm Definition"
|
||||
msgstr "Alarmdefinition bearbeiten"
|
||||
|
||||
msgid "Edit Alarm Definitions"
|
||||
msgstr "Bearbeiten Sie Alarmdefinitionen"
|
||||
|
||||
msgid "Edit Notification"
|
||||
msgstr "Benachrichtigung bearbeiten"
|
||||
|
||||
msgid "Expression"
|
||||
msgstr "Ausdruck"
|
||||
|
||||
msgid "Failed to present alarm history"
|
||||
msgstr "Alarmverlauf konnte nicht angezeigt werden"
|
||||
|
||||
msgid "Failed to validate name"
|
||||
msgstr "Der Name konnte nicht validiert werden"
|
||||
|
||||
msgid "Filter Alarms"
|
||||
msgstr "Filteralarme"
|
||||
|
||||
msgid "Graph Metric"
|
||||
msgstr "Grafische Metrik"
|
||||
|
||||
msgid "High"
|
||||
msgstr "Hoch"
|
||||
|
||||
msgid "Info"
|
||||
msgstr "Info"
|
||||
|
||||
msgid "Key"
|
||||
msgstr "Schlüssel"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Niedrig"
|
||||
|
||||
msgid "Match by"
|
||||
msgstr "Übereinstimmung mit"
|
||||
|
||||
msgid "Measurements for Alarms"
|
||||
msgstr "Messungen für Alarme"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Mittel"
|
||||
|
||||
msgid "Metric Dimensions"
|
||||
msgstr "Metrische Abmessungen"
|
||||
|
||||
msgid "Metric Name"
|
||||
msgstr "Metrischer Name"
|
||||
|
||||
msgid "Metric Names"
|
||||
msgstr "Metrik-Bezeichnungen"
|
||||
|
||||
msgid "Monitoring"
|
||||
msgstr "Überwachung"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgid "New State"
|
||||
msgstr "Neuer Status"
|
||||
|
||||
msgid "Next Page"
|
||||
msgstr "Nächste Seite"
|
||||
|
||||
msgid "No notifications available."
|
||||
msgstr "Keine Benachrichtigungen verfügbar."
|
||||
|
||||
msgid "None"
|
||||
msgstr "Keiner"
|
||||
|
||||
#, python-format
|
||||
msgid "Notification %s has already been deleted."
|
||||
msgstr "Die Benachrichtigung %s wurde bereits gelöscht."
|
||||
|
||||
msgid "Notification Method Details"
|
||||
msgstr "Details zur Benachrichtigungsmethode"
|
||||
|
||||
msgid "Notification has been edited successfully."
|
||||
msgstr "Die Benachrichtigung wurde erfolgreich bearbeitet."
|
||||
|
||||
msgid "Notification method has been created successfully."
|
||||
msgstr "Die Benachrichtigungsmethode wurde erfolgreich erstellt."
|
||||
|
||||
msgid ""
|
||||
"Notification methods. Notifications can be sent when an alarm state "
|
||||
"transition occurs."
|
||||
msgstr ""
|
||||
"Benachrichtigungsmethoden. Benachrichtigungen können gesendet werden, wenn "
|
||||
"ein Alarmstatusübergang auftritt."
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "Benachrichtigungen"
|
||||
|
||||
msgid "Notifications Enabled"
|
||||
msgstr "Benachrichtigungen aktiviert"
|
||||
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
msgid "OK Actions"
|
||||
msgstr "OK Aktionen"
|
||||
|
||||
msgid "OR"
|
||||
msgstr "ODER"
|
||||
|
||||
msgid "Old State"
|
||||
msgstr "Alter Status"
|
||||
|
||||
msgid "OpenStack Services"
|
||||
msgstr "OpenStack-Dienste"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Überblick"
|
||||
|
||||
msgid "Period"
|
||||
msgstr "Zeitraum"
|
||||
|
||||
msgid "Period must be zero except for type webhook."
|
||||
msgstr "Die Periode muss null sein, außer für den Typ webhook."
|
||||
|
||||
msgid "Previous Page"
|
||||
msgstr "Vorherige Seite"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "Grund"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Löschen"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "Speichern"
|
||||
|
||||
msgid "Save Notification"
|
||||
msgstr "Benachrichtigung speichern"
|
||||
|
||||
msgid "Select Notification"
|
||||
msgstr "Wählen Sie Benachrichtigung"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "Server"
|
||||
|
||||
msgid "Service :"
|
||||
msgstr "Dienst :"
|
||||
|
||||
msgid "Service Health"
|
||||
msgstr "Dienste-Gesundheit"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "Schwere"
|
||||
|
||||
msgid ""
|
||||
"Severity of an alarm. Must be either LOW, MEDIUM, HIGH or CRITICAL. Default "
|
||||
"is LOW."
|
||||
msgstr ""
|
||||
"Schweregrad eines Alarms. Muss entweder LOW, MEDIUM, HIGH oder CRITICAL "
|
||||
"sein. Standard ist LOW."
|
||||
|
||||
msgid "Show Alarm Definition"
|
||||
msgstr "Alarmdefinition anzeigen"
|
||||
|
||||
msgid "Show History"
|
||||
msgstr "Zeige die Geschichte"
|
||||
|
||||
msgid "State"
|
||||
msgstr "Zustand"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "einreichen"
|
||||
|
||||
msgid "The email/url address to notify."
|
||||
msgstr "Die E-Mail-/URL-Adresse, die benachrichtigt werden soll."
|
||||
|
||||
msgid "The metric dimensions used to create unique alarms."
|
||||
msgstr ""
|
||||
"Die metrischen Dimensionen, die zum Erstellen von eindeutigen Alarmen "
|
||||
"verwendet werden."
|
||||
|
||||
msgid "The notification period."
|
||||
msgstr "Der Benachrichtigungszeitraum"
|
||||
|
||||
msgid "The type of notification method (i.e. email)."
|
||||
msgstr "Der Typ der Benachrichtigungsmethode (z.B. E-Mail)."
|
||||
|
||||
msgid "Timestamp"
|
||||
msgstr "Zeitstempel"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
msgid "UTC"
|
||||
msgstr "koordinierte Weltzeit"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create alarm definition %s"
|
||||
msgstr "Die Alarmdefinition %s konnte nicht erstellt werden"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to list alarms: %s"
|
||||
msgstr "Alarme können nicht aufgelistet werden: %s"
|
||||
|
||||
msgid "Unable to retrieve alarm details."
|
||||
msgstr "Die Alarmdetails können nicht abgerufen werden."
|
||||
|
||||
msgid "Unable to retrieve metrics"
|
||||
msgstr "Die Messwerte konnten nicht abgerufen werden"
|
||||
|
||||
msgid "Unable to retrieve notification details."
|
||||
msgstr "Die Benachrichtigungsdetails konnten nicht abgerufen werden."
|
||||
|
||||
msgid "Undetermined"
|
||||
msgstr "Unbestimmt"
|
||||
|
||||
msgid "Undetermined Actions"
|
||||
msgstr "Unbestimmte Aktionen"
|
||||
|
||||
msgid "Unknown name"
|
||||
msgstr "Unbekannter Name"
|
||||
|
||||
#, python-format
|
||||
msgid "User %s does not have sufficient privileges to access Kibana"
|
||||
msgstr ""
|
||||
"Benutzer %s verfügt nicht über ausreichende Berechtigungen zum Zugriff auf "
|
||||
"Kibana"
|
||||
|
||||
msgid "Value"
|
||||
msgstr "Wert"
|
||||
|
||||
msgid "avg"
|
||||
msgstr "Durchschnitt"
|
||||
|
||||
msgid "count"
|
||||
msgstr "Anzahl"
|
||||
|
||||
msgid "last"
|
||||
msgstr "letzte"
|
||||
|
||||
msgid "max"
|
||||
msgstr "max"
|
||||
|
||||
msgid "min"
|
||||
msgstr "min"
|
||||
|
||||
msgid "sum"
|
||||
msgstr "sum"
|
||||
@@ -1,66 +0,0 @@
|
||||
# Frank Kloeker <eumel@arcor.de>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2018-03-06 16:04+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-02-10 09:29+0000\n"
|
||||
"Last-Translator: Frank Kloeker <eumel@arcor.de>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Hinzufügen"
|
||||
|
||||
msgid "Add a dimension"
|
||||
msgstr "Fügen Sie eine Dimension hinzu"
|
||||
|
||||
msgid "Comparator"
|
||||
msgstr "Vergleicher"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministisch"
|
||||
|
||||
msgid "Down"
|
||||
msgstr "Nieder"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Bearbeiten"
|
||||
|
||||
msgid "Function"
|
||||
msgstr "Funktion"
|
||||
|
||||
msgid "Matching Metrics"
|
||||
msgstr "Übereinstimmende Metriken"
|
||||
|
||||
msgid "Metric"
|
||||
msgstr "Metrik"
|
||||
|
||||
msgid "No metric available"
|
||||
msgstr "Keine Metrik verfügbar"
|
||||
|
||||
msgid "Operators"
|
||||
msgstr "Betreiber"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Löschen"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Einreichen"
|
||||
|
||||
msgid "Threshold"
|
||||
msgstr "Schwelle"
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Oben"
|
||||
|
||||
msgid "dimensions"
|
||||
msgstr "Maße"
|
||||
|
||||
msgid "name"
|
||||
msgstr "Name"
|
||||
@@ -1,644 +0,0 @@
|
||||
# Andi Chandler <andi@gowling.com>, 2016. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2017. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2018. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2020. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2022. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2022-07-06 20:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2022-05-25 09:28+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
"Language: en_GB\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" Each alarm definition is defined by its expression composed out of\n"
|
||||
" mathematical function, metric, time, times ,threshold and "
|
||||
"comparator for\n"
|
||||
" metric's value and the threshold. Time is the number of seconds for "
|
||||
"the measurement\n"
|
||||
" to be done. They can only be in a multiple of 60. Times is how many "
|
||||
"times in a row\n"
|
||||
" that expression must be true before triggering the alarm. Both time "
|
||||
"and times are\n"
|
||||
" optional and default to 60 and 1 respectively. Additionally it is "
|
||||
"possible to narrow\n"
|
||||
" evaluation of the alarm to certain entities by choosing their\n"
|
||||
" dimensions. The deterministic alarms never enter UNDETERMINED "
|
||||
"state.\n"
|
||||
" Use them for metrics that are received sporadically.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Each alarm definition is defined by its expression composed of\n"
|
||||
" mathematical function, metric, time, times, threshold and "
|
||||
"comparator for\n"
|
||||
" the metric's value and the threshold. Time is the number of seconds "
|
||||
"for the measurement\n"
|
||||
" to be done. They can only be in a multiple of 60. Times is how many "
|
||||
"times in a row\n"
|
||||
" that expression must be true before triggering the alarm. Both time "
|
||||
"and times are\n"
|
||||
" optional and default to 60 and 1 respectively. Additionally, it is "
|
||||
"possible to narrow\n"
|
||||
" the evaluation of the alarm to certain entities by choosing their\n"
|
||||
" dimensions. The deterministic alarms never enter an UNDETERMINED "
|
||||
"state.\n"
|
||||
" Use them for metrics that are received sporadically.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" If for some transitions notifications should not be sent they can "
|
||||
"be\n"
|
||||
" disabled.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" If for some transitions notifications should not be sent they can "
|
||||
"be\n"
|
||||
" disabled.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Description field can be used to describe alarm definition's\n"
|
||||
" purpose.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Description field can be used to describe alarm definition's\n"
|
||||
" purpose.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that "
|
||||
"should\n"
|
||||
" be sent when transitioning to another state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that "
|
||||
"should\n"
|
||||
" be sent when transitioning to another state.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Severity field allows to specify the importance of alarm\n"
|
||||
" definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Severity field allows to specify the importance of alarm\n"
|
||||
" definition.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" A non-zero value in the period field indicates how frequently a "
|
||||
"notification\n"
|
||||
" should be resent (only valid for webhook urls).\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" A non-zero value in the period field indicates how frequently a "
|
||||
"notification\n"
|
||||
" should be resent (only valid for webhook URLs).\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Address field indicates the email address, URL, or PagerDuty "
|
||||
"service\n"
|
||||
" key to be notified.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Address field indicates the email address, URL, or PagerDuty "
|
||||
"service\n"
|
||||
" key to be notified.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Expression field which if true, triggers a notification to be "
|
||||
"sent.\n"
|
||||
" See <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> for how to write an expression.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Expression field which if true, triggers a notification to be "
|
||||
"sent.\n"
|
||||
" See <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> for how to write an expression.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the notification method.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Name field is used to identify the notification method.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that should "
|
||||
"be sent when transitioning to another ALARM state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that should "
|
||||
"be sent when transitioning to another ALARM state.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Type field indicates how the notification is sent when an alarm is "
|
||||
"triggered.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" The Type field indicates how the notification is sent when an alarm is "
|
||||
"triggered.\n"
|
||||
" "
|
||||
|
||||
msgid "+ Add more"
|
||||
msgstr "+ Add more"
|
||||
|
||||
msgid "---Please select---"
|
||||
msgstr "---Please select---"
|
||||
|
||||
msgid "A description of an alarm."
|
||||
msgstr "A description of an alarm."
|
||||
|
||||
msgid "A descriptive name of the notification method."
|
||||
msgstr "A descriptive name of the notification method."
|
||||
|
||||
msgid "AND"
|
||||
msgstr "AND"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Add"
|
||||
|
||||
msgid "Add a match by"
|
||||
msgstr "Add a match by"
|
||||
|
||||
msgid "Address"
|
||||
msgstr "Address"
|
||||
|
||||
msgid "Address must contain a valid URL address."
|
||||
msgstr "Address must contain a valid URL address."
|
||||
|
||||
msgid "Address must contain a valid email address."
|
||||
msgstr "Address must contain a valid email address."
|
||||
|
||||
msgid "Alarm"
|
||||
msgstr "Alarm"
|
||||
|
||||
msgid "Alarm Actions"
|
||||
msgstr "Alarm Actions"
|
||||
|
||||
msgid "Alarm Definition Details"
|
||||
msgstr "Alarm Definition Details"
|
||||
|
||||
msgid "Alarm Definitions"
|
||||
msgstr "Alarm Definitions"
|
||||
|
||||
msgid "Alarm Details"
|
||||
msgstr "Alarm Details"
|
||||
|
||||
msgid "Alarm History"
|
||||
msgstr "Alarm History"
|
||||
|
||||
msgid "Alarm ID"
|
||||
msgstr "Alarm ID"
|
||||
|
||||
msgid "Alarm Id"
|
||||
msgstr "Alarm Id"
|
||||
|
||||
msgid "Alarm Metric Dimensions"
|
||||
msgstr "Alarm Metric Dimensions"
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition %s has been created"
|
||||
msgstr "Alarm definition %s has been created"
|
||||
|
||||
msgid "Alarm definition has been updated."
|
||||
msgstr "Alarm definition has been updated."
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition with %s name already exists"
|
||||
msgstr "Alarm definition with %s name already exists"
|
||||
|
||||
msgid "Alarm has been created successfully."
|
||||
msgstr "Alarm has been created successfully."
|
||||
|
||||
msgid "Alarm has been edited successfully."
|
||||
msgstr "Alarm has been edited successfully."
|
||||
|
||||
msgid "Alarms"
|
||||
msgstr "Alarms"
|
||||
|
||||
msgid "Alarms for "
|
||||
msgstr "Alarms for "
|
||||
|
||||
msgid "All Alarms"
|
||||
msgstr "All Alarms"
|
||||
|
||||
msgid "An alarm expression."
|
||||
msgstr "An alarm expression."
|
||||
|
||||
msgid "An unique name of the alarm."
|
||||
msgstr "An unique name of the alarm."
|
||||
|
||||
msgid "Associated Metrics"
|
||||
msgstr "Associated Metrics"
|
||||
|
||||
msgid "Browser local"
|
||||
msgstr "Browser local"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
msgid "Could not retrieve alarm definitions"
|
||||
msgstr "Could not retrieve alarm definitions"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm for %s"
|
||||
msgstr "Could not retrieve alarm for %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm history for %s"
|
||||
msgstr "Could not retrieve alarm history for %s"
|
||||
|
||||
msgid "Could not retrieve alarms"
|
||||
msgstr "Could not retrieve alarms"
|
||||
|
||||
msgid "Could not retrieve notifications"
|
||||
msgstr "Could not retrieve notifications"
|
||||
|
||||
msgid "Create Alarm"
|
||||
msgstr "Create Alarm"
|
||||
|
||||
msgid "Create Alarm Definition"
|
||||
msgstr "Create Alarm Definition"
|
||||
|
||||
msgid "Create Notification"
|
||||
msgstr "Create Notification"
|
||||
|
||||
msgid "Create Notification Method"
|
||||
msgstr "Create Notification Method"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Critical"
|
||||
|
||||
msgid "Delete Alarm"
|
||||
msgid_plural "Delete Alarms"
|
||||
msgstr[0] "Delete Alarm"
|
||||
msgstr[1] "Delete Alarms"
|
||||
|
||||
msgid "Delete Alarm Definition"
|
||||
msgid_plural "Delete Alarm Definitions"
|
||||
msgstr[0] "Delete Alarm Definition"
|
||||
msgstr[1] "Delete Alarm Definitions"
|
||||
|
||||
msgid "Delete Notification"
|
||||
msgid_plural "Delete Notifications"
|
||||
msgstr[0] "Delete Notification"
|
||||
msgstr[1] "Delete Notifications"
|
||||
|
||||
msgid "Deleted Alarm"
|
||||
msgid_plural "Deleted Alarms"
|
||||
msgstr[0] "Deleted Alarm"
|
||||
msgstr[1] "Deleted Alarms"
|
||||
|
||||
msgid "Deleted Alarm Definition"
|
||||
msgid_plural "Deleted Alarm Definitions"
|
||||
msgstr[0] "Deleted Alarm Definition"
|
||||
msgstr[1] "Deleted Alarm Definitions"
|
||||
|
||||
msgid "Deleted Notification"
|
||||
msgid_plural "Deleted Notifications"
|
||||
msgstr[0] "Deleted Notification"
|
||||
msgstr[1] "Deleted Notifications"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "Details"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Dimension(s)"
|
||||
msgstr "Dimension(s)"
|
||||
|
||||
msgid "Edit Alarm"
|
||||
msgstr "Edit Alarm"
|
||||
|
||||
msgid "Edit Alarm Definition"
|
||||
msgstr "Edit Alarm Definition"
|
||||
|
||||
msgid "Edit Alarm Definitions"
|
||||
msgstr "Edit Alarm Definitions"
|
||||
|
||||
msgid "Edit Notification"
|
||||
msgstr "Edit Notification"
|
||||
|
||||
msgid "Expression"
|
||||
msgstr "Expression"
|
||||
|
||||
msgid "Failed to present alarm history"
|
||||
msgstr "Failed to present alarm history"
|
||||
|
||||
msgid "Failed to validate name"
|
||||
msgstr "Failed to validate name"
|
||||
|
||||
msgid "Filter Alarms"
|
||||
msgstr "Filter Alarms"
|
||||
|
||||
msgid "Graph Metric"
|
||||
msgstr "Graph Metric"
|
||||
|
||||
msgid "High"
|
||||
msgstr "High"
|
||||
|
||||
msgid "Info"
|
||||
msgstr "Info"
|
||||
|
||||
msgid "Key"
|
||||
msgstr "Key"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Low"
|
||||
|
||||
msgid "Match by"
|
||||
msgstr "Match by"
|
||||
|
||||
msgid "Measurements for Alarms"
|
||||
msgstr "Measurements for Alarms"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medium"
|
||||
|
||||
msgid "Metric Dimensions"
|
||||
msgstr "Metric Dimensions"
|
||||
|
||||
msgid "Metric Name"
|
||||
msgstr "Metric Name"
|
||||
|
||||
msgid "Metric Names"
|
||||
msgstr "Metric Names"
|
||||
|
||||
msgid "Monitoring"
|
||||
msgstr "Monitoring"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgid "New State"
|
||||
msgstr "New State"
|
||||
|
||||
msgid "Next Page"
|
||||
msgstr "Next Page"
|
||||
|
||||
msgid "No notifications available."
|
||||
msgstr "No notifications available."
|
||||
|
||||
msgid "None"
|
||||
msgstr "None"
|
||||
|
||||
#, python-format
|
||||
msgid "Notification %s has already been deleted."
|
||||
msgstr "Notification %s has already been deleted."
|
||||
|
||||
msgid "Notification Method Details"
|
||||
msgstr "Notification Method Details"
|
||||
|
||||
msgid "Notification has been edited successfully."
|
||||
msgstr "Notification has been edited successfully."
|
||||
|
||||
msgid "Notification method has been created successfully."
|
||||
msgstr "Notification method has been created successfully."
|
||||
|
||||
msgid ""
|
||||
"Notification methods. Notifications can be sent when an alarm state "
|
||||
"transition occurs."
|
||||
msgstr ""
|
||||
"Notification methods. Notifications can be sent when an alarm state "
|
||||
"transition occurs."
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "Notifications"
|
||||
|
||||
msgid "Notifications Enabled"
|
||||
msgstr "Notifications Enabled"
|
||||
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
msgid "OK Actions"
|
||||
msgstr "OK Actions"
|
||||
|
||||
msgid "OR"
|
||||
msgstr "OR"
|
||||
|
||||
msgid "Old State"
|
||||
msgstr "Old State"
|
||||
|
||||
msgid "OpenStack Services"
|
||||
msgstr "OpenStack Services"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Overview"
|
||||
|
||||
msgid "Period"
|
||||
msgstr "Period"
|
||||
|
||||
msgid "Period must be zero except for type webhook."
|
||||
msgstr "Period must be zero except for type webhook."
|
||||
|
||||
msgid "Previous Page"
|
||||
msgstr "Previous Page"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "Reason"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Remove"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
msgid "Save Notification"
|
||||
msgstr "Save Notification"
|
||||
|
||||
msgid "Select Notification"
|
||||
msgstr "Select Notification"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "Servers"
|
||||
|
||||
msgid "Service :"
|
||||
msgstr "Service :"
|
||||
|
||||
msgid "Service Health"
|
||||
msgstr "Service Health"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "Severity"
|
||||
|
||||
msgid ""
|
||||
"Severity of an alarm. Must be either LOW, MEDIUM, HIGH or CRITICAL. Default "
|
||||
"is LOW."
|
||||
msgstr ""
|
||||
"Severity of an alarm. Must be either LOW, MEDIUM, HIGH or CRITICAL. Default "
|
||||
"is LOW."
|
||||
|
||||
msgid "Show Alarm Definition"
|
||||
msgstr "Show Alarm Definition"
|
||||
|
||||
msgid "Show History"
|
||||
msgstr "Show History"
|
||||
|
||||
msgid "State"
|
||||
msgstr "State"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Submit"
|
||||
|
||||
msgid "The email/url address to notify."
|
||||
msgstr "The email/URL address to notify."
|
||||
|
||||
msgid "The metric dimensions used to create unique alarms."
|
||||
msgstr "The metric dimensions used to create unique alarms."
|
||||
|
||||
msgid "The notification period."
|
||||
msgstr "The notification period."
|
||||
|
||||
msgid "The type of notification method (i.e. email)."
|
||||
msgstr "The type of notification method (i.e. email)."
|
||||
|
||||
msgid "Timestamp"
|
||||
msgstr "Timestamp"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
msgid "UTC"
|
||||
msgstr "UTC"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create alarm definition %s"
|
||||
msgstr "Unable to create alarm definition %s"
|
||||
|
||||
msgid "Unable to create alarm definition."
|
||||
msgstr "Unable to create alarm definition."
|
||||
|
||||
msgid "Unable to create the alarm."
|
||||
msgstr "Unable to create the alarm."
|
||||
|
||||
msgid "Unable to create the notification method."
|
||||
msgstr "Unable to create the notification method."
|
||||
|
||||
msgid "Unable to delete notification."
|
||||
msgstr "Unable to delete notification."
|
||||
|
||||
msgid "Unable to edit the alarm."
|
||||
msgstr "Unable to edit the alarm."
|
||||
|
||||
msgid "Unable to edit the notification."
|
||||
msgstr "Unable to edit the notification."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to list alarms: %s"
|
||||
msgstr "Unable to list alarms: %s"
|
||||
|
||||
msgid "Unable to retrieve alarm details."
|
||||
msgstr "Unable to retrieve alarm details."
|
||||
|
||||
msgid "Unable to retrieve metrics"
|
||||
msgstr "Unable to retrieve metrics"
|
||||
|
||||
msgid "Unable to retrieve notification details."
|
||||
msgstr "Unable to retrieve notification details."
|
||||
|
||||
msgid "Unable to retrieve notifications."
|
||||
msgstr "Unable to retrieve notifications."
|
||||
|
||||
msgid "Unable to update alarm definition."
|
||||
msgstr "Unable to update alarm definition."
|
||||
|
||||
msgid "Undetermined"
|
||||
msgstr "Undetermined"
|
||||
|
||||
msgid "Undetermined Actions"
|
||||
msgstr "Undetermined Actions"
|
||||
|
||||
msgid "Unknown name"
|
||||
msgstr "Unknown name"
|
||||
|
||||
#, python-format
|
||||
msgid "User %s does not have sufficient privileges to access Kibana"
|
||||
msgstr "User %s does not have sufficient privileges to access Kibana"
|
||||
|
||||
msgid "Value"
|
||||
msgstr "Value"
|
||||
|
||||
msgid "avg"
|
||||
msgstr "avg"
|
||||
|
||||
msgid "count"
|
||||
msgstr "count"
|
||||
|
||||
msgid "last"
|
||||
msgstr "last"
|
||||
|
||||
msgid "max"
|
||||
msgstr "max"
|
||||
|
||||
msgid "min"
|
||||
msgstr "min"
|
||||
|
||||
msgid "sum"
|
||||
msgstr "sum"
|
||||
@@ -1,72 +0,0 @@
|
||||
# Andi Chandler <andi@gowling.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2018-02-05 13:14+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-10-17 07:02+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
"Language: en_GB\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Add"
|
||||
|
||||
msgid "Add a dimension"
|
||||
msgstr "Add a dimension"
|
||||
|
||||
msgid "Comparator"
|
||||
msgstr "Comparator"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Down"
|
||||
msgstr "Down"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Edit"
|
||||
|
||||
msgid "Function"
|
||||
msgstr "Function"
|
||||
|
||||
msgid "Matching Metrics"
|
||||
msgstr "Matching Metrics"
|
||||
|
||||
msgid "Metric"
|
||||
msgstr "Metric"
|
||||
|
||||
msgid "No metric available"
|
||||
msgstr "No metric available"
|
||||
|
||||
msgid "Operators"
|
||||
msgstr "Operators"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Remove"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Submit"
|
||||
|
||||
msgid "Threshold"
|
||||
msgstr "Threshold"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Time"
|
||||
|
||||
msgid "Times"
|
||||
msgstr "Times"
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Up"
|
||||
|
||||
msgid "dimensions"
|
||||
msgstr "dimensions"
|
||||
|
||||
msgid "name"
|
||||
msgstr "name"
|
||||
@@ -1,562 +0,0 @@
|
||||
# suhartono <cloudsuhartono@gmail.com>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2022-07-06 20:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-05-09 04:46+0000\n"
|
||||
"Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Name field digunakan untuk mengidentifikasi definisi alarm. "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" If for some transitions notifications should not be sent they can "
|
||||
"be\n"
|
||||
" disabled.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Jika untuk beberapa notifikasi transisi seharusnya tidak terkirim, mereka "
|
||||
"dapat di non aktif."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Description field can be used to describe alarm definition's\n"
|
||||
" purpose.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Description field dapat digunakan untuk menjelaskan tujuan definisi alarm.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Name field digunakan untuk mengidentifikasi definisi alarm. "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that "
|
||||
"should\n"
|
||||
" be sent when transitioning to another state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Notifications field berisi daftar Notifications yang seharusnya\n"
|
||||
" dikirim saat beralih ke status bagian lain.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Severity field allows to specify the importance of alarm\n"
|
||||
" definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Severity field memungkinkan untuk menentukan pentingnya definisi alarm.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" A non-zero value in the period field indicates how frequently a "
|
||||
"notification\n"
|
||||
" should be resent (only valid for webhook urls).\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Nilai non-zero pada Period field menunjukkan seberapa sering sebuah "
|
||||
"notifikasi\n"
|
||||
" harus dikirim ulang (hanya berlaku untuk url webhook).\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Address field indicates the email address, URL, or PagerDuty "
|
||||
"service\n"
|
||||
" key to be notified.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Address field menunjukkan alamat email, URL, atau PagerDuty\n"
|
||||
" kunci layanan untuk diberitahu\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Expression field which if true, triggers a notification to be "
|
||||
"sent.\n"
|
||||
" See <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> for how to write an expression.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Expression field jika benar, memicu notifikasi untuk dikirim.\n"
|
||||
" Lihat <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> untuk bagaimana menulis sebuah "
|
||||
"ekspresi.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the notification method.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Name field digunakan untuk mengidentifikasi metode notifikasi. "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that should "
|
||||
"be sent when transitioning to another ALARM state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Notifications field berisi daftar Notifications yang harus dikirim saat "
|
||||
"beralih ke status ALARM lainnya.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Type field indicates how the notification is sent when an alarm is "
|
||||
"triggered.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Type field menunjukkan bagaimana notifikasi dikirim saat alarm dipicu. "
|
||||
|
||||
msgid "+ Add more"
|
||||
msgstr "+ Add more"
|
||||
|
||||
msgid "---Please select---"
|
||||
msgstr "---Please select---"
|
||||
|
||||
msgid "A description of an alarm."
|
||||
msgstr "Penjelasan alarm"
|
||||
|
||||
msgid "A descriptive name of the notification method."
|
||||
msgstr "Nama deskriptif dari metode notifikasi."
|
||||
|
||||
msgid "AND"
|
||||
msgstr "AND"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Add"
|
||||
|
||||
msgid "Add a match by"
|
||||
msgstr "Tambahkan kecocokan dengan"
|
||||
|
||||
msgid "Address"
|
||||
msgstr "Address"
|
||||
|
||||
msgid "Address must contain a valid URL address."
|
||||
msgstr "Alamat harus berisi alamat URL yang valid."
|
||||
|
||||
msgid "Address must contain a valid email address."
|
||||
msgstr "Alamat harus berisi alamat email yang valid."
|
||||
|
||||
msgid "Alarm"
|
||||
msgstr "Alarm"
|
||||
|
||||
msgid "Alarm Actions"
|
||||
msgstr "Alarm Actions"
|
||||
|
||||
msgid "Alarm Definition Details"
|
||||
msgstr "Alarm Definition Details"
|
||||
|
||||
msgid "Alarm Definitions"
|
||||
msgstr "Alarm Definitions"
|
||||
|
||||
msgid "Alarm Details"
|
||||
msgstr "Alarm Details"
|
||||
|
||||
msgid "Alarm History"
|
||||
msgstr "Alarm History"
|
||||
|
||||
msgid "Alarm ID"
|
||||
msgstr "Alarm ID"
|
||||
|
||||
msgid "Alarm Id"
|
||||
msgstr "Alarm Id"
|
||||
|
||||
msgid "Alarm Metric Dimensions"
|
||||
msgstr "Alarm Metric Dimensions"
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition %s has been created"
|
||||
msgstr "Alarm definition %s telah dibuat"
|
||||
|
||||
msgid "Alarm definition has been updated."
|
||||
msgstr "Definisi alarm telah diperbarui."
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition with %s name already exists"
|
||||
msgstr "Definisi alarm dengan nama %s sudah ada"
|
||||
|
||||
msgid "Alarm has been created successfully."
|
||||
msgstr "Alarm telah berhasil dibuat."
|
||||
|
||||
msgid "Alarm has been edited successfully."
|
||||
msgstr "Alarm telah berhasil diedit."
|
||||
|
||||
msgid "Alarms"
|
||||
msgstr "Alarm"
|
||||
|
||||
msgid "Alarms for "
|
||||
msgstr "Alarm untuk"
|
||||
|
||||
msgid "All Alarms"
|
||||
msgstr "Semua Alarms"
|
||||
|
||||
msgid "An alarm expression."
|
||||
msgstr "Sebuah ekspresi alarm."
|
||||
|
||||
msgid "An unique name of the alarm."
|
||||
msgstr "Nama unik dari alarm."
|
||||
|
||||
msgid "Associated Metrics"
|
||||
msgstr "Associated Metrics"
|
||||
|
||||
msgid "Browser local"
|
||||
msgstr "Browser lokal"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
msgid "Could not retrieve alarm definitions"
|
||||
msgstr "Tidak dapat mengambil definisi alarm"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm for %s"
|
||||
msgstr "Tidak dapat mengambil alarm untuk %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm history for %s"
|
||||
msgstr "Tidak dapat mengambil riwayat alarm untuk %s"
|
||||
|
||||
msgid "Could not retrieve alarms"
|
||||
msgstr "Tidak dapat mengambil alarm"
|
||||
|
||||
msgid "Could not retrieve notifications"
|
||||
msgstr "Tidak dapat mengambil notifikasi"
|
||||
|
||||
msgid "Create Alarm"
|
||||
msgstr "Create Alarm"
|
||||
|
||||
msgid "Create Alarm Definition"
|
||||
msgstr "Create Alarm Definition"
|
||||
|
||||
msgid "Create Notification"
|
||||
msgstr "Create Notification"
|
||||
|
||||
msgid "Create Notification Method"
|
||||
msgstr "Create Notification Method"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Critical"
|
||||
|
||||
msgid "Delete Alarm"
|
||||
msgid_plural "Delete Alarms"
|
||||
msgstr[0] "Delete Alarm"
|
||||
|
||||
msgid "Delete Alarm Definition"
|
||||
msgid_plural "Delete Alarm Definitions"
|
||||
msgstr[0] "Delete Alarm Definition"
|
||||
|
||||
msgid "Delete Notification"
|
||||
msgid_plural "Delete Notifications"
|
||||
msgstr[0] "Delete Notification"
|
||||
|
||||
msgid "Deleted Alarm"
|
||||
msgid_plural "Deleted Alarms"
|
||||
msgstr[0] "Deleted Alarm"
|
||||
|
||||
msgid "Deleted Alarm Definition"
|
||||
msgid_plural "Deleted Alarm Definitions"
|
||||
msgstr[0] "Deleted Alarm Definition"
|
||||
|
||||
msgid "Deleted Notification"
|
||||
msgid_plural "Deleted Notifications"
|
||||
msgstr[0] "Deleted Notification"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "Deskripsi"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "Rincian"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Dimension(s)"
|
||||
msgstr "Dimension(s)"
|
||||
|
||||
msgid "Edit Alarm"
|
||||
msgstr "Edit Alarm"
|
||||
|
||||
msgid "Edit Alarm Definition"
|
||||
msgstr "Edit Alarm Definition"
|
||||
|
||||
msgid "Edit Alarm Definitions"
|
||||
msgstr "Edit Alarm Definitions"
|
||||
|
||||
msgid "Edit Notification"
|
||||
msgstr "Edit Notification"
|
||||
|
||||
msgid "Expression"
|
||||
msgstr "Ekspresi"
|
||||
|
||||
msgid "Failed to present alarm history"
|
||||
msgstr "Gagal menampilkan riwayat alarm"
|
||||
|
||||
msgid "Failed to validate name"
|
||||
msgstr "Gagal memvalidasi nama"
|
||||
|
||||
msgid "Filter Alarms"
|
||||
msgstr "Filter Alarms"
|
||||
|
||||
msgid "Graph Metric"
|
||||
msgstr "Graph Metric"
|
||||
|
||||
msgid "High"
|
||||
msgstr "High (tinggi)"
|
||||
|
||||
msgid "Info"
|
||||
msgstr "Info"
|
||||
|
||||
msgid "Key"
|
||||
msgstr "Key"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Low (rendah)"
|
||||
|
||||
msgid "Match by"
|
||||
msgstr "Match by (cocok dengan)"
|
||||
|
||||
msgid "Measurements for Alarms"
|
||||
msgstr "Measurements untuk Alarms"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medium (sedang)"
|
||||
|
||||
msgid "Metric Dimensions"
|
||||
msgstr "Metric Dimensions"
|
||||
|
||||
msgid "Metric Name"
|
||||
msgstr "Metric Name"
|
||||
|
||||
msgid "Metric Names"
|
||||
msgstr "Metric Names"
|
||||
|
||||
msgid "Monitoring"
|
||||
msgstr "Monitoring"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgid "New State"
|
||||
msgstr "New State"
|
||||
|
||||
msgid "Next Page"
|
||||
msgstr "Next Page"
|
||||
|
||||
msgid "No notifications available."
|
||||
msgstr "Tidak ada pemberitahuan yang tersedia."
|
||||
|
||||
msgid "None"
|
||||
msgstr "None"
|
||||
|
||||
#, python-format
|
||||
msgid "Notification %s has already been deleted."
|
||||
msgstr "Notifikasi %s telah dihapus."
|
||||
|
||||
msgid "Notification Method Details"
|
||||
msgstr "Notification Method Details"
|
||||
|
||||
msgid "Notification has been edited successfully."
|
||||
msgstr "Notifikasi berhasil diedit."
|
||||
|
||||
msgid "Notification method has been created successfully."
|
||||
msgstr "Metode notifikasi telah berhasil dibuat."
|
||||
|
||||
msgid ""
|
||||
"Notification methods. Notifications can be sent when an alarm state "
|
||||
"transition occurs."
|
||||
msgstr ""
|
||||
"Metode notifikasi. Notifikasi dapat dikirim ketika transisi status alarm "
|
||||
"terjadi."
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "Notifikasi"
|
||||
|
||||
msgid "Notifications Enabled"
|
||||
msgstr "Notifikasi Diaktifkan"
|
||||
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
msgid "OK Actions"
|
||||
msgstr "OK Actions"
|
||||
|
||||
msgid "OR"
|
||||
msgstr "OR"
|
||||
|
||||
msgid "Old State"
|
||||
msgstr "Old State"
|
||||
|
||||
msgid "OpenStack Services"
|
||||
msgstr "OpenStack Services"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Overview"
|
||||
|
||||
msgid "Period"
|
||||
msgstr "Period"
|
||||
|
||||
msgid "Period must be zero except for type webhook."
|
||||
msgstr "Periode harus nol kecuali untuk jenis webhook."
|
||||
|
||||
msgid "Previous Page"
|
||||
msgstr "Previous Page"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "Reason"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Remove"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
msgid "Save Notification"
|
||||
msgstr "Save Notification"
|
||||
|
||||
msgid "Select Notification"
|
||||
msgstr "Select Notification"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "Servers"
|
||||
|
||||
msgid "Service :"
|
||||
msgstr "Service :"
|
||||
|
||||
msgid "Service Health"
|
||||
msgstr "Service Health"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "Severity"
|
||||
|
||||
msgid ""
|
||||
"Severity of an alarm. Must be either LOW, MEDIUM, HIGH or CRITICAL. Default "
|
||||
"is LOW."
|
||||
msgstr ""
|
||||
"Tingkat keparahan alarm. Harus berada di LOW, MEDIUM, HIGH or CRITICAL. atau "
|
||||
"KRITIS. Default-nya LOW."
|
||||
|
||||
msgid "Show Alarm Definition"
|
||||
msgstr "Show Alarm Definition"
|
||||
|
||||
msgid "Show History"
|
||||
msgstr "Show History"
|
||||
|
||||
msgid "State"
|
||||
msgstr "State"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Submit"
|
||||
|
||||
msgid "The email/url address to notify."
|
||||
msgstr "Alamat email/url untuk diberitahu"
|
||||
|
||||
msgid "The metric dimensions used to create unique alarms."
|
||||
msgstr "Dimensi metrik digunakan untuk membuat alarm unik."
|
||||
|
||||
msgid "The notification period."
|
||||
msgstr "Periode notifikasi."
|
||||
|
||||
msgid "The type of notification method (i.e. email)."
|
||||
msgstr "Jenis metode pemberitahuan (yaitu email)."
|
||||
|
||||
msgid "Timestamp"
|
||||
msgstr "Timestamp"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
msgid "UTC"
|
||||
msgstr "UTC"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create alarm definition %s"
|
||||
msgstr "Tidak dapat membuat definisi alarm %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to list alarms: %s"
|
||||
msgstr "Tidak dapat menampilkan alarm: %s"
|
||||
|
||||
msgid "Unable to retrieve alarm details."
|
||||
msgstr "Tidak dapat mengambil detail alarm."
|
||||
|
||||
msgid "Unable to retrieve metrics"
|
||||
msgstr "Tidak dapat mengambil metrik"
|
||||
|
||||
msgid "Unable to retrieve notification details."
|
||||
msgstr "Tidak dapat mengambil detail notifikasi."
|
||||
|
||||
msgid "Undetermined"
|
||||
msgstr "Undetermined"
|
||||
|
||||
msgid "Undetermined Actions"
|
||||
msgstr "Undetermined Actions"
|
||||
|
||||
msgid "Unknown name"
|
||||
msgstr "Nama tidak dikenal"
|
||||
|
||||
#, python-format
|
||||
msgid "User %s does not have sufficient privileges to access Kibana"
|
||||
msgstr "Pengguna %s tidak memiliki hak yang cukup untuk mengakses Kibana"
|
||||
|
||||
msgid "Value"
|
||||
msgstr "Value"
|
||||
|
||||
msgid "avg"
|
||||
msgstr "avg"
|
||||
|
||||
msgid "count"
|
||||
msgstr "count"
|
||||
|
||||
msgid "last"
|
||||
msgstr "last"
|
||||
|
||||
msgid "max"
|
||||
msgstr "max"
|
||||
|
||||
msgid "min"
|
||||
msgstr "min"
|
||||
|
||||
msgid "sum"
|
||||
msgstr "sum"
|
||||
@@ -1,66 +0,0 @@
|
||||
# suhartono <cloudsuhartono@gmail.com>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2018-03-06 16:04+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-02-28 02:09+0000\n"
|
||||
"Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Add"
|
||||
|
||||
msgid "Add a dimension"
|
||||
msgstr "Tambahkan dimensi"
|
||||
|
||||
msgid "Comparator"
|
||||
msgstr "Comparator"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Down"
|
||||
msgstr "Down"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Edit"
|
||||
|
||||
msgid "Function"
|
||||
msgstr "Function"
|
||||
|
||||
msgid "Matching Metrics"
|
||||
msgstr "Matching Metrics"
|
||||
|
||||
msgid "Metric"
|
||||
msgstr "Metric"
|
||||
|
||||
msgid "No metric available"
|
||||
msgstr "Tidak ada metrik yang tersedia"
|
||||
|
||||
msgid "Operators"
|
||||
msgstr "Operators"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Remove"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Submit"
|
||||
|
||||
msgid "Threshold"
|
||||
msgstr "Threshold"
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Up"
|
||||
|
||||
msgid "dimensions"
|
||||
msgstr "dimensi"
|
||||
|
||||
msgid "name"
|
||||
msgstr "nama"
|
||||
@@ -1,595 +0,0 @@
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Sayako Kondo <kondoh.sayako@jp.fujitsu.com>, 2016. #zanata
|
||||
# Shinya Kawabata <s-kawabata@wx.jp.nec.com>, 2016. #zanata
|
||||
# Shu Muto <shu.mutow@gmail.com>, 2016. #zanata
|
||||
# haruki yamanashi <h.yamanashi@jp.fujitsu.com>, 2016. #zanata
|
||||
# haruki yamanashi <h.yamanashi@jp.fujitsu.com>, 2017. #zanata
|
||||
# haruki yamanashi <h.yamanashi@jp.fujitsu.com>, 2018. #zanata
|
||||
# haruki yamanashi <h.yamanashi@jp.fujitsu.com>, 2021. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2022-07-06 20:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2021-03-12 09:09+0000\n"
|
||||
"Last-Translator: haruki yamanashi <h.yamanashi@jp.fujitsu.com>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 名前フィールドは、アラーム定義を特定するために使用されます。"
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" If for some transitions notifications should not be sent they can "
|
||||
"be\n"
|
||||
" disabled.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 特定のアラーム状態遷移時の通知を無効にすることができます。\n"
|
||||
" \n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Description field can be used to describe alarm definition's\n"
|
||||
" purpose.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 説明フィールドはアラーム定義の用途を説明するために使用されます。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 名前フィールドはアラーム定義を識別するために使用されます。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that "
|
||||
"should\n"
|
||||
" be sent when transitioning to another state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 通知フィールドには、アラーム状態が遷移した時に送信される通知のリスト"
|
||||
"が含まれます。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Severity field allows to specify the importance of alarm\n"
|
||||
" definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 重大度フィールドはアラーム定義の重要性を特定するために使用されま"
|
||||
"す。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" A non-zero value in the period field indicates how frequently a "
|
||||
"notification\n"
|
||||
" should be resent (only valid for webhook urls).\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 期間フィールドは、値が0でない場合どの頻度で通知が再送されるかを示しま"
|
||||
"す。(webhook の場合のみ有効です。)\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Address field indicates the email address, URL, or PagerDuty "
|
||||
"service\n"
|
||||
" key to be notified.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" アドレスフィールドは通知を受け取るメールアドレス、URL、または "
|
||||
"PagerDuty サービスキーを示します。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Expression field which if true, triggers a notification to be "
|
||||
"sent.\n"
|
||||
" See <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> for how to write an expression.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 条件式フィールドは、その式が真となった場合に通知が発行されます。条件式"
|
||||
"の記述方法は、 <a href=\"https://github.com/openstack/monasca-api/blob/"
|
||||
"master/docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a>を参照してください。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the notification method.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 名前フィールドは通知方法を識別するために使用されます。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that should "
|
||||
"be sent when transitioning to another ALARM state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 通知フィールドには、アラーム状態が遷移した時に送信される通知のリストが"
|
||||
"含まれます。\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Type field indicates how the notification is sent when an alarm is "
|
||||
"triggered.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 種別フィールドはアラームが発生した時にどのように通知が行われるかを示し"
|
||||
"ます。\n"
|
||||
" "
|
||||
|
||||
msgid "+ Add more"
|
||||
msgstr "+ さらに追加"
|
||||
|
||||
msgid "---Please select---"
|
||||
msgstr "---選択してください---"
|
||||
|
||||
msgid "A description of an alarm."
|
||||
msgstr "アラームの説明"
|
||||
|
||||
msgid "A descriptive name of the notification method."
|
||||
msgstr "通知方法の名前"
|
||||
|
||||
msgid "AND"
|
||||
msgstr "AND"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "追加"
|
||||
|
||||
msgid "Add a match by"
|
||||
msgstr "マッチの追加"
|
||||
|
||||
msgid "Address"
|
||||
msgstr "アドレス"
|
||||
|
||||
msgid "Address must contain a valid URL address."
|
||||
msgstr "アドレスフィールドには、有効な URL を入力する必要があります。"
|
||||
|
||||
msgid "Address must contain a valid email address."
|
||||
msgstr "アドレスフィールドには、有効なメールアドレスを入力する必要があります。"
|
||||
|
||||
msgid "Alarm"
|
||||
msgstr "アラーム"
|
||||
|
||||
msgid "Alarm Actions"
|
||||
msgstr "ALARM 時のアクション"
|
||||
|
||||
msgid "Alarm Definition Details"
|
||||
msgstr "アラーム定義の詳細"
|
||||
|
||||
msgid "Alarm Definitions"
|
||||
msgstr "アラーム定義"
|
||||
|
||||
msgid "Alarm Details"
|
||||
msgstr "アラームの詳細"
|
||||
|
||||
msgid "Alarm History"
|
||||
msgstr "アラーム履歴"
|
||||
|
||||
msgid "Alarm ID"
|
||||
msgstr "アラーム ID"
|
||||
|
||||
msgid "Alarm Id"
|
||||
msgstr "アラーム ID"
|
||||
|
||||
msgid "Alarm Metric Dimensions"
|
||||
msgstr "ディメンション"
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition %s has been created"
|
||||
msgstr "アラーム定義「%s」が作成されました"
|
||||
|
||||
msgid "Alarm definition has been updated."
|
||||
msgstr "アラーム定義が正常に更新されました。"
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition with %s name already exists"
|
||||
msgstr "アラーム定義「%s」は既に存在しています"
|
||||
|
||||
msgid "Alarm has been created successfully."
|
||||
msgstr "アラームが正常に作成されました。"
|
||||
|
||||
msgid "Alarm has been edited successfully."
|
||||
msgstr "アラームが正常に更新されました。"
|
||||
|
||||
msgid "Alarms"
|
||||
msgstr "アラーム"
|
||||
|
||||
msgid "Alarms for "
|
||||
msgstr "アラーム 対象:"
|
||||
|
||||
msgid "All Alarms"
|
||||
msgstr "全アラーム"
|
||||
|
||||
msgid "An alarm expression."
|
||||
msgstr "アラーム条件式"
|
||||
|
||||
msgid "An unique name of the alarm."
|
||||
msgstr "アラームの名前"
|
||||
|
||||
msgid "Associated Metrics"
|
||||
msgstr "関連するメトリック"
|
||||
|
||||
msgid "Browser local"
|
||||
msgstr "ローカルタイム"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "取り消し"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "閉じる"
|
||||
|
||||
msgid "Could not retrieve alarm definitions"
|
||||
msgstr "アラーム定義を取得できませんでした。"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm for %s"
|
||||
msgstr "アラームが取得できませんでした: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm history for %s"
|
||||
msgstr "アラーム履歴が取得できませんでした: %s"
|
||||
|
||||
msgid "Could not retrieve alarms"
|
||||
msgstr "アラームを取得できませんでした"
|
||||
|
||||
msgid "Could not retrieve notifications"
|
||||
msgstr "通知を取得できませんでした"
|
||||
|
||||
msgid "Create Alarm"
|
||||
msgstr "アラームの作成"
|
||||
|
||||
msgid "Create Alarm Definition"
|
||||
msgstr "アラーム定義の作成"
|
||||
|
||||
msgid "Create Notification"
|
||||
msgstr "通知の作成"
|
||||
|
||||
msgid "Create Notification Method"
|
||||
msgstr "通知方法の作成"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Critical"
|
||||
|
||||
msgid "Delete Alarm"
|
||||
msgid_plural "Delete Alarms"
|
||||
msgstr[0] "アラームの削除"
|
||||
|
||||
msgid "Delete Alarm Definition"
|
||||
msgid_plural "Delete Alarm Definitions"
|
||||
msgstr[0] "アラーム定義の削除"
|
||||
|
||||
msgid "Delete Notification"
|
||||
msgid_plural "Delete Notifications"
|
||||
msgstr[0] "通知の削除"
|
||||
|
||||
msgid "Deleted Alarm"
|
||||
msgid_plural "Deleted Alarms"
|
||||
msgstr[0] "削除されたアラーム"
|
||||
|
||||
msgid "Deleted Alarm Definition"
|
||||
msgid_plural "Deleted Alarm Definitions"
|
||||
msgstr[0] "削除されたアラーム定義"
|
||||
|
||||
msgid "Deleted Notification"
|
||||
msgid_plural "Deleted Notifications"
|
||||
msgstr[0] "削除された通知"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "説明"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "詳細"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Dimension(s)"
|
||||
msgstr "ディメンション"
|
||||
|
||||
msgid "Edit Alarm"
|
||||
msgstr "アラームの編集"
|
||||
|
||||
msgid "Edit Alarm Definition"
|
||||
msgstr "アラーム定義の編集"
|
||||
|
||||
msgid "Edit Alarm Definitions"
|
||||
msgstr "アラーム定義の編集"
|
||||
|
||||
msgid "Edit Notification"
|
||||
msgstr "通知の編集"
|
||||
|
||||
msgid "Expression"
|
||||
msgstr "条件式"
|
||||
|
||||
msgid "Failed to present alarm history"
|
||||
msgstr "アラーム履歴の表示に失敗しました"
|
||||
|
||||
msgid "Failed to validate name"
|
||||
msgstr "検証に失敗しました"
|
||||
|
||||
msgid "Filter Alarms"
|
||||
msgstr "アラームのフィルター"
|
||||
|
||||
msgid "Graph Metric"
|
||||
msgstr "グラフの表示"
|
||||
|
||||
msgid "High"
|
||||
msgstr "High"
|
||||
|
||||
msgid "Info"
|
||||
msgstr "情報"
|
||||
|
||||
msgid "Key"
|
||||
msgstr "キー"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Low"
|
||||
|
||||
msgid "Match by"
|
||||
msgstr "マッチ"
|
||||
|
||||
msgid "Measurements for Alarms"
|
||||
msgstr "アラームの測定項目"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medium"
|
||||
|
||||
msgid "Metric Dimensions"
|
||||
msgstr "ディメンション"
|
||||
|
||||
msgid "Metric Name"
|
||||
msgstr "メトリック名"
|
||||
|
||||
msgid "Metric Names"
|
||||
msgstr "メトリック名"
|
||||
|
||||
msgid "Monitoring"
|
||||
msgstr "モニタリング"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "名前"
|
||||
|
||||
msgid "New State"
|
||||
msgstr "後状態"
|
||||
|
||||
msgid "Next Page"
|
||||
msgstr "次のページ"
|
||||
|
||||
msgid "No notifications available."
|
||||
msgstr "通知がありません。"
|
||||
|
||||
msgid "None"
|
||||
msgstr "なし"
|
||||
|
||||
#, python-format
|
||||
msgid "Notification %s has already been deleted."
|
||||
msgstr "通知「%s」はすでに削除されています。"
|
||||
|
||||
msgid "Notification Method Details"
|
||||
msgstr "通知方法の詳細"
|
||||
|
||||
msgid "Notification has been edited successfully."
|
||||
msgstr "通知は正常に更新されました。"
|
||||
|
||||
msgid "Notification method has been created successfully."
|
||||
msgstr "通知方法は正常に作成されました。"
|
||||
|
||||
msgid ""
|
||||
"Notification methods. Notifications can be sent when an alarm state "
|
||||
"transition occurs."
|
||||
msgstr "通知方法。アラーム状態遷移が起こると通知が送られます。"
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "通知"
|
||||
|
||||
msgid "Notifications Enabled"
|
||||
msgstr "通知の有効化"
|
||||
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
msgid "OK Actions"
|
||||
msgstr "OK 時のアクション"
|
||||
|
||||
msgid "OR"
|
||||
msgstr "OR"
|
||||
|
||||
msgid "Old State"
|
||||
msgstr "前状態"
|
||||
|
||||
msgid "OpenStack Services"
|
||||
msgstr "OpenStack サービス"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "概要"
|
||||
|
||||
msgid "Period"
|
||||
msgstr "期間"
|
||||
|
||||
msgid "Period must be zero except for type webhook."
|
||||
msgstr "Webhook 以外のタイプを選択した場合、期間には0を指定してください。"
|
||||
|
||||
msgid "Previous Page"
|
||||
msgstr "前のページ"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "理由"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "削除"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
msgid "Save Notification"
|
||||
msgstr "通知の保存"
|
||||
|
||||
msgid "Select Notification"
|
||||
msgstr "通知の選択"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "サーバー"
|
||||
|
||||
msgid "Service :"
|
||||
msgstr "サービス :"
|
||||
|
||||
msgid "Service Health"
|
||||
msgstr "サービスヘルス"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "重大度"
|
||||
|
||||
msgid ""
|
||||
"Severity of an alarm. Must be either LOW, MEDIUM, HIGH or CRITICAL. Default "
|
||||
"is LOW."
|
||||
msgstr ""
|
||||
"アラームの重大度。LOW、MEDIUM、HIGH、または CRITICAL のいずれかです。デフォル"
|
||||
"トは LOW です。"
|
||||
|
||||
msgid "Show Alarm Definition"
|
||||
msgstr "アラーム定義の表示"
|
||||
|
||||
msgid "Show History"
|
||||
msgstr "履歴の表示"
|
||||
|
||||
msgid "State"
|
||||
msgstr "状態"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "ステータス"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "決定"
|
||||
|
||||
msgid "The email/url address to notify."
|
||||
msgstr "通知先の メール/URL アドレス"
|
||||
|
||||
msgid "The metric dimensions used to create unique alarms."
|
||||
msgstr "一意なアラームを生成するために使われるメトリックのディメンション"
|
||||
|
||||
msgid "The notification period."
|
||||
msgstr "通知が再送される期間(webhook の場合のみ有効)"
|
||||
|
||||
msgid "The type of notification method (i.e. email)."
|
||||
msgstr "通知方法の種別 (例 メール)"
|
||||
|
||||
msgid "Timestamp"
|
||||
msgstr "タイムスタンプ"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "種別"
|
||||
|
||||
msgid "UTC"
|
||||
msgstr "UTC"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create alarm definition %s"
|
||||
msgstr "アラーム定義「%s」を作成できません"
|
||||
|
||||
msgid "Unable to create alarm definition."
|
||||
msgstr "アラーム定義を作成できません。"
|
||||
|
||||
msgid "Unable to create the alarm."
|
||||
msgstr "アラームを作成できません。"
|
||||
|
||||
msgid "Unable to create the notification method."
|
||||
msgstr "通知方法を作成できません。"
|
||||
|
||||
msgid "Unable to delete notification."
|
||||
msgstr "通知を削除できません。"
|
||||
|
||||
msgid "Unable to edit the alarm."
|
||||
msgstr "アラームを編集できません。"
|
||||
|
||||
msgid "Unable to edit the notification."
|
||||
msgstr "通知を編集できません。"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to list alarms: %s"
|
||||
msgstr "アラームの一覧を表示できません: %s"
|
||||
|
||||
msgid "Unable to retrieve alarm details."
|
||||
msgstr "アラームの詳細を取得できません。"
|
||||
|
||||
msgid "Unable to retrieve metrics"
|
||||
msgstr "メトリックを取得できません"
|
||||
|
||||
msgid "Unable to retrieve notification details."
|
||||
msgstr "通知の詳細を取得できません"
|
||||
|
||||
msgid "Unable to retrieve notifications."
|
||||
msgstr "通知を取得できませんでした。"
|
||||
|
||||
msgid "Unable to update alarm definition."
|
||||
msgstr "アラーム定義を更新できません。"
|
||||
|
||||
msgid "Undetermined"
|
||||
msgstr "Undetermined"
|
||||
|
||||
msgid "Undetermined Actions"
|
||||
msgstr "UNDETERMINED 時のアクション"
|
||||
|
||||
msgid "Unknown name"
|
||||
msgstr "不明"
|
||||
|
||||
#, python-format
|
||||
msgid "User %s does not have sufficient privileges to access Kibana"
|
||||
msgstr "ユーザー %s は Kibana にアクセスするための権限がありません"
|
||||
|
||||
msgid "Value"
|
||||
msgstr "値"
|
||||
|
||||
msgid "avg"
|
||||
msgstr "avg"
|
||||
|
||||
msgid "count"
|
||||
msgstr "count"
|
||||
|
||||
msgid "last"
|
||||
msgstr "last"
|
||||
|
||||
msgid "max"
|
||||
msgstr "max"
|
||||
|
||||
msgid "min"
|
||||
msgstr "min"
|
||||
|
||||
msgid "sum"
|
||||
msgstr "sum"
|
||||
@@ -1,67 +0,0 @@
|
||||
# haruki yamanashi <h.yamanashi@jp.fujitsu.com>, 2016. #zanata
|
||||
# haruki yamanashi <h.yamanashi@jp.fujitsu.com>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2018-01-18 08:05+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-01-24 05:46+0000\n"
|
||||
"Last-Translator: haruki yamanashi <h.yamanashi@jp.fujitsu.com>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "追加"
|
||||
|
||||
msgid "Add a dimension"
|
||||
msgstr "ディメンションの追加"
|
||||
|
||||
msgid "Comparator"
|
||||
msgstr "比較演算子"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Down"
|
||||
msgstr "Down"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "編集"
|
||||
|
||||
msgid "Function"
|
||||
msgstr "関数"
|
||||
|
||||
msgid "Matching Metrics"
|
||||
msgstr "マッチしたメトリック"
|
||||
|
||||
msgid "Metric"
|
||||
msgstr "メトリック"
|
||||
|
||||
msgid "No metric available"
|
||||
msgstr "メトリックが取得できません"
|
||||
|
||||
msgid "Operators"
|
||||
msgstr "演算子"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "削除"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "決定"
|
||||
|
||||
msgid "Threshold"
|
||||
msgstr "閾値"
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Up"
|
||||
|
||||
msgid "dimensions"
|
||||
msgstr "ディメンション"
|
||||
|
||||
msgid "name"
|
||||
msgstr "名前"
|
||||
@@ -1,558 +0,0 @@
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Ian Y. Choi <ianyrchoi@gmail.com>, 2018. #zanata
|
||||
# Sungjin Kang <gang.sungjin@gmail.com>, 2018. #zanata
|
||||
# Ian Y. Choi <ianyrchoi@gmail.com>, 2020. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2022-07-06 20:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2020-05-05 11:49+0000\n"
|
||||
"Last-Translator: Ian Y. Choi <ianyrchoi@gmail.com>\n"
|
||||
"Language-Team: Korean (South Korea)\n"
|
||||
"Language: ko_KR\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"이름 필드는 알림 정의를 식별하는데 사용됩니다."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" If for some transitions notifications should not be sent they can "
|
||||
"be\n"
|
||||
" disabled.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"일부 전환의 경우 알림을 보내지 않아야 한다면 알림을 비활성화할 수 있습니다."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Description field can be used to describe alarm definition's\n"
|
||||
" purpose.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 설명 필드는 알람 정의 목적을 설명하는데 사용할 수\n"
|
||||
" 있습니다.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the alarm definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 이름 필드는 알림 정의를 식별하는데 사용됩니다.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that "
|
||||
"should\n"
|
||||
" be sent when transitioning to another state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"알림 필드에는 다른 상태로 전환할 때 전송해야 하는 알림 목록이 포함됩니다."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Severity field allows to specify the importance of alarm\n"
|
||||
" definition.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Severity(심각도) 필드는 경보 정의에 대한 중요성을 \n"
|
||||
" 지정할 수 있습니다.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" A non-zero value in the period field indicates how frequently a "
|
||||
"notification\n"
|
||||
" should be resent (only valid for webhook urls).\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"기간 필드에 0이 아닌 값은 알림이 얼마나 자주 응답해야 하는지를 나타냅니다(웹 "
|
||||
"후크 URL에만 유효)."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Address field indicates the email address, URL, or PagerDuty "
|
||||
"service\n"
|
||||
" key to be notified.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" 주소 필드는 알림을 받을 이메일 주소, URL 또는\n"
|
||||
" PagerDuty 서비스 키를 나타냅니다.\n"
|
||||
" "
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Expression field which if true, triggers a notification to be "
|
||||
"sent.\n"
|
||||
" See <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" "
|
||||
"target=\"_blank\">Alarm Expressions</a> for how to write an expression.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"이 표현 필드는 True이면, 전송할 알림이 트리거됩니다.\n"
|
||||
"자세한 내용은 <a href=\"https://github.com/openstack/monasca-api/blob/master/"
|
||||
"docs/monasca-api-spec.md#alarm-definition-expressions\" target=\"_blank\">을 "
|
||||
"참조하십시오. 알람 표현식 </a> 은 표현 작성방법에 관한 것입니다."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Name field is used to identify the notification method.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"이름 필드는 알림 메서드를 식별하는데 사용됩니다."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Notifications field contains the list of Notifications that should "
|
||||
"be sent when transitioning to another ALARM state.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"알림 필드에는 다른 ALARM 상태로 전환할 때 전송해야 하는 알림 목록이 포함됩니"
|
||||
"다."
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" The Type field indicates how the notification is sent when an alarm is "
|
||||
"triggered.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"타입 필드는 알람이 트리거될 때 알림을 보내는 방법을 나타냅니다."
|
||||
|
||||
msgid "+ Add more"
|
||||
msgstr "+ 더 추가"
|
||||
|
||||
msgid "---Please select---"
|
||||
msgstr "---선택 해주세요---"
|
||||
|
||||
msgid "A description of an alarm."
|
||||
msgstr "알람에 대한 설명입니다."
|
||||
|
||||
msgid "A descriptive name of the notification method."
|
||||
msgstr "통지 방법에 대한 설명 이름."
|
||||
|
||||
msgid "AND"
|
||||
msgstr "AND"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "추가"
|
||||
|
||||
msgid "Add a match by"
|
||||
msgstr "일치 항목 추가"
|
||||
|
||||
msgid "Address"
|
||||
msgstr "주소"
|
||||
|
||||
msgid "Address must contain a valid URL address."
|
||||
msgstr "주소는 유효한 URL 주소를 포함해야 합니다."
|
||||
|
||||
msgid "Address must contain a valid email address."
|
||||
msgstr "주소는 유효한 이메일 주소를 포함해야 합니다."
|
||||
|
||||
msgid "Alarm"
|
||||
msgstr "알람"
|
||||
|
||||
msgid "Alarm Actions"
|
||||
msgstr "알람 동작"
|
||||
|
||||
msgid "Alarm Definition Details"
|
||||
msgstr "알람 정의 세부 사항"
|
||||
|
||||
msgid "Alarm Definitions"
|
||||
msgstr "알람 정의"
|
||||
|
||||
msgid "Alarm Details"
|
||||
msgstr "알람 상세 정보"
|
||||
|
||||
msgid "Alarm History"
|
||||
msgstr "알람 이력"
|
||||
|
||||
msgid "Alarm ID"
|
||||
msgstr "알람 ID"
|
||||
|
||||
msgid "Alarm Id"
|
||||
msgstr "알람 Id"
|
||||
|
||||
msgid "Alarm Metric Dimensions"
|
||||
msgstr "알람 메트릭 치수"
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition %s has been created"
|
||||
msgstr "알람 정의 %s 가 생성되었습니다."
|
||||
|
||||
msgid "Alarm definition has been updated."
|
||||
msgstr "알람 정의가 업데이트 되었습니다."
|
||||
|
||||
#, python-format
|
||||
msgid "Alarm definition with %s name already exists"
|
||||
msgstr "%s 이름으로 되어있는 알람 정의가 이미 있습니다."
|
||||
|
||||
msgid "Alarm has been created successfully."
|
||||
msgstr "알람을 성공적으로 생성하였습니다."
|
||||
|
||||
msgid "Alarm has been edited successfully."
|
||||
msgstr "알람을 성공적으로 수정하였습니다."
|
||||
|
||||
msgid "Alarms"
|
||||
msgstr "알람"
|
||||
|
||||
msgid "Alarms for "
|
||||
msgstr "에 대한 알람"
|
||||
|
||||
msgid "All Alarms"
|
||||
msgstr "모든 알람"
|
||||
|
||||
msgid "An alarm expression."
|
||||
msgstr "알람 표현식."
|
||||
|
||||
msgid "An unique name of the alarm."
|
||||
msgstr "알람에 대한 고유한 이름."
|
||||
|
||||
msgid "Associated Metrics"
|
||||
msgstr "연관된 메트릭"
|
||||
|
||||
msgid "Browser local"
|
||||
msgstr "브라우저 로컬"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "취소"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "닫기"
|
||||
|
||||
msgid "Could not retrieve alarm definitions"
|
||||
msgstr "알람 정의를 찾을 수 없습니다."
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm for %s"
|
||||
msgstr "%s 에 대한 알람을 찾을 수 없습니다"
|
||||
|
||||
#, python-format
|
||||
msgid "Could not retrieve alarm history for %s"
|
||||
msgstr "%s 에 대한 알람 내역을 찾을 수 없습니다"
|
||||
|
||||
msgid "Could not retrieve alarms"
|
||||
msgstr "알람을 찾지 못했습니다"
|
||||
|
||||
msgid "Could not retrieve notifications"
|
||||
msgstr "알림을 가져올 수 없습니다"
|
||||
|
||||
msgid "Create Alarm"
|
||||
msgstr "알람 생성"
|
||||
|
||||
msgid "Create Alarm Definition"
|
||||
msgstr "알람 정의 생성"
|
||||
|
||||
msgid "Create Notification"
|
||||
msgstr "알림 생성"
|
||||
|
||||
msgid "Create Notification Method"
|
||||
msgstr "알림 메서드 생성"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Critical"
|
||||
|
||||
msgid "Delete Alarm"
|
||||
msgid_plural "Delete Alarms"
|
||||
msgstr[0] "알람 삭제"
|
||||
|
||||
msgid "Delete Alarm Definition"
|
||||
msgid_plural "Delete Alarm Definitions"
|
||||
msgstr[0] "알람 정의 삭제"
|
||||
|
||||
msgid "Delete Notification"
|
||||
msgid_plural "Delete Notifications"
|
||||
msgstr[0] "알림 삭제"
|
||||
|
||||
msgid "Deleted Alarm"
|
||||
msgid_plural "Deleted Alarms"
|
||||
msgstr[0] "알람 삭제됨"
|
||||
|
||||
msgid "Deleted Alarm Definition"
|
||||
msgid_plural "Deleted Alarm Definitions"
|
||||
msgstr[0] "삭제된 알람 정의"
|
||||
|
||||
msgid "Deleted Notification"
|
||||
msgid_plural "Deleted Notifications"
|
||||
msgstr[0] "알림 삭제됨"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "설명"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "상세 정보"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Dimension(s)"
|
||||
msgstr "차원"
|
||||
|
||||
msgid "Edit Alarm"
|
||||
msgstr "알람 수정"
|
||||
|
||||
msgid "Edit Alarm Definition"
|
||||
msgstr "알람 정의 수정"
|
||||
|
||||
msgid "Edit Alarm Definitions"
|
||||
msgstr "알람 정의 수정"
|
||||
|
||||
msgid "Edit Notification"
|
||||
msgstr "알림 수정"
|
||||
|
||||
msgid "Expression"
|
||||
msgstr "표현식"
|
||||
|
||||
msgid "Failed to present alarm history"
|
||||
msgstr "알람 이력을 표시하는데 실패했습니다."
|
||||
|
||||
msgid "Failed to validate name"
|
||||
msgstr "이름을 확인하지 못했습니다."
|
||||
|
||||
msgid "Filter Alarms"
|
||||
msgstr "알람 필터"
|
||||
|
||||
msgid "Graph Metric"
|
||||
msgstr "그래프 메트릭"
|
||||
|
||||
msgid "High"
|
||||
msgstr "높음"
|
||||
|
||||
msgid "Info"
|
||||
msgstr "정보"
|
||||
|
||||
msgid "Key"
|
||||
msgstr "키"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "낮음"
|
||||
|
||||
msgid "Match by"
|
||||
msgstr "일치"
|
||||
|
||||
msgid "Measurements for Alarms"
|
||||
msgstr "알람에 대한 측정"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "보통"
|
||||
|
||||
msgid "Metric Dimensions"
|
||||
msgstr "메트릭 치수"
|
||||
|
||||
msgid "Metric Name"
|
||||
msgstr "메트릭 이름"
|
||||
|
||||
msgid "Metric Names"
|
||||
msgstr "메트릭 이름"
|
||||
|
||||
msgid "Monitoring"
|
||||
msgstr "모니터링"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "이름"
|
||||
|
||||
msgid "New State"
|
||||
msgstr "새 상태"
|
||||
|
||||
msgid "Next Page"
|
||||
msgstr "다음 페이지"
|
||||
|
||||
msgid "No notifications available."
|
||||
msgstr "사용가능한 알림이 없습니다."
|
||||
|
||||
msgid "None"
|
||||
msgstr "None"
|
||||
|
||||
#, python-format
|
||||
msgid "Notification %s has already been deleted."
|
||||
msgstr "알림 %s 가 이미 삭제되었습니다."
|
||||
|
||||
msgid "Notification Method Details"
|
||||
msgstr "알림 메소드 상세 정보"
|
||||
|
||||
msgid "Notification has been edited successfully."
|
||||
msgstr "알림 수정을 성공적으로 완료하였습니다."
|
||||
|
||||
msgid "Notification method has been created successfully."
|
||||
msgstr "통지 방법이 성공적으로 생성되었습니다."
|
||||
|
||||
msgid ""
|
||||
"Notification methods. Notifications can be sent when an alarm state "
|
||||
"transition occurs."
|
||||
msgstr "알림 방식. 알림은 알람 상태 변경이 일어날 때 전송됩니다."
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "알림"
|
||||
|
||||
msgid "Notifications Enabled"
|
||||
msgstr "알림 활성화됨"
|
||||
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
msgid "OK Actions"
|
||||
msgstr "OK 동작"
|
||||
|
||||
msgid "OR"
|
||||
msgstr "OR"
|
||||
|
||||
msgid "Old State"
|
||||
msgstr "이전 상태"
|
||||
|
||||
msgid "OpenStack Services"
|
||||
msgstr "OpenStack 서비스"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "개요"
|
||||
|
||||
msgid "Period"
|
||||
msgstr "기간"
|
||||
|
||||
msgid "Period must be zero except for type webhook."
|
||||
msgstr "주기는 타입 웹 후크를 제외하고 0이어야 합니다."
|
||||
|
||||
msgid "Previous Page"
|
||||
msgstr "이전 페이지"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "원인"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "제거"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "저장"
|
||||
|
||||
msgid "Save Notification"
|
||||
msgstr "알림 저장"
|
||||
|
||||
msgid "Select Notification"
|
||||
msgstr "알림 선택"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "서버"
|
||||
|
||||
msgid "Service :"
|
||||
msgstr "서비스:"
|
||||
|
||||
msgid "Service Health"
|
||||
msgstr "서비스 상태"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "심각도"
|
||||
|
||||
msgid ""
|
||||
"Severity of an alarm. Must be either LOW, MEDIUM, HIGH or CRITICAL. Default "
|
||||
"is LOW."
|
||||
msgstr ""
|
||||
"알람에 대한 심각도입니다. LOW, MEDIUM, HIGH, CRITICAL 중 하나이어야 합니다. "
|
||||
"디폴트는 LOW 입니다."
|
||||
|
||||
msgid "Show Alarm Definition"
|
||||
msgstr "알람 정의 확인"
|
||||
|
||||
msgid "Show History"
|
||||
msgstr "이력 확인"
|
||||
|
||||
msgid "State"
|
||||
msgstr "상태"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "상태"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "제출"
|
||||
|
||||
msgid "The email/url address to notify."
|
||||
msgstr "통지할 이메일/url 주소"
|
||||
|
||||
msgid "The metric dimensions used to create unique alarms."
|
||||
msgstr "유일한 알람을 생성하는데 사용하기 위한 계량적 부피"
|
||||
|
||||
msgid "The notification period."
|
||||
msgstr "통지 주기"
|
||||
|
||||
msgid "The type of notification method (i.e. email)."
|
||||
msgstr "알림 메소드 타입(i.e. email)."
|
||||
|
||||
msgid "Timestamp"
|
||||
msgstr "타임스탬프"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "타입"
|
||||
|
||||
msgid "UTC"
|
||||
msgstr "UTC"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create alarm definition %s"
|
||||
msgstr "알람 정의 %s 를 생성할 수 없습니다."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to list alarms: %s"
|
||||
msgstr "알람 목록을 불러오지 못했습니다: %s"
|
||||
|
||||
msgid "Unable to retrieve alarm details."
|
||||
msgstr "알람 세부 정보를 찾을 수 없습니다."
|
||||
|
||||
msgid "Unable to retrieve metrics"
|
||||
msgstr "측정 항목을 찾을 수 없습니다."
|
||||
|
||||
msgid "Unable to retrieve notification details."
|
||||
msgstr "알림 세부 정보를 가져올 수 없습니다."
|
||||
|
||||
msgid "Undetermined"
|
||||
msgstr "정해지지 않음"
|
||||
|
||||
msgid "Undetermined Actions"
|
||||
msgstr "정해지지 않은 동작"
|
||||
|
||||
msgid "Unknown name"
|
||||
msgstr "알 수 없는 이름"
|
||||
|
||||
#, python-format
|
||||
msgid "User %s does not have sufficient privileges to access Kibana"
|
||||
msgstr "사용자 %s 에게 Kibana 에 접근하기 위한 충분한 권한이 없습니다."
|
||||
|
||||
msgid "Value"
|
||||
msgstr "값"
|
||||
|
||||
msgid "avg"
|
||||
msgstr "avg"
|
||||
|
||||
msgid "count"
|
||||
msgstr "count"
|
||||
|
||||
msgid "last"
|
||||
msgstr "last"
|
||||
|
||||
msgid "max"
|
||||
msgstr "max"
|
||||
|
||||
msgid "min"
|
||||
msgstr "min"
|
||||
|
||||
msgid "sum"
|
||||
msgstr "sum"
|
||||
@@ -1,67 +0,0 @@
|
||||
# Sungjin Kang <gang.sungjin@gmail.com>, 2018. #zanata
|
||||
# Ian Y. Choi <ianyrchoi@gmail.com>, 2020. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: monasca-ui VERSION\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2020-04-16 08:55+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2020-05-05 11:49+0000\n"
|
||||
"Last-Translator: Ian Y. Choi <ianyrchoi@gmail.com>\n"
|
||||
"Language-Team: Korean (South Korea)\n"
|
||||
"Language: ko_KR\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "추가"
|
||||
|
||||
msgid "Add a dimension"
|
||||
msgstr "차원 추가"
|
||||
|
||||
msgid "Comparator"
|
||||
msgstr "Comparator"
|
||||
|
||||
msgid "Deterministic"
|
||||
msgstr "Deterministic"
|
||||
|
||||
msgid "Down"
|
||||
msgstr "Down"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "편집"
|
||||
|
||||
msgid "Function"
|
||||
msgstr "기능"
|
||||
|
||||
msgid "Matching Metrics"
|
||||
msgstr "지표 일치"
|
||||
|
||||
msgid "Metric"
|
||||
msgstr "메트릭"
|
||||
|
||||
msgid "No metric available"
|
||||
msgstr "사용가능한 메트릭이 없습니다"
|
||||
|
||||
msgid "Operators"
|
||||
msgstr "연산자"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "제거"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "제출"
|
||||
|
||||
msgid "Threshold"
|
||||
msgstr "임계값"
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Up"
|
||||
|
||||
msgid "dimensions"
|
||||
msgstr "dimensions"
|
||||
|
||||
msgid "name"
|
||||
msgstr "name"
|
||||
@@ -1,21 +0,0 @@
|
||||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
||||
@@ -1,31 +0,0 @@
|
||||
# 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 gettext_lazy as _
|
||||
|
||||
|
||||
class NotificationType(object):
|
||||
EMAIL = "EMAIL"
|
||||
WEBHOOK = "WEBHOOK"
|
||||
PAGERDUTY = "PAGERDUTY"
|
||||
|
||||
|
||||
EMAIL_VALIDATOR = validators.EmailValidator(
|
||||
message=_("Address must contain a valid email address."))
|
||||
WEBHOOK_VALIDATOR = validators.URLValidator(
|
||||
message=_("Address must contain a valid URL address."))
|
||||
|
||||
URL_PREFIX = 'horizon:monitoring:notifications:'
|
||||
TEMPLATE_PREFIX = 'monitoring/notifications/'
|
||||
@@ -1,160 +0,0 @@
|
||||
# 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.utils.functional import cached_property # noqa
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from monitoring import api
|
||||
from monitoring.notifications import constants
|
||||
|
||||
|
||||
READONLY_TEXTINPUT = forms.TextInput(attrs={'readonly': 'readonly'})
|
||||
|
||||
|
||||
class BaseNotificationMethodForm(forms.SelfHandlingForm):
|
||||
@classmethod
|
||||
def _instantiate(cls, request, *args, **kwargs):
|
||||
return cls(request, *args, **kwargs)
|
||||
|
||||
def _init_fields(self, readOnly=False, create=False):
|
||||
required = True
|
||||
textWidget = None
|
||||
selectWidget = None
|
||||
readOnlyTextInput = READONLY_TEXTINPUT
|
||||
readOnlySelectInput = forms.Select(attrs={'disabled': 'disabled'})
|
||||
if readOnly:
|
||||
required = False
|
||||
textWidget = readOnlyTextInput
|
||||
selectWidget = readOnlySelectInput
|
||||
|
||||
choices = [(n['type'], n['type'].capitalize()) for n in self.notification_types]
|
||||
choices = sorted(choices, key=lambda c: c[0])
|
||||
period_choices = [(0, '0'), (60, '60')]
|
||||
|
||||
self.fields['name'] = forms.CharField(label=_("Name"),
|
||||
required=required,
|
||||
max_length="250",
|
||||
widget=textWidget,
|
||||
help_text=_("A descriptive name of "
|
||||
"the notification method."))
|
||||
self.fields['type'] = forms.ChoiceField(
|
||||
label=_("Type"),
|
||||
required=required,
|
||||
widget=selectWidget,
|
||||
choices=choices,
|
||||
initial=constants.NotificationType.EMAIL,
|
||||
help_text=_("The type of notification method (i.e. email)."))
|
||||
self.fields['address'] = forms.CharField(label=_("Address"),
|
||||
required=required,
|
||||
max_length="512",
|
||||
widget=textWidget,
|
||||
help_text=_("The email/url address to notify."))
|
||||
self.fields['period'] = forms.ChoiceField(label=_("Period"),
|
||||
widget=selectWidget,
|
||||
choices=period_choices,
|
||||
initial=0,
|
||||
required=required,
|
||||
help_text=_("The notification period."))
|
||||
|
||||
def clean_period(self):
|
||||
'''Check to make sure period is zero unless type is WEBHOOK.
|
||||
|
||||
For WEBHOOK period must be set to 0 or 60.
|
||||
'''
|
||||
data = self.cleaned_data
|
||||
if data['type'] != constants.NotificationType.WEBHOOK and data['period'] != '0':
|
||||
raise forms.ValidationError(
|
||||
_('Period must be zero except for type webhook.'))
|
||||
|
||||
return data['period']
|
||||
|
||||
def clean_address(self):
|
||||
'''Check to make sure address is the correct format depending on the type of notification.
|
||||
|
||||
'''
|
||||
data = self.cleaned_data
|
||||
if data['type'] == constants.NotificationType.EMAIL:
|
||||
constants.EMAIL_VALIDATOR(data['address'])
|
||||
elif data['type'] == constants.NotificationType.WEBHOOK:
|
||||
constants.WEBHOOK_VALIDATOR(data['address'])
|
||||
elif data['type'] == constants.NotificationType.PAGERDUTY:
|
||||
pass
|
||||
|
||||
return data['address']
|
||||
|
||||
@cached_property
|
||||
def notification_types(self):
|
||||
return api.monitor.notification_type_list(self.request)
|
||||
|
||||
|
||||
class CreateMethodForm(BaseNotificationMethodForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateMethodForm, self).__init__(request, *args, **kwargs)
|
||||
super(CreateMethodForm, self)._init_fields(readOnly=False)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
api.monitor.notification_create(
|
||||
request,
|
||||
name=data['name'],
|
||||
type=data['type'],
|
||||
address=data['address'],
|
||||
period=int(data['period']))
|
||||
messages.success(request,
|
||||
_('Notification method has been created '
|
||||
'successfully.'))
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to create the notification method.'))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class DetailMethodForm(BaseNotificationMethodForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(DetailMethodForm, self).__init__(request, *args, **kwargs)
|
||||
super(DetailMethodForm, self)._init_fields(readOnly=True)
|
||||
|
||||
def handle(self, request, data):
|
||||
return True
|
||||
|
||||
|
||||
class EditMethodForm(BaseNotificationMethodForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(EditMethodForm, self).__init__(request, *args, **kwargs)
|
||||
super(EditMethodForm, self)._init_fields(readOnly=False)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
kwargs = {}
|
||||
kwargs['notification_id'] = self.initial['id']
|
||||
kwargs['name'] = data['name']
|
||||
kwargs['type'] = data['type']
|
||||
kwargs['address'] = data['address']
|
||||
kwargs['period'] = int(data['period'])
|
||||
api.monitor.notification_update(
|
||||
request,
|
||||
**kwargs
|
||||
)
|
||||
messages.success(request,
|
||||
_('Notification has been edited successfully.'))
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to edit the notification.'))
|
||||
return False
|
||||
return True
|
||||
@@ -1,26 +0,0 @@
|
||||
# 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.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from monitoring import dashboard
|
||||
|
||||
|
||||
class Notifications(horizon.Panel):
|
||||
name = _("Notifications")
|
||||
slug = 'notifications'
|
||||
|
||||
|
||||
dashboard.Monitoring.register(Notifications)
|
||||
@@ -1,111 +0,0 @@
|
||||
# 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.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
|
||||
from monitoring import api
|
||||
from monitoring.notifications import constants
|
||||
|
||||
|
||||
class DeleteNotification(tables.DeleteAction):
|
||||
name = "delete_notification"
|
||||
verbose_name = _("Delete Notification")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
"Delete Notification",
|
||||
"Delete Notifications",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
"Deleted Notification",
|
||||
"Deleted Notifications",
|
||||
count
|
||||
)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
try:
|
||||
api.monitor.notification_delete(request, obj_id)
|
||||
except Exception:
|
||||
exceptions.handle(
|
||||
request, _('Unable to delete notification.'))
|
||||
|
||||
|
||||
class CreateNotification(tables.LinkAction):
|
||||
name = "create_notification"
|
||||
verbose_name = _("Create Notification")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("notification", "notification:create"),)
|
||||
ajax = True
|
||||
|
||||
def get_link_url(self):
|
||||
url = constants.URL_PREFIX + 'notification_create'
|
||||
return reverse(url)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class EditNotification(tables.LinkAction):
|
||||
name = "edit_notification"
|
||||
verbose_name = _("Edit Notification")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
|
||||
def get_link_url(self, datum):
|
||||
return reverse(constants.URL_PREFIX + 'notification_edit',
|
||||
args=(datum['id'], ))
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
return True
|
||||
|
||||
|
||||
class NotificationsFilterAction(tables.FilterAction):
|
||||
def filter(self, table, notifications, filter_string):
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [notif for notif in notifications
|
||||
if q in notif['name'].lower()]
|
||||
|
||||
|
||||
class NotificationsTable(tables.DataTable):
|
||||
target = tables.Column('name', verbose_name=_('Name'))
|
||||
type = tables.Column('type', verbose_name=_('Type'))
|
||||
address = tables.Column('address', verbose_name=_('Address'))
|
||||
period = tables.Column('period', verbose_name=_('Period'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return obj['id']
|
||||
|
||||
def get_object_display(self, obj):
|
||||
return obj['name']
|
||||
|
||||
class Meta(object):
|
||||
name = "notifications"
|
||||
verbose_name = _("Notifications")
|
||||
row_actions = (EditNotification, DeleteNotification, )
|
||||
table_actions = (CreateNotification, NotificationsFilterAction,
|
||||
DeleteNotification)
|
||||
@@ -1,38 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}create_notif_method_form{% endblock %}
|
||||
{% block form_action %}{{ action_url }}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Create Notification Method" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right quota-dynamic">
|
||||
<h3>{% trans "Description" %}:</h3>
|
||||
<p>{% blocktrans %}
|
||||
The Name field is used to identify the notification method.
|
||||
{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}
|
||||
The Type field indicates how the notification is sent when an alarm is triggered.
|
||||
{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}
|
||||
The Address field indicates the email address, URL, or PagerDuty service
|
||||
key to be notified.
|
||||
{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}
|
||||
A non-zero value in the period field indicates how frequently a notification
|
||||
should be resent (only valid for webhook urls).
|
||||
{% endblocktrans %}</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{{ cancel_url }}" class="btn btn-default cancel">{% trans "Cancel" %}</a>
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Create Notification Method" %}" />
|
||||
{% endblock %}
|
||||
@@ -1,17 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}detail_notif_method_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:project:monitoring_notifications:methods:detail' notification_method.id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Notification Method Details" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{% url 'horizon:project:monitoring_notifications:index' %}" class="btn btn-default cancel">{% trans "Close" %}</a>
|
||||
{% endblock %}
|
||||
@@ -1,23 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}edit_method_form{% endblock %}
|
||||
{% block form_action %}{{ action_url }}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Edit Notification" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<h3>{% trans "Description" %}:</h3>
|
||||
<p>{% blocktrans %}
|
||||
The Name field is used to identify the notification method.
|
||||
{% endblocktrans %}</p>
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{{ cancel_url }}" class="btn btn-default cancel">{% trans "Cancel" %}</a>
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Save Notification" %}" />
|
||||
{% endblock %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Create Notification Method' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title="Create Notification Method" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'monitoring/notifications/_create.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Notification Method Details' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title="Notification Method Details" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'monitoring/notifications/_detail.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Create Notification' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title="Create Notification" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'monitoring/notifications/_edit.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,28 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Monitoring' %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include 'horizon/common/_page_header.html' with title=_("Notifications") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item-truncate">{% trans "Monitoring" %}</li>
|
||||
<li class="breadcrumb-item-truncate active">{% trans "Notifications" %}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if prev_page_offset != None %}
|
||||
<a href="?page_offset={{ prev_page_offset }}" class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Previous Page' %}</a>
|
||||
{% endif %}
|
||||
{% if page_offset %}
|
||||
<a href="?page_offset={{ page_offset }}" class="btn btn-default btn-sm" style="position:relative;top:-35px;">{% trans 'Next Page' %}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,65 +0,0 @@
|
||||
# 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.urls import reverse
|
||||
from unittest.mock import patch
|
||||
|
||||
from monitoring.notifications import constants
|
||||
from monitoring.test import helpers
|
||||
|
||||
|
||||
INDEX_URL = reverse(
|
||||
constants.URL_PREFIX + 'index')
|
||||
CREATE_URL = reverse(
|
||||
constants.URL_PREFIX + 'notification_create')
|
||||
EDIT_URL = reverse(
|
||||
constants.URL_PREFIX + 'notification_edit', args=('12345',))
|
||||
|
||||
|
||||
class AlarmsTest(helpers.TestCase):
|
||||
|
||||
def test_index(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['notification_list'],
|
||||
'notification_list.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertEqual(mock.notification_list.call_count, 2)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/notifications/index.html')
|
||||
|
||||
def test_notifications_create(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['notification_type_list'],
|
||||
'notification_type_list.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(CREATE_URL)
|
||||
self.assertEqual(mock. notification_type_list.call_count, 1)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/notifications/_create.html')
|
||||
|
||||
def test_notifications_edit(self):
|
||||
with patch('monitoring.api.monitor', **{
|
||||
'spec_set': ['notification_get', 'notification_type_list'],
|
||||
'notification_get.return_value': {
|
||||
'alarm_actions': []
|
||||
},
|
||||
'notification_type_list.return_value': [],
|
||||
}) as mock:
|
||||
res = self.client.get(EDIT_URL)
|
||||
self.assertEqual(mock.notification_get.call_count, 1)
|
||||
self.assertEqual(mock.notification_type_list.call_count, 1)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/notifications/_edit.html')
|
||||
@@ -1,26 +0,0 @@
|
||||
# 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.urls import re_path
|
||||
|
||||
from monitoring.notifications import views
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', views.IndexView.as_view(), name='index'),
|
||||
re_path(r'^notification_create$',
|
||||
views.NotificationCreateView.as_view(),
|
||||
name='notification_create'),
|
||||
re_path(r'^notification_edit/(?P<id>[^/]+)$',
|
||||
views.NotificationEditView.as_view(),
|
||||
name='notification_edit')
|
||||
]
|
||||
@@ -1,169 +0,0 @@
|
||||
# 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.contrib import messages
|
||||
from django.core.paginator import EmptyPage
|
||||
from django.core.paginator import Paginator
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon.utils import functions as utils
|
||||
|
||||
from monitoring import api
|
||||
from monitoring.notifications import constants
|
||||
from monitoring.notifications import forms as notification_forms
|
||||
from monitoring.notifications import tables as notification_tables
|
||||
|
||||
from openstack_dashboard import policy
|
||||
|
||||
PREV_PAGE_LIMIT = 100
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = notification_tables.NotificationsTable
|
||||
template_name = constants.TEMPLATE_PREFIX + 'index.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(IndexView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get_data(self):
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
results = []
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
limit = utils.get_page_size(self.request)
|
||||
try:
|
||||
results = api.monitor.notification_list(self.request, page_offset, limit)
|
||||
paginator = Paginator(results, limit)
|
||||
results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
results = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve notifications"))
|
||||
return results
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(IndexView, self).get_context_data(**kwargs)
|
||||
contacts = []
|
||||
results = []
|
||||
num_results = 0
|
||||
prev_page_stack = []
|
||||
page_offset = self.request.GET.get('page_offset')
|
||||
|
||||
if 'prev_page_stack' in self.request.session:
|
||||
prev_page_stack = self.request.session['prev_page_stack']
|
||||
|
||||
if page_offset is None:
|
||||
page_offset = 0
|
||||
prev_page_stack = []
|
||||
else:
|
||||
page_offset = int(page_offset)
|
||||
|
||||
limit = utils.get_page_size(self.request)
|
||||
try:
|
||||
# To judge whether there is next page, get LIMIT + 1
|
||||
results = api.monitor.notification_list(self.request, page_offset,
|
||||
limit + 1)
|
||||
num_results = len(results)
|
||||
paginator = Paginator(results, limit)
|
||||
contacts = paginator.page(1)
|
||||
except EmptyPage:
|
||||
contacts = paginator.page(paginator.num_pages)
|
||||
except Exception:
|
||||
messages.error(self.request, _("Could not retrieve notifications"))
|
||||
return context
|
||||
|
||||
context["contacts"] = contacts
|
||||
|
||||
if num_results < limit + 1:
|
||||
context["page_offset"] = None
|
||||
else:
|
||||
context["page_offset"] = page_offset + limit
|
||||
|
||||
if page_offset in prev_page_stack:
|
||||
index = prev_page_stack.index(page_offset)
|
||||
prev_page_stack = prev_page_stack[0:index]
|
||||
|
||||
prev_page_offset = prev_page_stack[-1] if prev_page_stack else None
|
||||
if prev_page_offset is not None:
|
||||
context["prev_page_offset"] = prev_page_offset
|
||||
|
||||
if len(prev_page_stack) > PREV_PAGE_LIMIT:
|
||||
del prev_page_stack[0]
|
||||
prev_page_stack.append(page_offset)
|
||||
self.request.session['prev_page_stack'] = prev_page_stack
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class NotificationCreateView(forms.ModalFormView):
|
||||
form_class = notification_forms.CreateMethodForm
|
||||
template_name = constants.TEMPLATE_PREFIX + 'create.html'
|
||||
success_url = reverse_lazy(constants.URL_PREFIX + 'index')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(NotificationCreateView, self). \
|
||||
get_context_data(**kwargs)
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
action = constants.URL_PREFIX + 'notification_create'
|
||||
context["action_url"] = reverse(action)
|
||||
return context
|
||||
|
||||
|
||||
class NotificationEditView(forms.ModalFormView):
|
||||
form_class = notification_forms.EditMethodForm
|
||||
template_name = constants.TEMPLATE_PREFIX + 'edit.html'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(NotificationEditView, 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.notification_get(self.request, id)
|
||||
return self._object
|
||||
except Exception:
|
||||
redirect = reverse(constants.URL_PREFIX + 'index')
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve notification details.'),
|
||||
redirect=redirect)
|
||||
return None
|
||||
|
||||
def get_initial(self):
|
||||
self.notification = self.get_object()
|
||||
return self.notification
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(NotificationEditView, self).get_context_data(**kwargs)
|
||||
id = self.kwargs['id']
|
||||
context["cancel_url"] = self.get_success_url()
|
||||
context["action_url"] = reverse(constants.URL_PREFIX +
|
||||
'notification_edit', args=(id,))
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy(constants.URL_PREFIX + 'index',)
|
||||
@@ -1,25 +0,0 @@
|
||||
# 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.conf import settings
|
||||
|
||||
URL_PREFIX = 'horizon:monitoring:overview:'
|
||||
TEMPLATE_PREFIX = 'monitoring/overview/'
|
||||
|
||||
prefix = settings.STATIC_URL or ''
|
||||
CRITICAL_ICON = prefix + 'monitoring/img/critical-icon.png'
|
||||
WARNING_ICON = prefix + 'monitoring/img/warning-icon.png'
|
||||
OK_ICON = prefix + 'monitoring/img/ok-icon.png'
|
||||
UNKNOWN_ICON = prefix + 'monitoring/img/unknown-icon.png'
|
||||
NOTFOUND_ICON = prefix + 'monitoring/img/notfound-icon.png'
|
||||
@@ -1,27 +0,0 @@
|
||||
# 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.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from monitoring import dashboard
|
||||
|
||||
|
||||
class Overview(horizon.Panel):
|
||||
name = _("Overview")
|
||||
slug = 'overview'
|
||||
|
||||
|
||||
dashboard.Monitoring.register(Overview)
|
||||
@@ -1,64 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans 'Monitoring' %}{% endblock %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item-truncate">{% trans "Monitoring" %}</li>
|
||||
<li class="breadcrumb-item-truncate active">{% trans "Overview" %}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include 'horizon/common/_page_header.html' with title=_("Monitoring") %}
|
||||
{% endblock page_header %}
|
||||
{% block main %}
|
||||
<div style="padding: 3px;">
|
||||
{% if grafana_url %}
|
||||
{% if show_grafana_home %}
|
||||
<a target="Grafana Home" href="{{grafana_url}}" class="btn btn-primary">
|
||||
<span class="fa fa-bar-chart"></span>
|
||||
Grafana Home
|
||||
</a>
|
||||
{% endif %}
|
||||
{% for dashboard in dashboards %}
|
||||
{% if dashboard.raw %}
|
||||
<a target={{ dashboard.title }} href="{{ dashboard.path }}" class="btn btn-default">
|
||||
{% else %}
|
||||
<a target={{ dashboard.title }} href="{{grafana_url}}/dashboard/db/{{ dashboard.path }}" class="btn btn-default">
|
||||
{% endif %}
|
||||
<span class="fa fa-bar-chart"></span>
|
||||
{% trans dashboard.title %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for dashboard in dashboards %}
|
||||
{% if dashboard.raw %}
|
||||
<a target={{ dashboard.title }} href="{{ dashboard.fileName }}" class="btn btn-default">
|
||||
{% else %}
|
||||
<a target={{ dashboard.title }} href="/grafana/index.html#/dashboard/file/{{ dashboard.fileName }}?api={{api}}" class="btn btn-default">
|
||||
{% endif %}
|
||||
<span class="fa fa-bar-chart"></span>
|
||||
{% trans dashboard.title %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if can_access_kibana %}
|
||||
{% if enable_log_management_button or enable_event_management_button %}
|
||||
<a target="dashboard" href="{% url 'horizon:monitoring:overview:kibana_proxy' url='/app/kibana' %}" class="btn btn-default">
|
||||
<span class="fa fa-bar-chart"></span>
|
||||
{% if enable_log_management_button %}
|
||||
{% if enable_event_management_button %}
|
||||
Log and Event Management
|
||||
{% else %}
|
||||
Log Management
|
||||
{% endif %}
|
||||
{% elif enable_event_management_button %}
|
||||
Event Management
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'monitoring/overview/monitor.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,71 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<style>
|
||||
.chicklet-container {
|
||||
border: 1px solid #F1F1F1;
|
||||
color:black;
|
||||
font-weight: bold;
|
||||
background-color: white;
|
||||
padding: 14px 35px 14px 14px;
|
||||
margin-bottom: 18px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.base-chicklet {
|
||||
border: 3px solid;
|
||||
color: #7C7C7C;
|
||||
background-color: white;
|
||||
display:inline-block;
|
||||
padding:0px;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.chicklet-success {
|
||||
border-color: #36A32C;
|
||||
}
|
||||
.chicklet-notfound {
|
||||
border-color: #e5e5e5;
|
||||
}
|
||||
.chicklet-unknown {
|
||||
border-color: #626262;
|
||||
}
|
||||
.chicklet-error {
|
||||
border-color: #9E1213;
|
||||
}
|
||||
.chicklet-warning {
|
||||
border-color: #D6C037;
|
||||
}
|
||||
.status-icon-holder {
|
||||
background-color: #EDEEF0;
|
||||
float: left;
|
||||
padding-top:10px;
|
||||
padding-left: 6px;
|
||||
padding-right: 2px;
|
||||
padding-bottom: 10px;
|
||||
border-right:2px solid;
|
||||
border-color: #D8D7DD;
|
||||
}
|
||||
.service-text {
|
||||
float: right;
|
||||
margin: 10px;
|
||||
min-width: 60px;
|
||||
}
|
||||
</style>
|
||||
<div style="border: 1px solid #dddddd;">
|
||||
<div ng-controller = "monitoringController"
|
||||
ng-init="fetchStatus('{% get_static_prefix %}')">
|
||||
<div class="chicklet-container" ng-repeat="group in _serviceModel">
|
||||
<div>
|
||||
{$ group.name $}
|
||||
</div>
|
||||
<div ng-repeat="service in group.services" style="display:inline-block; margin-bottom:4px; margin-right:4px;">
|
||||
<a href="{% url 'horizon:monitoring:alarms:index' %}alarm/{$ service.name $}">
|
||||
<div class="btn {$ service.class $}">
|
||||
<div class="fa {$ service.icon $}"> </div>
|
||||
{$ service.display $}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
# 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.test import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from monitoring.overview import constants
|
||||
from monitoring.overview import views
|
||||
from monitoring.test import helpers
|
||||
|
||||
|
||||
INDEX_URL = reverse(
|
||||
constants.URL_PREFIX + 'index')
|
||||
|
||||
|
||||
class OverviewTest(helpers.TestCase):
|
||||
|
||||
@override_settings(DASHBOARDS=[],
|
||||
KIBANA_POLICY_SCOPE='monitoring',
|
||||
KIBANA_POLICY_RULE='monitoring:kibana_access',
|
||||
ENABLE_LOG_MANAGEMENT_BUTTON=False,
|
||||
ENABLE_EVENT_MANAGEMENT_BUTTON=False,
|
||||
SHOW_GRAFANA_HOME=True)
|
||||
def test_index_get(self):
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(
|
||||
res, 'monitoring/overview/index.html')
|
||||
self.assertTemplateUsed(res, 'monitoring/overview/monitor.html')
|
||||
|
||||
|
||||
class KibanaProxyViewTest(helpers.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(KibanaProxyViewTest, self).setUp()
|
||||
self.view = views.KibanaProxyView()
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
def test_get_relative_url_with_unicode(self):
|
||||
"""Tests if it properly converts multibyte characters."""
|
||||
from urllib import parse as urlparse
|
||||
|
||||
self.view.request = self.request_factory.get(
|
||||
'/', data={'a': 1, 'b': 2}
|
||||
)
|
||||
expected_path = ('/elasticsearch/.kibana/search'
|
||||
'/New-Saved-Search%E3%81%82')
|
||||
expected_qs = {'a': ['1'], 'b': ['2']}
|
||||
|
||||
url = self.view.get_relative_url(
|
||||
u'/elasticsearch/.kibana/search/New-Saved-Searchあ'
|
||||
)
|
||||
# order of query params may change
|
||||
parsed_url = urlparse.urlparse(url)
|
||||
actual_path = parsed_url.path
|
||||
actual_qs = urlparse.parse_qs(parsed_url.query)
|
||||
|
||||
self.assertEqual(actual_path, expected_path)
|
||||
self.assertEqual(actual_qs, expected_qs)
|
||||
@@ -1,29 +0,0 @@
|
||||
# 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.urls import re_path
|
||||
|
||||
from monitoring.config import local_settings as settings
|
||||
from monitoring.overview import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', views.IndexView.as_view(), name='index'),
|
||||
re_path(r'^status', views.StatusView.as_view(), name='status'),
|
||||
re_path(r'^proxy\/(?P<restpath>.*)$', views.MonascaProxyView.as_view()),
|
||||
re_path(r'^proxy', views.MonascaProxyView.as_view(), name='proxy'),
|
||||
re_path(r'^logs_proxy(?P<url>.*)$',
|
||||
views.KibanaProxyView.as_view(base_url=settings.KIBANA_HOST),
|
||||
name='kibana_proxy'
|
||||
)
|
||||
]
|
||||
@@ -1,440 +0,0 @@
|
||||
# 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 base64
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django import http
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views import generic
|
||||
from django.views.generic import TemplateView
|
||||
from horizon import exceptions
|
||||
from openstack_auth import utils as auth_utils
|
||||
from openstack_dashboard import policy
|
||||
import urllib
|
||||
|
||||
from monitoring.alarms import tables as alarm_tables
|
||||
from monitoring import api
|
||||
from monitoring.overview import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
STATUS_FA_ICON_MAP = {'btn-success': "fa-check",
|
||||
'btn-danger': "fa-exclamation-triangle",
|
||||
'btn-warning': "fa-exclamation",
|
||||
'btn-default': "fa-question-circle"}
|
||||
|
||||
|
||||
def get_icon(status):
|
||||
return STATUS_FA_ICON_MAP.get(status, "fa-question-circle")
|
||||
|
||||
|
||||
priorities = [
|
||||
{'status': 'btn-success', 'severity': 'OK'},
|
||||
{'status': 'btn-default', 'severity': 'UNDETERMINED'},
|
||||
{'status': 'btn-warning', 'severity': 'LOW'},
|
||||
{'status': 'btn-warning', 'severity': 'MEDIUM'},
|
||||
{'status': 'btn-warning', 'severity': 'HIGH'},
|
||||
{'status': 'btn-danger', 'severity': 'CRITICAL'},
|
||||
]
|
||||
index_by_severity = {d['severity']: i for i, d in enumerate(priorities)}
|
||||
|
||||
|
||||
def get_dashboard_links(request):
|
||||
#
|
||||
# GRAFANA_LINKS is a list of dictionaries, but can either
|
||||
# be a nested list of dictionaries indexed by project name
|
||||
# (or '*'), or simply the list of links to display. This
|
||||
# code is a bit more complicated as a result but will allow
|
||||
# for backward compatibility and ensure existing installations
|
||||
# that don't take advantage of project specific dashboard
|
||||
# links are unaffected. The 'non_project_keys' are the
|
||||
# expected dictionary keys for the list of dashboard links,
|
||||
# so if we encounter one of those, we know we're supporting
|
||||
# legacy/non-project specific behavior.
|
||||
#
|
||||
# See examples of both in local_settings.py
|
||||
#
|
||||
non_project_keys = {'fileName', 'title'}
|
||||
try:
|
||||
for project_link in settings.DASHBOARDS:
|
||||
key = list(project_link)[0]
|
||||
value = list(project_link.values())[0]
|
||||
if key in non_project_keys:
|
||||
#
|
||||
# we're not indexed by project, just return
|
||||
# the whole list.
|
||||
#
|
||||
return settings.DASHBOARDS
|
||||
elif key == request.user.project_name:
|
||||
#
|
||||
# we match this project, return the project
|
||||
# specific links.
|
||||
#
|
||||
return value
|
||||
elif key == '*':
|
||||
#
|
||||
# this is a global setting, squirrel it away
|
||||
# in case we exhaust the list without a project
|
||||
# match
|
||||
#
|
||||
return value
|
||||
return settings.DEFAULT_LINKS
|
||||
except Exception:
|
||||
LOG.warning("Failed to parse dashboard links by project, returning defaults.")
|
||||
pass
|
||||
#
|
||||
# Extra safety here -- should have got a match somewhere above,
|
||||
# but fall back to defaults.
|
||||
#
|
||||
return settings.DASHBOARDS
|
||||
|
||||
|
||||
def get_monitoring_services(request):
|
||||
#
|
||||
# GRAFANA_LINKS is a list of dictionaries, but can either
|
||||
# be a nested list of dictionaries indexed by project name
|
||||
# (or '*'), or simply the list of links to display. This
|
||||
# code is a bit more complicated as a result but will allow
|
||||
# for backward compatibility and ensure existing installations
|
||||
# that don't take advantage of project specific dashboard
|
||||
# links are unaffected. The 'non_project_keys' are the
|
||||
# expected dictionary keys for the list of dashboard links,
|
||||
# so if we encounter one of those, we know we're supporting
|
||||
# legacy/non-project specific behavior.
|
||||
#
|
||||
# See examples of both in local_settings.py
|
||||
#
|
||||
non_project_keys = {'name', 'groupBy'}
|
||||
try:
|
||||
for group in settings.MONITORING_SERVICES:
|
||||
key = list(group.keys())[0]
|
||||
value = list(group.values())[0]
|
||||
if key in non_project_keys:
|
||||
#
|
||||
# we're not indexed by project, just return
|
||||
# the whole list.
|
||||
#
|
||||
return settings.MONITORING_SERVICES
|
||||
elif key == request.user.project_name:
|
||||
#
|
||||
# we match this project, return the project
|
||||
# specific links.
|
||||
#
|
||||
return value
|
||||
elif key == '*':
|
||||
#
|
||||
# this is a global setting, squirrel it away
|
||||
# in case we exhaust the list without a project
|
||||
# match
|
||||
#
|
||||
return value
|
||||
return settings.MONITORING_SERVICES
|
||||
except Exception:
|
||||
LOG.warning("Failed to parse monitoring services by project, returning defaults.")
|
||||
pass
|
||||
#
|
||||
# Extra safety here -- should have got a match somewhere above,
|
||||
# but fall back to defaults.
|
||||
#
|
||||
return settings.MONITORING_SERVICES
|
||||
|
||||
|
||||
def show_by_dimension(data, dim_name):
|
||||
if 'metrics' in data:
|
||||
dimensions = []
|
||||
for metric in data['metrics']:
|
||||
if 'dimensions' in metric:
|
||||
if dim_name in metric['dimensions']:
|
||||
dimension = metric['dimensions'][dim_name]
|
||||
dimensions.append(dimension)
|
||||
|
||||
return dimensions
|
||||
return []
|
||||
|
||||
|
||||
def get_status(alarms):
|
||||
if not alarms:
|
||||
return 'chicklet-notfound'
|
||||
status_index = 0
|
||||
for a in alarms:
|
||||
severity = alarm_tables.show_severity(a)
|
||||
severity_index = index_by_severity.get(severity, None)
|
||||
status_index = max(status_index, severity_index)
|
||||
return priorities[status_index]['status']
|
||||
|
||||
|
||||
def generate_status(request):
|
||||
try:
|
||||
alarms = api.monitor.alarm_list(request)
|
||||
except Exception as e:
|
||||
messages.error(request,
|
||||
_('Unable to list alarms: %s') % str(e))
|
||||
alarms = []
|
||||
alarms_by_service = {}
|
||||
for a in alarms:
|
||||
service = alarm_tables.get_service(a)
|
||||
service_alarms = alarms_by_service.setdefault(service, [])
|
||||
service_alarms.append(a)
|
||||
monitoring_services = copy.deepcopy(get_monitoring_services(request))
|
||||
for row in monitoring_services:
|
||||
row['name'] = str(row['name'])
|
||||
if 'groupBy' in row:
|
||||
alarms_by_group = {}
|
||||
for a in alarms:
|
||||
groups = show_by_dimension(a, row['groupBy'])
|
||||
if groups:
|
||||
for group in groups:
|
||||
group_alarms = alarms_by_group.setdefault(group, [])
|
||||
group_alarms.append(a)
|
||||
services = []
|
||||
for group, group_alarms in alarms_by_group.items():
|
||||
name = '%s=%s' % (row['groupBy'], group)
|
||||
# Encode as base64url to be able to include '/'
|
||||
# encoding and decoding is required because of python3 compatibility
|
||||
# urlsafe_b64encode requires byte-type text
|
||||
name = 'b64:' + base64.urlsafe_b64encode(name.encode('utf-8')).decode('utf-8')
|
||||
service = {
|
||||
'display': group,
|
||||
'name': name,
|
||||
'class': get_status(group_alarms)
|
||||
}
|
||||
service['icon'] = get_icon(service['class'])
|
||||
services.append(service)
|
||||
row['services'] = services
|
||||
else:
|
||||
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'])
|
||||
service['display'] = str(service['display'])
|
||||
return monitoring_services
|
||||
|
||||
|
||||
class IndexView(TemplateView):
|
||||
template_name = constants.TEMPLATE_PREFIX + 'index.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if not policy.check((('monitoring', 'monitoring:monitoring'), ), self.request):
|
||||
raise exceptions.NotAuthorized()
|
||||
context = super(IndexView, self).get_context_data(**kwargs)
|
||||
try:
|
||||
region = self.request.user.services_region
|
||||
context["grafana_url"] = getattr(settings, 'GRAFANA_URL').get(region, '')
|
||||
except AttributeError:
|
||||
# Catches case where Grafana 2 is not enabled.
|
||||
proxy_url_path = str(reverse_lazy(constants.URL_PREFIX + 'proxy'))
|
||||
api_root = self.request.build_absolute_uri(proxy_url_path)
|
||||
context["api"] = api_root
|
||||
context["dashboards"] = get_dashboard_links(self.request)
|
||||
# Ensure all links have a 'raw' attribute
|
||||
for link in context["dashboards"]:
|
||||
link['raw'] = link.get('raw', False)
|
||||
context['can_access_kibana'] = policy.check(
|
||||
((getattr(settings, 'KIBANA_POLICY_SCOPE'), getattr(settings, 'KIBANA_POLICY_RULE')), ),
|
||||
self.request
|
||||
)
|
||||
context['enable_log_management_button'] = settings.ENABLE_LOG_MANAGEMENT_BUTTON
|
||||
context['enable_event_management_button'] = settings.ENABLE_EVENT_MANAGEMENT_BUTTON
|
||||
context['show_grafana_home'] = settings.SHOW_GRAFANA_HOME
|
||||
return context
|
||||
|
||||
|
||||
class MonascaProxyView(TemplateView):
|
||||
template_name = ""
|
||||
|
||||
def _convert_dimensions(self, req_kwargs):
|
||||
"""Converts the dimension string service:monitoring into a dict
|
||||
|
||||
This method converts the dimension string
|
||||
service:monitoring (requested by a query string arg)
|
||||
into a python dict that looks like
|
||||
{"service": "monitoring"} (used by monasca api calls)
|
||||
"""
|
||||
dim_dict = {}
|
||||
if 'dimensions' in req_kwargs:
|
||||
dimensions_str = req_kwargs['dimensions'][0]
|
||||
dimensions_str_array = dimensions_str.split(',')
|
||||
for dimension in dimensions_str_array:
|
||||
# limit splitting since value may contain a ':' such as in
|
||||
# the `url` dimension of the service_status check.
|
||||
dimension_name_value = dimension.split(':', 1)
|
||||
if len(dimension_name_value) == 2:
|
||||
name = dimension_name_value[0]
|
||||
value = dimension_name_value[1]
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode("utf-8")
|
||||
dim_dict[name] = urllib.parse.unquote(value)
|
||||
else:
|
||||
raise Exception('Dimensions are malformed')
|
||||
|
||||
#
|
||||
# If the request specifies 'INJECT_REGION' as the region, we'll
|
||||
# replace with the horizon scoped region. We can't do this by
|
||||
# default, since some implementations don't publish region as a
|
||||
# dimension for all metrics (mini-mon for one).
|
||||
#
|
||||
if 'region' in dim_dict and dim_dict['region'] == 'INJECT_REGION':
|
||||
dim_dict['region'] = self.request.user.services_region
|
||||
req_kwargs['dimensions'] = dim_dict
|
||||
|
||||
return req_kwargs
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
# monasca_endpoint = api.monitor.monasca_endpoint(self.request)
|
||||
restpath = self.kwargs['restpath']
|
||||
|
||||
results = None
|
||||
parts = restpath.split('/')
|
||||
if "metrics" == parts[0]:
|
||||
req_kwargs = dict(self.request.GET)
|
||||
self._convert_dimensions(req_kwargs)
|
||||
if len(parts) == 1:
|
||||
results = {'elements': api.monitor.
|
||||
metrics_list(request,
|
||||
**req_kwargs)}
|
||||
elif "statistics" == parts[1]:
|
||||
results = {'elements': api.monitor.
|
||||
metrics_stat_list(request,
|
||||
**req_kwargs)}
|
||||
elif "measurements" == parts[1]:
|
||||
results = {'elements': api.monitor.
|
||||
metrics_measurement_list(request,
|
||||
**req_kwargs)}
|
||||
elif "dimensions" == parts[1]:
|
||||
results = {'elements': api.monitor.
|
||||
metrics_dimension_value_list(request,
|
||||
**req_kwargs)}
|
||||
if not results:
|
||||
LOG.warning("There was a request made for the path %s that"
|
||||
" is not supported." % restpath)
|
||||
results = {}
|
||||
return HttpResponse(json.dumps(results),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
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 _HttpMethodRequest(urllib.request.Request):
|
||||
|
||||
def __init__(self, method, url, **kwargs):
|
||||
urllib.request.Request.__init__(self, url, **kwargs)
|
||||
self.method = method
|
||||
|
||||
def get_method(self):
|
||||
return self.method
|
||||
|
||||
|
||||
def proxy_stream_generator(response):
|
||||
while True:
|
||||
chunk = response.read(1000 * 1024)
|
||||
if not chunk:
|
||||
break
|
||||
yield chunk
|
||||
|
||||
|
||||
class KibanaProxyView(generic.View):
|
||||
|
||||
base_url = None
|
||||
http_method_names = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']
|
||||
|
||||
def read(self, method, url, data, headers):
|
||||
|
||||
proxy_request_url = self.get_absolute_url(url)
|
||||
proxy_request = _HttpMethodRequest(
|
||||
method, proxy_request_url, data=data, headers=headers
|
||||
)
|
||||
try:
|
||||
response = urllib.request.urlopen(proxy_request)
|
||||
|
||||
except urllib.error.HTTPError as e:
|
||||
return http.HttpResponse(
|
||||
e.read(),
|
||||
status=e.code,
|
||||
content_type=e.hdrs['content-type']
|
||||
)
|
||||
except urllib.error.URLError as e:
|
||||
return http.HttpResponse(e.reason, 404)
|
||||
|
||||
else:
|
||||
status = response.getcode()
|
||||
proxy_response = http.StreamingHttpResponse(
|
||||
proxy_stream_generator(response),
|
||||
status=status,
|
||||
content_type=response.headers['content-type']
|
||||
)
|
||||
if 'set-cookie' in response.headers:
|
||||
proxy_response['set-cookie'] = response.headers['set-cookie']
|
||||
return proxy_response
|
||||
|
||||
@csrf_exempt
|
||||
def dispatch(self, request, url):
|
||||
if not url:
|
||||
url = '/'
|
||||
|
||||
if request.method not in self.http_method_names:
|
||||
return http.HttpResponseNotAllowed(request.method)
|
||||
|
||||
if not self._can_access_kibana():
|
||||
error_msg = (_('User %s does not have sufficient '
|
||||
'privileges to access Kibana')
|
||||
% auth_utils.get_user(request))
|
||||
LOG.error(error_msg)
|
||||
return http.HttpResponseForbidden(content=error_msg)
|
||||
|
||||
# passing kbn version explicitly for kibana >= 4.3.x
|
||||
headers = {
|
||||
"X-Auth-Token": request.user.token.id,
|
||||
"kbn-version": request.META.get("HTTP_KBN_VERSION", ""),
|
||||
"Cookie": request.META.get("HTTP_COOKIE", ""),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
return self.read(request.method, url, request.body, headers)
|
||||
|
||||
def get_relative_url(self, url):
|
||||
url = urllib.parse.quote(url.encode('utf-8'))
|
||||
params_str = self.request.GET.urlencode()
|
||||
|
||||
if params_str:
|
||||
return '{0}?{1}'.format(url, params_str)
|
||||
return url
|
||||
|
||||
def get_absolute_url(self, url):
|
||||
return self.base_url + self.get_relative_url(url).lstrip('/')
|
||||
|
||||
def _can_access_kibana(self):
|
||||
return policy.check(
|
||||
((getattr(settings, 'KIBANA_POLICY_SCOPE'), getattr(settings, 'KIBANA_POLICY_RULE')), ),
|
||||
self.request
|
||||
)
|
||||
@@ -1,60 +0,0 @@
|
||||
.alarm-expression {
|
||||
|
||||
.row {
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
|
||||
pre.expression-preview {
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
}
|
||||
|
||||
pre.expression-valid {
|
||||
border-color: #00B700;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
pre.expression-invalid {
|
||||
border-color: #A94442;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.alarm-sub-expression {
|
||||
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
padding: 10px;
|
||||
|
||||
.sub-expression-preview {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.expression-details {
|
||||
margin-bottom: 5px;
|
||||
|
||||
div {
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
|
||||
.has-error {
|
||||
border-color: #a94442;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
tags-input *, *:before, *:after {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
tags-input .host {
|
||||
position: relative;
|
||||
/*margin-top: 5px;*/
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
tags-input .host:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
tags-input .tags {
|
||||
-moz-appearance: textfield;
|
||||
-webkit-appearance: textfield;
|
||||
padding: 1px;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
cursor: text;
|
||||
background-color: white;
|
||||
border: 1px solid darkgray;
|
||||
box-shadow: 1px 1px 1px 0 lightgray inset;
|
||||
}
|
||||
tags-input .tags.focused {
|
||||
outline: none;
|
||||
-webkit-box-shadow: 0 0 3px 1px rgba(5, 139, 242, 0.6);
|
||||
-moz-box-shadow: 0 0 3px 1px rgba(5, 139, 242, 0.6);
|
||||
box-shadow: 0 0 3px 1px rgba(5, 139, 242, 0.6);
|
||||
}
|
||||
tags-input .tags .tag-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
tags-input .tags .tag-item {
|
||||
margin: 2px;
|
||||
padding: 0 5px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
/*font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;*/
|
||||
height: 26px;
|
||||
line-height: 25px;
|
||||
border: 1px solid #acacac;
|
||||
border-radius: 3px;
|
||||
background: -webkit-linear-gradient(top, #f0f9ff 0%, #cbebff 47%, #a1dbff 100%);
|
||||
background: linear-gradient(to bottom, #f0f9ff 0%, #cbebff 47%, #a1dbff 100%);
|
||||
}
|
||||
tags-input .tags .tag-item.selected {
|
||||
background: -webkit-linear-gradient(top, #febbbb 0%, #fe9090 45%, #ff5c5c 100%);
|
||||
background: linear-gradient(to bottom, #febbbb 0%, #fe9090 45%, #ff5c5c 100%);
|
||||
}
|
||||
tags-input .tags .tag-item .remove-button {
|
||||
margin: 0 0 0 5px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
font: bold 16px Arial, sans-serif;
|
||||
color: #585858;
|
||||
}
|
||||
tags-input .tags .tag-item .remove-button:active {
|
||||
color: red;
|
||||
}
|
||||
tags-input .tags .input {
|
||||
border: 0;
|
||||
outline: none;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
padding-left: 5px;
|
||||
float: left;
|
||||
height: 26px;
|
||||
/*font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;*/
|
||||
}
|
||||
tags-input .tags .input.invalid-tag {
|
||||
color: red;
|
||||
}
|
||||
tags-input .tags .input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
tags-input.ng-invalid .tags {
|
||||
-webkit-box-shadow: 0 0 3px 1px rgba(255, 0, 0, 0.6);
|
||||
-moz-box-shadow: 0 0 3px 1px rgba(255, 0, 0, 0.6);
|
||||
box-shadow: 0 0 3px 1px rgba(255, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
tags-input .autocomplete {
|
||||
margin-top: 5px;
|
||||
position: absolute;
|
||||
padding: 5px 0;
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
tags-input .autocomplete .suggestion-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item {
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item.selected {
|
||||
color: white;
|
||||
background-color: #0097cf;
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item.selected em {
|
||||
color: white;
|
||||
background-color: #0097cf;
|
||||
}
|
||||
tags-input .autocomplete .suggestion-item em {
|
||||
font: normal bold 16px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,19 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Declare app level module which depends on filters, and services
|
||||
angular
|
||||
.module('monitoringApp', [
|
||||
'monitoring.controllers',
|
||||
'monitoring.directives',
|
||||
'monitoring.filters',
|
||||
'monitoring.services',
|
||||
'ngTagsInput'
|
||||
])
|
||||
.config(config);
|
||||
|
||||
config.$inject = ['$provide', '$windowProvider'];
|
||||
|
||||
function config($provide, $windowProvider) {
|
||||
var path = $windowProvider.$get().STATIC_URL + 'monitoring/widgets/';
|
||||
$provide.constant('monitoringApp.staticPath', path);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user