Refactor the FM panel to leverage AngularJS

The FM panel which contains 3 tabs is now replaced with a new panel
group called “Fault Management” under Admin which contains an
“Active Alarms”, “Events” and “Events Suppression” panel.

The suppression buttons are gone, filters replace the need for the
suppression buttons. Paging is done automatically in AngularJS the and
‘Default Limit' button is also gone.

The Alarm Summary Banner was removed from the FM panels as it is
already displayed in the header banner.

The “Related Alarms” tab under the “Provider Network Topology“ panel was
refactored to still uses Django’s alarms table.

Other related FM Django code that is no longer used has been removed.

Refactored some DC Cloud panels as well.

Story: 2004818
Task: 28984

Change-Id: I7fdece26118dc066cf93001189767f78bfe2caf7
Signed-off-by: Kristine Bujold <kristine.bujold@windriver.com>
This commit is contained in:
Kristine Bujold 2019-01-22 10:55:04 -05:00
parent f7865d2e71
commit 0f57d0e1dc
64 changed files with 2348 additions and 1099 deletions

View File

@ -1,2 +1,2 @@
SRC_DIR="starlingx-dashboard"
TIS_PATCH_VER=22
TIS_PATCH_VER=23

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
# Copyright (c) 2018-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -248,9 +248,14 @@ class EventSuppression(base.APIResourceWrapper):
super(EventSuppression, self).__init__(apiresource)
def event_suppression_list(request):
def event_suppression_list(request, include_unsuppressed=False):
q = []
if not include_unsuppressed:
q.append(
dict(field='suppression_status', value='suppressed', op='eq',
type='string'))
suppression_list = fmclient(request).event_suppression.list()
suppression_list = fmclient(request).event_suppression.list(q)
return [EventSuppression(n) for n in suppression_list]

View File

@ -1,8 +1,9 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
# Copyright (c) 2018-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import logging
from django.views import generic
@ -11,6 +12,9 @@ from openstack_dashboard.api.rest import utils as rest_utils
from starlingx_dashboard.api import fm
LOG = logging.getLogger(__name__)
@urls.register
class AlarmSummary(generic.View):
"""API for retrieving alarm summaries."""
@ -22,3 +26,81 @@ class AlarmSummary(generic.View):
include_suppress = request.GET.get('include_suppress', False)
result = fm.alarm_summary_get(request, include_suppress)
return result.to_dict()
@urls.register
class Alarms(generic.View):
"""API for retrieving alarms."""
url_regex = r'fm/alarm_list/$'
@rest_utils.ajax()
def get(self, request):
search_opts = {'suppression': 'SUPPRESS_SHOW'}
result = fm.alarm_list(request, search_opts=search_opts)
return {'items': [sc.to_dict() for sc in result]}
@urls.register
class Alarm(generic.View):
"""API for retrieving one alarm."""
url_regex = r'fm/alarm_get/(?P<uuid>[^/]+|default)/$'
@rest_utils.ajax()
def get(self, request, uuid):
result = fm.alarm_get(request, uuid)
return result.to_dict()
@urls.register
class Events(generic.View):
"""API for retrieving events."""
url_regex = r'fm/event_log_list/$'
@rest_utils.ajax()
def get(self, request):
search_opts = {'suppression': 'SUPPRESS_SHOW'}
result, _more = fm.event_log_list(request, search_opts=search_opts)
return {'items': [sc.to_dict() for sc in result]}
@urls.register
class Event(generic.View):
"""API for retrieving one event."""
url_regex = r'fm/event_log_get/(?P<uuid>[^/]+|default)/$'
@rest_utils.ajax()
def get(self, request, uuid):
result = fm.event_log_get(request, uuid)
return result.to_dict()
@urls.register
class EventsSuppression(generic.View):
"""API for retrieving events suppression."""
url_regex = r'fm/events_suppression_list/$'
@rest_utils.ajax()
def get(self, request):
if 'include_unsuppressed' in request.GET:
include_unsuppressed = True
else:
include_unsuppressed = False
result = fm.event_suppression_list(
request, include_unsuppressed=include_unsuppressed)
return {'items': [sc.to_dict() for sc in result]}
@urls.register
class EventSuppression(generic.View):
"""API for updating the event suppression."""
url_regex = r'fm/event_suppression/(?P<uuid>[^/]+)$'
@rest_utils.ajax(data_required=True)
def patch(self, request, uuid):
result = fm.event_suppression_update(request, uuid, **(request.DATA))
return result.to_dict()

View File

@ -1,9 +1,3 @@
# 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
@ -16,7 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from django.utils.translation import ugettext_lazy as _ # noqa
@ -26,10 +22,9 @@ from openstack_dashboard.api import base
from openstack_dashboard.dashboards.admin import dashboard
class FaultManagement(horizon.Panel):
name = _("Fault Management")
slug = 'fault_management'
permissions = ('openstack.services.platform',)
class ActiveAlarms(horizon.Panel):
name = _("Active Alarms")
slug = "active_alarms"
def allowed(self, context):
if context['request'].user.services_region == 'SystemController':
@ -37,7 +32,7 @@ class FaultManagement(horizon.Panel):
if not base.is_service_enabled(context['request'], 'platform'):
return False
else:
return super(FaultManagement, self).allowed(context)
return super(ActiveAlarms, self).allowed(context)
def nav(self, context):
if context['request'].user.services_region == 'SystemController':
@ -48,4 +43,4 @@ class FaultManagement(horizon.Panel):
return True
dashboard.Admin.register(FaultManagement)
dashboard.Admin.register(ActiveAlarms)

View File

@ -0,0 +1,29 @@
# 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.
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from django.conf.urls import url
from django.utils.translation import ugettext_lazy as _
from horizon.browsers import views
from starlingx_dashboard.dashboards.admin.active_alarms import \
views as viewsDjango
title = _("Active Alarms")
urlpatterns = [
url(r'^$', views.AngularIndexView.as_view(title=title), name='index'),
url(r'^banner/$', viewsDjango.BannerView.as_view(), name='banner')
]

View File

@ -0,0 +1,76 @@
# 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.
#
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import logging
from django.utils.translation import ugettext_lazy as _ # noqa
from django.views.generic import TemplateView
from horizon import exceptions
from openstack_dashboard.api.base import is_service_enabled
from starlingx_dashboard.api import dc_manager
from starlingx_dashboard.api import fm
LOG = logging.getLogger(__name__)
class BannerView(TemplateView):
template_name = 'header/_alarm_banner.html'
def get_context_data(self, **kwargs):
context = super(BannerView, self).get_context_data(**kwargs)
if not self.request.is_ajax():
raise exceptions.NotFound()
if (not self.request.user.is_authenticated() or
not self.request.user.is_superuser):
context["alarmbanner"] = False
elif 'dc_admin' in self.request.META.get('HTTP_REFERER'):
summaries = self.get_subcloud_data()
central_summary = self.get_data()
summaries.append(central_summary)
context["dc_admin"] = True
context["alarmbanner"] = True
context["OK"] = len(
[s for s in summaries if s.status == 'OK'])
context["degraded"] = len(
[s for s in summaries if s.status == 'degraded'])
context["critical"] = len(
[s for s in summaries if s.status == 'critical'])
context["disabled"] = len(
[s for s in summaries if s.status == 'disabled'])
elif is_service_enabled(self.request, 'platform'):
context["summary"] = self.get_data()
context["alarmbanner"] = True
return context
def get_data(self):
summary = None
try:
summary = fm.alarm_summary_get(self.request)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve alarm summary.'))
return summary
def get_subcloud_data(self):
return dc_manager.alarm_summary_list(self.request)

View File

@ -0,0 +1,41 @@
# 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.
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.api import base
class Events(horizon.Panel):
name = _("Events")
slug = "events"
def allowed(self, context):
if context['request'].user.services_region == 'SystemController':
return False
if not base.is_service_enabled(context['request'], 'platform'):
return False
else:
return super(Events, self).allowed(context)
def nav(self, context):
if context['request'].user.services_region == 'SystemController':
return False
if not base.is_service_enabled(context['request'], 'platform'):
return False
else:
return True

View File

@ -0,0 +1,25 @@
# 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.
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from django.conf.urls import url
from django.utils.translation import ugettext_lazy as _
from horizon.browsers import views
title = _("Events")
urlpatterns = [
url('', views.AngularIndexView.as_view(title=title), name='index'),
]

View File

@ -0,0 +1,41 @@
# 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.
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.api import base
class EventsSuppression(horizon.Panel):
name = _("Events Suppression")
slug = "events_suppression"
def allowed(self, context):
if context['request'].user.services_region == 'SystemController':
return False
if not base.is_service_enabled(context['request'], 'platform'):
return False
else:
return super(EventsSuppression, self).allowed(context)
def nav(self, context):
if context['request'].user.services_region == 'SystemController':
return False
if not base.is_service_enabled(context['request'], 'platform'):
return False
else:
return True

View File

@ -0,0 +1,25 @@
# 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.
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from django.conf.urls import url
from django.utils.translation import ugettext_lazy as _
from horizon.browsers import views
title = _("Events Suppression")
urlpatterns = [
url('', views.AngularIndexView.as_view(title=title), name='index'),
]

View File

@ -1,280 +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.
#
# Copyright (c) 2013-2018 Wind River Systems, Inc.
#
from django.utils.html import escape as escape_html
from django.utils.safestring import mark_safe
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _ # noqa
from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import tables
from horizon.utils import filters as utils_filters
from starlingx_dashboard import api as stx_api
SUPPRESSION_STATUS_CHOICES = (
("suppressed", False),
("unsuppressed", True),
("None", True),
)
SUPPRESSION_STATUS_DISPLAY_CHOICES = (
("suppressed", pgettext_lazy("Indicates this type of alarm \
is suppressed", "suppressed")),
("unsuppressed", pgettext_lazy("Indicates this type of alarm \
is unsuppressed", "unsuppressed")),
("None", pgettext_lazy("Indicates an event type", "None")),
)
class AlarmsLimitAction(tables.LimitAction):
verbose_name = _("Alarms")
class AlarmFilterAction(tables.FixedWithQueryFilter):
def __init__(self, **kwargs):
super(AlarmFilterAction, self).__init__(**kwargs)
self.filter_choices = [
(
(stx_api.fm.FM_SUPPRESS_SHOW, _("Show Suppressed"), True),
(stx_api.fm.FM_SUPPRESS_HIDE, _('Hide Suppressed'), True)
)
]
self.default_value = stx_api.fm.FM_SUPPRESS_HIDE
self.disabled_choices = ['enabled']
class AlarmsTable(tables.DataTable):
alarm_id = tables.Column('alarm_id',
link="horizon:admin:fault_management:detail",
verbose_name=_('Alarm ID'))
reason_text = tables.Column('reason_text',
verbose_name=_('Reason Text'))
entity_instance_id = tables.Column('entity_instance_id',
verbose_name=_('Entity Instance ID'))
suppression_status = \
tables.Column('suppression_status',
verbose_name=_('Suppression Status'),
status=True,
status_choices=SUPPRESSION_STATUS_CHOICES,
display_choices=SUPPRESSION_STATUS_DISPLAY_CHOICES)
severity = tables.Column('severity',
verbose_name=_('Severity'))
timestamp = tables.Column('timestamp',
attrs={'data-type': 'timestamp'},
filters=(utils_filters.parse_isotime,),
verbose_name=_('Timestamp'))
def get_object_id(self, obj):
return obj.uuid
class Meta(object):
name = "alarms"
verbose_name = _("Active Alarms")
status_columns = ["suppression_status"]
limit_param = "alarm_limit"
pagination_param = "alarm_marker"
prev_pagination_param = 'prev_alarm_marker'
table_actions = (AlarmFilterAction, AlarmsLimitAction)
multi_select = False
hidden_title = False
class EventLogsLimitAction(tables.LimitAction):
verbose_name = _("Events")
class EventLogsFilterAction(tables.FixedWithQueryFilter):
def __init__(self, **kwargs):
super(EventLogsFilterAction, self).__init__(**kwargs)
self.filter_choices = [
(
(stx_api.fm.FM_ALL, _("All Events"), True),
(stx_api.fm.FM_ALARM, _('Alarm Events'), True),
(stx_api.fm.FM_LOG, _('Log Events'), True),
),
(
(stx_api.fm.FM_SUPPRESS_SHOW, _("Show Suppressed"), True),
(stx_api.fm.FM_SUPPRESS_HIDE, _('Hide Suppressed'), True)
)
]
self.default_value = stx_api.fm.FM_ALL_SUPPRESS_HIDE
self.disabled_choices = ['enabled', 'enabled']
class EventLogsTable(tables.DataTable):
timestamp = tables.Column('timestamp',
attrs={'data-type': 'timestamp'},
filters=(utils_filters.parse_isotime,),
verbose_name=_('Timestamp'))
state = tables.Column('state', verbose_name=_('State'))
event_log_id = tables.Column('event_log_id',
link="horizon:admin:fault_management:"
"eventlogdetail",
verbose_name=_('ID'))
reason_text = tables.Column('reason_text', verbose_name=_('Reason Text'))
entity_instance_id = tables.Column('entity_instance_id',
verbose_name=_('Entity Instance ID'))
suppression_status = \
tables.Column('suppression_status',
verbose_name=_('Suppression Status'),
status=True,
status_choices=SUPPRESSION_STATUS_CHOICES,
display_choices=SUPPRESSION_STATUS_DISPLAY_CHOICES)
severity = tables.Column('severity', verbose_name=_('Severity'))
def get_object_id(self, obj):
return obj.uuid
class Meta(object):
name = "eventLogs"
verbose_name = _("Events")
status_columns = ["suppression_status"]
table_actions = (EventLogsFilterAction,
EventLogsLimitAction,)
limit_param = "event_limit"
pagination_param = "event_marker"
prev_pagination_param = 'prev_event_marker'
multi_select = False
class SuppressEvent(tables.BatchAction):
name = "suppress"
action_type = 'danger'
icon = "remove"
help_text = _("Events with selected Alarm ID will be suppressed.")
@staticmethod
def action_present(count):
return ungettext_lazy(
"Suppress Event",
"Suppress Event",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
"Events suppressed for Alarm ID",
"Events suppressed for Alarm ID",
count
)
def allowed(self, request, datum):
"""Allow suppress action if Alarm ID is unsuppressed."""
if datum.suppression_status == stx_api.fm.FM_SUPPRESSED:
return False
return True
def action(self, request, obj_id):
kwargs = {"suppression_status": stx_api.fm.FM_SUPPRESSED}
try:
stx_api.fm.event_suppression_update(request, obj_id, **kwargs)
except Exception:
exceptions.handle(request,
_('Unable to set specified alarm type to \
suppressed\'s.'))
class UnsuppressEvent(tables.BatchAction):
name = "unsuppress"
action_type = 'danger'
icon = "remove"
help_text = _("Events with selected Alarm ID will be unsuppressed.")
@staticmethod
def action_present(count):
return ungettext_lazy(
"Unsuppress Event",
"Unsuppress Event",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
"Events unsuppressed for Alarm ID",
"Events unsuppressed for Alarm ID",
count
)
def allowed(self, request, datum):
"""Allow unsuppress action if Alarm ID is suppressed."""
if datum.suppression_status == stx_api.fm.FM_UNSUPPRESSED:
return False
return True
def action(self, request, obj_id):
kwargs = {"suppression_status": stx_api.fm.FM_UNSUPPRESSED}
try:
stx_api.fm.event_suppression_update(request, obj_id, **kwargs)
except Exception:
exceptions.handle(request,
_('Unable to set specified alarm type to \
unsuppressed\'s.'))
class EventsSuppressionTable(tables.DataTable):
# noinspection PyMethodParameters
def description_inject(row_data): # pylint: disable=no-self-argument
description = \
escape_html(str(row_data.description)).replace("\n", "<br/>")
description = description.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
description = description.replace(" " * 4, "&nbsp;" * 4)
description = description.replace(" " * 3, "&nbsp;" * 3)
description = description.replace(" " * 2, "&nbsp;" * 2)
return mark_safe(description)
alarm_id = tables.Column('alarm_id',
verbose_name=_('Event ID'))
description = tables.Column(description_inject,
verbose_name=_('Description'))
status = tables.Column('suppression_status',
verbose_name=_('Status'))
def get_object_id(self, obj):
# return obj.alarm_id
return obj.uuid
def get_object_display(self, datum):
"""Returns a display name that identifies this object."""
if hasattr(datum, 'alarm_id'):
return datum.alarm_id
return None
class Meta(object):
name = "eventsSuppression"
limit_param = "events_suppression_limit"
pagination_param = "events_suppression_marker"
prev_pagination_param = 'prev_event_ids_marker'
verbose_name = _("Events Suppression")
row_actions = (SuppressEvent, UnsuppressEvent,)
multi_select = False

View File

@ -1,309 +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.
#
# Copyright (c) 2013-2018 Wind River Systems, Inc.
#
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import tabs
from starlingx_dashboard import api as stx_api
from starlingx_dashboard.dashboards.admin.fault_management import tables
ALARMS_SUPPRESSION_FILTER_GROUP = 0
EVENT_SUPPRESSION_FILTER_GROUP = 1
class ActiveAlarmsTab(tabs.TableTab):
table_classes = (tables.AlarmsTable,)
name = _("Active Alarms")
slug = "alarms"
template_name = 'admin/fault_management/_active_alarms.html'
def has_more_data(self, table):
return self._more
def get_limit_count(self, table):
return self._limit
def getTableFromName(self, tableName):
table = self._tables[tableName]
return table
def set_suppression_filter(self, disabled_status):
alarmsTable = self.getTableFromName('alarms')
filter_action = alarmsTable._meta._filter_action
filter_action.set_disabled_filter_field_for_group(
ALARMS_SUPPRESSION_FILTER_GROUP, disabled_status)
filter_action.updateFromRequestDataToSession(self.request)
def get_context_data(self, request):
context = super(ActiveAlarmsTab, self).get_context_data(request)
summary = stx_api.fm.alarm_summary_get(
self.request, include_suppress=False)
context["total"] = summary.critical + summary.major + summary.minor \
+ summary.warnings
context["summary"] = summary
events_types = self.get_event_suppression_data()
suppressed_events_types = len([etype for etype
in events_types
if etype.suppression_status ==
'suppressed'])
alarms_table = self.getTableFromName('alarms')
suppress_filter = self.get_filters()
suppress_filter_state = suppress_filter.get('suppression')
hidden_found = 'hidden' in alarms_table.columns["suppression_status"].\
classes
if not hidden_found:
if suppressed_events_types == 0:
self.set_suppression_filter('disabled')
alarms_table.columns["suppression_status"]\
.classes.append('hidden')
elif suppress_filter_state == stx_api.fm.FM_SUPPRESS_HIDE:
self.set_suppression_filter('enabled')
alarms_table.columns["suppression_status"].classes\
.append('hidden')
else:
if suppressed_events_types == 0:
self.set_suppression_filter('disabled')
else:
self.set_suppression_filter('enabled')
if suppress_filter_state == stx_api.fm.FM_SUPPRESS_SHOW:
alarms_table.columns["suppression_status"].classes\
.remove('hidden')
return context
def get_filters(self, filters=None):
filters = filters or {}
alarmsTable = self.getTableFromName('alarms')
filter_action = alarmsTable._meta._filter_action
filter_action.updateFromRequestDataToSession(self.request)
filter_field = filter_action.get_filter_field(self.request)
if filter_field:
suppression = filter_action.get_filter_field_for_group(0)
filters["suppression"] = suppression
return filters
def get_alarms_data(self):
search_opts = {}
# get retrieve parameters from request/session env
limit = \
self.request.GET.get(tables.AlarmsTable.Meta.limit_param,
None)
search_opts = self.get_filters()
search_opts.update({
'paginate': True,
'sort_key': 'severity,entity_instance_id',
'sort_dir': 'asc'})
alarms = []
try:
if 'paginate' in search_opts:
alarms, self._more = stx_api.fm.alarm_list(
self.request, search_opts=search_opts)
else:
alarms = stx_api.fm.alarm_list(
self.request, search_opts=search_opts)
self._limit = limit
except Exception:
self._more = False
self._limit = None
exceptions.handle(self.request,
_('Unable to retrieve alarms list.'))
return alarms
def get_event_suppression_data(self):
event_types = []
try:
if 'suppression_list' not in self.tab_group.kwargs:
self.tab_group.kwargs['suppression_list'] = \
stx_api.fm.event_suppression_list(self.request)
event_types = self.tab_group.kwargs['suppression_list']
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve event suppression table'
' list.'))
return event_types
class EventLogTab(tabs.TableTab):
table_classes = (tables.EventLogsTable,)
name = _("Events")
slug = "eventLogs"
template_name = 'admin/fault_management/_summary.html'
preload = False
def has_more_data(self, table):
return self._more
def get_limit_count(self, table):
return self._limit
def getTableFromName(self, tableName):
table = self._tables[tableName]
return table
def set_suppression_filter(self, disabled_status):
alarmsTable = self.getTableFromName('eventLogs')
filter_action = alarmsTable._meta._filter_action
filter_action.set_disabled_filter_field_for_group(
EVENT_SUPPRESSION_FILTER_GROUP, disabled_status)
filter_action.updateFromRequestDataToSession(self.request)
def get_context_data(self, request):
context = super(EventLogTab, self).get_context_data(request)
events_types = self.get_event_suppression_data()
suppressed_events_types = len([etype for etype in events_types
if etype.suppression_status ==
'suppressed'])
event_log_table = self.getTableFromName('eventLogs')
filters = self.get_filters({'marker': None,
'limit': None,
'paginate': True})
suppress_filter_state = filters.get('suppression')
hidden_found = 'hidden' in event_log_table\
.columns["suppression_status"].classes
if not hidden_found:
if suppressed_events_types == 0:
self.set_suppression_filter('disabled')
event_log_table.columns["suppression_status"]\
.classes.append('hidden')
elif suppress_filter_state == stx_api.fm.FM_SUPPRESS_HIDE:
self.set_suppression_filter('enabled')
event_log_table.columns["suppression_status"].\
classes.append('hidden')
else:
if suppressed_events_types == 0:
self.set_suppression_filter('disabled')
else:
self.set_suppression_filter('enabled')
if suppress_filter_state == stx_api.fm.FM_SUPPRESS_SHOW:
event_log_table.columns["suppression_status"]\
.classes.remove('hidden')
return context
def get_filters(self, filters):
eventLogsTable = self.getTableFromName('eventLogs')
filter_action = eventLogsTable._meta._filter_action
filter_action.updateFromRequestDataToSession(self.request)
filter_field = filter_action.get_filter_field(self.request)
if filter_field:
filters["evtType"] = filter_action.get_filter_field_for_group(0)
filters["suppression"] = filter_action\
.get_filter_field_for_group(1)
return filters
def get_eventLogs_data(self):
# get retrieve parameters from request/session env
marker = \
self.request.GET.get(tables.EventLogsTable.Meta.pagination_param,
None)
limit = \
self.request.GET.get(tables.EventLogsTable.Meta.limit_param,
None)
search_opts = self.get_filters({'marker': marker,
'limit': limit,
'paginate': True})
events = []
try:
# now retrieve data from rest API
events, self._more = \
stx_api.fm.event_log_list(self.request,
search_opts=search_opts)
self._limit = limit
return events
except Exception:
events = []
self._more = False
self._limit = None
exceptions.handle(self.request,
_('Unable to retrieve Event Log list.'))
return events
def get_event_suppression_data(self):
event_types = []
try:
if 'suppression_list' not in self.tab_group.kwargs:
self.tab_group.kwargs['suppression_list'] = \
stx_api.fm.event_suppression_list(self.request)
event_types = self.tab_group.kwargs['suppression_list']
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve event suppression \
table list.'))
return event_types
class EventsSuppressionTab(tabs.TableTab):
table_classes = (tables.EventsSuppressionTable,)
name = _("Events Suppression")
slug = "eventsSuppression"
template_name = 'admin/fault_management/_summary.html'
preload = False
def get_eventsSuppression_data(self):
event_suppression_list = []
try:
if 'suppression_list' not in self.tab_group.kwargs:
self.tab_group.kwargs['suppression_list'] = \
stx_api.fm.event_suppression_list(self.request)
event_suppression_list = self.tab_group.kwargs['suppression_list']
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve event suppression \
list\'s.'))
event_suppression_list.sort(key=lambda a: (a.alarm_id))
return event_suppression_list
class AlarmsTabs(tabs.TabGroup):
slug = "alarms_tabs"
tabs = (ActiveAlarmsTab, EventLogTab, EventsSuppressionTab)
sticky = True

View File

@ -1,26 +0,0 @@
{% load i18n %}
<div id="active-alarm-stats" class="info details">
<span>
<strong>{% trans "Active Alarms" %}:</strong>
<span class="badge {% if total != 0 %} badge-success{% endif %}">{{ total }}</span>
</span>
<span>
<strong>{% trans "Critical" %}:</strong>
<span class="badge{% if summary.critical != 0 %} badge-danger{% endif %}">{{ summary.critical }}</span>
</span>
<span>
<strong>{% trans "Major" %}:</strong>
<span class="badge{% if summary.major != 0 %} badge-danger{% endif %}">{{ summary.major }}</span>
</span>
<span>
<strong>{% trans "Minor" %}:</strong>
<span class="badge{% if summary.minor != 0 %} badge-warning{% endif %}">{{ summary.minor }}</span>
</span>
<span>
<strong>{% trans "Warning" %}:</strong>
<span class="badge{% if summary.warnings != 0 %} badge-success{% endif %}">{{ summary.warnings }}</span>
</span>
</div>
{{ table.render }}

View File

@ -1,58 +0,0 @@
{% extends 'base.html' %}
{% load i18n breadcrumb_nav %}
{% block title %}{% trans "Historical Alarm Details" %}{% endblock %}
{% block main %}
{% if history.event_log_id == '' or history.event_log_id == ' ' %}
<h3> {{history.reason_text }} </h3>
{% else %}
<h3> {{history.state }} - {{history.event_log_id }} - {{history.reason_text }} </h3>
{% endif %}
<div class="row">
<div class="col-sm-12">
<div class="info row-fluid detail">
<h4>{% trans "Info" %}</h4>
<hr class="header_rule">
<dl>
<dt>{% trans "Alarm UUID" %}</dt>
<dd>{{ history.uuid }}</dd>
{% if history.event_log_id != '' and history.event_log_id != ' ' %}
<dt>{% trans "Alarm ID" %}</dt>
<dd>{{ history.event_log_id }}</dd>
{% endif %}
<dt>{% trans "Severity" %}</dt>
<dd>{{ history.severity }}</dd>
<dt>{% trans "Alarm State" %}</dt>
<dd>{{ history.state }}</dd>
<dt>{% trans "Alarm Type" %}</dt>
<dd>{{ history.event_log_type }}</dd>
<dt>{% trans "Timestamp" %}</dt>
<dd>{{ history.timestamp|parse_isotime }}</dd>
<dt>{% trans "Suppression" %}</dt>
<dd>{{ history.suppression }}</dd>
</dl>
<dl>
<dt>{% trans "Entity Instance ID" %}</dt>
<dd>{{ history.entity_instance_id }}</dd>
{% if history.entity_type_id != '' and history.entity_type_id != ' ' %}
<dt>{% trans "Entity Type ID" %}</dt>
<dd>{{ history.entity_type_id }}</dd>
{% endif %}
<dt>{% trans "Probable Cause" %}</dt>
<dd>{{ history.probable_cause }}</dd>
{% if history.proposed_repair_action != '' and history.proposed_repair_action != ' ' %}
<dt>{% trans "Proposed Repair Action" %}</dt>
<dd>{{ history.proposed_repair_action }}</dd>
{% endif %}
<dt>{% trans "Service Affecting" %}</dt>
<dd>{{ history.service_affecting }}</dd>
{% if history.reason_text != '' and history.reason_text != ' ' %}
<dt>{% trans "Reason" %}</dt>
<dd>{{ history.reason_text }}</dd>
{% endif %}
</dl>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,50 +0,0 @@
{% extends 'base.html' %}
{% load i18n breadcrumb_nav %}
{% block title %}{% trans "Customer Log Details" %}{% endblock %}
{% block main %}
{% if log.event_log_id == '' or log.event_log_id == ' ' %}
<h3> {{log.reason_text }} </h3>
{% else %}
<h3> {{log.event_log_id }} - {{log.reason_text }} </h3>
{% endif %}
<div class="row">
<div class="col-sm-12">
<div class="info row-fluid detail">
<h4>{% trans "Info" %}</h4>
<hr class="header_rule">
<dl>
<dt>{% trans "Log UUID" %}</dt>
<dd>{{ log.uuid }}</dd>
{% if log.event_log_id != '' and log.event_log_id != ' ' %}
<dt>{% trans "Log ID" %}</dt>
<dd>{{ log.event_log_id }}</dd>
{% endif %}
<dt>{% trans "Severity" %}</dt>
<dd>{{ log.severity }}</dd>
<dt>{% trans "Log Type" %}</dt>
<dd>{{ log.event_log_type }}</dd>
<dt>{% trans "Timestamp" %}</dt>
<dd>{{ log.timestamp|parse_isotime }}</dd>
</dl>
<dl>
<dt>{% trans "Entity Instance ID" %}</dt>
<dd>{{ log.entity_instance_id }}</dd>
{% if log.entity_type_id != '' and log.entity_type_id != ' ' %}
<dt>{% trans "Entity Type ID" %}</dt>
<dd>{{ log.entity_type_id }}</dd>
{% endif %}
<dt>{% trans "Probable Cause" %}</dt>
<dd>{{ log.probable_cause }}</dd>
<dt>{% trans "Service Affecting" %}</dt>
<dd>{{ log.service_affecting }}</dd>
{% if log.reason_text != '' and log.reason_text != ' ' %}
<dt>{% trans "Reason" %}</dt>
<dd>{{ log.reason_text }}</dd>
{% endif %}
</dl>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,52 +0,0 @@
{% extends 'base.html' %}
{% load i18n breadcrumb_nav %}
{% block title %}{% trans "Alarm Details" %}{% endblock %}
{% block main %}
<h3> {{alarm.alarm_id }} - {{alarm.reason_text }}</h3>
<div class="row">
<div class="col-sm-12">
<div class="info row-fluid detail">
<h4>{% trans "Info" %}</h4>
<hr class="header_rule">
<dl>
<dt>{% trans "Alarm UUID" %}</dt>
<dd>{{ alarm.uuid }}</dd>
<dt>{% trans "Alarm ID" %}</dt>
<dd>{{ alarm.alarm_id }}</dd>
<dt>{% trans "Severity" %}</dt>
<dd>{{ alarm.severity }}</dd>
<dt>{% trans "Alarm State" %}</dt>
<dd>{{ alarm.alarm_state }}</dd>
<dt>{% trans "Alarm Type" %}</dt>
<dd>{{ alarm.alarm_type }}</dd>
<dt>{% trans "Timestamp" %}</dt>
<dd>{{ alarm.timestamp|parse_isotime }}</dd>
<dt>{% trans "Suppression" %}</dt>
<dd>{{ alarm.suppression }}</dd>
</dl>
<dl>
<dt>{% trans "Entity Instance ID" %}</dt>
<dd>{{ alarm.entity_instance_id }}</dd>
<dt>{% trans "Entity Type ID" %}</dt>
<dd>{{ alarm.entity_type_id }}</dd>
<dt>{% trans "Probable Cause" %}</dt>
<dd>{{ alarm.probable_cause }}</dd>
<dt>{% trans "Proposed Repair Action" %}</dt>
<dd>{{ alarm.proposed_repair_action }}</dd>
<dt>{% trans "Service Affecting" %}</dt>
<dd>{{ alarm.service_affecting }}</dd>
<dt>{% trans "Management Affecting" %}</dt>
<dd>{{ alarm.mgmt_affecting }}</dd>
{% if alarm.reason_text != '' and alarm.reason_text != ' ' %}
<dt>{% trans "Reason" %}</dt>
<dd>{{ alarm.reason_text }}</dd>
{% endif %}
</dl>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,5 +0,0 @@
{% load i18n %}
{% block main %}
{{ table.render }}
{% endblock %}

View File

@ -1,29 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Fault Management" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Fault Management")%}
{% endblock page_header %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}
{% block js %}
{{ block.super }}
<script type="text/javascript" charset="utf-8">
horizon.refresh.addRefreshFunction(function (html) {
var $old_stats = $('#active-alarm-stats');
var $new_stats = $(html).find('#active-alarm-stats');
if ($new_stats.html() != $old_stats.html()) {
$old_stats.replaceWith($new_stats);
}
});
</script>
{% endblock %}

View File

@ -1,35 +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.
#
# Copyright (c) 2013-2015 Wind River Systems, Inc.
#
from django.conf.urls import url # noqa
from starlingx_dashboard.dashboards.admin.fault_management import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<id>[^/]+)/detail/$',
views.DetailView.as_view(), name='detail'),
url(r'^(?P<id>[^/]+)/eventlogdetail/$',
views.EventLogDetailView.as_view(), name='eventlogdetail'),
url(r'^banner/$', views.BannerView.as_view(),
name='banner')
]

View File

@ -1,175 +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.
#
# Copyright (c) 2013-2018 Wind River Systems, Inc.
#
import logging
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _ # noqa
from django.views.generic import TemplateView
from horizon import exceptions
from horizon import tabs
from horizon import views
from openstack_dashboard.api.base import is_service_enabled
from starlingx_dashboard.api import dc_manager
from starlingx_dashboard.api import fm
from starlingx_dashboard.dashboards.admin.fault_management import \
tabs as project_tabs
LOG = logging.getLogger(__name__)
class IndexView(tabs.TabbedTableView):
tab_group_class = project_tabs.AlarmsTabs
template_name = 'admin/fault_management/index.html'
page_title = _("Fault Management")
class DetailView(views.HorizonTemplateView):
template_name = 'admin/fault_management/_detail_overview.html'
page_title = 'Alarm Details'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["alarm"] = self.get_data()
return context
def get_data(self):
if not hasattr(self, "_alarm"):
alarm_uuid = self.kwargs['id']
try:
alarm = fm.alarm_get(self.request, alarm_uuid)
except Exception:
redirect = reverse('horizon:admin:fault_management:index')
exceptions.handle(self.request,
_('Unable to retrieve details for '
'alarm "%s".') % alarm_uuid,
redirect=redirect)
self._alarm = alarm
return self._alarm
class EventLogDetailView(views.HorizonTemplateView):
# Strategy behind this event log detail view is to
# first retrieve the event_log data, then examine the event's
# state property, and from that, determine if it should use
# the _detail_history.html (alarmhistory) template or
# or use the _detail_log.html (customer log) template
def get_template_names(self):
if self.type == "alarmhistory":
template_name = 'admin/fault_management/_detail_history.html'
else:
template_name = 'admin/fault_management/_detail_log.html'
return template_name
def _detectEventLogType(self):
if hasattr(self, "type"):
return self.type
if not self._eventlog:
raise Exception("Cannot determine Event Log type for "
"EventLogDetailView. First retrieve "
"Eventlog data")
if self._eventlog.state == "log":
self.type = "log"
elif self._eventlog.state in ["set", "clear"]:
self.type = "alarmhistory"
else:
raise Exception("Invalid state = '{}'. Cannot "
"determine Event log type for "
"event log".format(self._eventlog.state))
return self.type
def get_context_data(self, **kwargs):
context = super(EventLogDetailView, self).get_context_data(**kwargs)
data = self.get_data()
if self.type == "alarmhistory":
self.page_title = 'Historical Alarm Details'
self.template_name = 'admin/fault_management/_detail_history.html'
context["history"] = data
else:
self.page_title = 'Customer Log Detail'
self.template_name = 'admin/fault_management/_detail_log.html'
context["log"] = data
return context
def get_data(self):
if not hasattr(self, "_eventlog"):
uuid = self.kwargs['id']
try:
self._eventlog = fm.event_log_get(self.request, uuid)
self._detectEventLogType()
except Exception:
redirect = reverse('horizon:admin:fault_management:index')
exceptions.handle(self.request,
_('Unable to retrieve details for '
'event log "%s".') % uuid,
redirect=redirect)
return self._eventlog
class BannerView(TemplateView):
template_name = 'header/_alarm_banner.html'
def get_context_data(self, **kwargs):
context = super(BannerView, self).get_context_data(**kwargs)
if not self.request.is_ajax():
raise exceptions.NotFound()
if (not self.request.user.is_authenticated() or
not self.request.user.is_superuser):
context["alarmbanner"] = False
elif 'dc_admin' in self.request.META.get('HTTP_REFERER'):
summaries = self.get_subcloud_data()
central_summary = self.get_data()
summaries.append(central_summary)
context["dc_admin"] = True
context["alarmbanner"] = True
context["OK"] = len(
[s for s in summaries if s.status == 'OK'])
context["degraded"] = len(
[s for s in summaries if s.status == 'degraded'])
context["critical"] = len(
[s for s in summaries if s.status == 'critical'])
context["disabled"] = len(
[s for s in summaries if s.status == 'disabled'])
elif is_service_enabled(self.request, 'platform'):
context["summary"] = self.get_data()
context["alarmbanner"] = True
return context
def get_data(self):
summary = None
try:
summary = fm.alarm_summary_get(self.request)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve alarm summary.'))
return summary
def get_subcloud_data(self):
return dc_manager.alarm_summary_list(self.request)

View File

@ -1,15 +1,16 @@
#
# Copyright (c) 2016-2018 Wind River Systems, Inc.
# Copyright (c) 2016-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from horizon import tables
from horizon.utils import filters as utils_filters
import logging
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _
from starlingx_dashboard.dashboards.admin.fault_management import \
tables as fm_tables
from starlingx_dashboard.dashboards.admin.inventory.interfaces import \
tables as if_tables
from starlingx_dashboard.dashboards.admin.providernets.providernets.ranges \
@ -17,6 +18,19 @@ from starlingx_dashboard.dashboards.admin.providernets.providernets.ranges \
LOG = logging.getLogger(__name__)
SUPPRESSION_STATUS_CHOICES = (
("suppressed", False),
("unsuppressed", True),
("None", True),
)
SUPPRESSION_STATUS_DISPLAY_CHOICES = (
("suppressed", pgettext_lazy("Indicates this type of alarm \
is suppressed", "suppressed")),
("unsuppressed", pgettext_lazy("Indicates this type of alarm \
is unsuppressed", "unsuppressed")),
("None", pgettext_lazy("Indicates an event type", "None")),
)
class ProviderNetworkRangeTable(sr_tables.ProviderNetworkRangeTable):
class Meta(object):
@ -26,7 +40,34 @@ class ProviderNetworkRangeTable(sr_tables.ProviderNetworkRangeTable):
row_actions = ()
class AlarmsTable(fm_tables.AlarmsTable):
def get_alarm_link_url(alarm):
return "/ngdetails/OS::StarlingX::ActiveAlarms/" + alarm.uuid
class AlarmsTable(tables.DataTable):
alarm_id = tables.Column('alarm_id',
verbose_name=_('Alarm ID'),
link=get_alarm_link_url)
reason_text = tables.Column('reason_text',
verbose_name=_('Reason Text'))
entity_instance_id = tables.Column('entity_instance_id',
verbose_name=_('Entity Instance ID'))
suppression_status = \
tables.Column('suppression_status',
verbose_name=_('Suppression Status'),
status=True,
status_choices=SUPPRESSION_STATUS_CHOICES,
display_choices=SUPPRESSION_STATUS_DISPLAY_CHOICES)
severity = tables.Column('severity',
verbose_name=_('Severity'))
timestamp = tables.Column('timestamp',
attrs={'data-type': 'timestamp'},
filters=(utils_filters.parse_isotime,),
verbose_name=_('Timestamp'))
def get_object_id(self, obj):
return obj.uuid
class Meta(object):
name = "alarms"
verbose_name = _("Related Alarms")

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 Wind River Systems, Inc.
* Copyright (c) 2017-2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
@ -105,7 +105,7 @@
/////////////
function goToCentralAlarmDetails(cloud) {
$window.location.href = "/auth/switch_services_region/RegionOne/?next=/admin/fault_management/";
$window.location.href = "/auth/switch_services_region/RegionOne/?next=/admin/active_alarms/";
}
function goToCentralHostDetails(cloud) {

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 Wind River Systems, Inc.
* Copyright (c) 2017-2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
@ -386,7 +386,7 @@
keystone.getCurrentUserSession().success(function(session){
session.available_services_regions.indexOf(cloud.name)
if (session.available_services_regions.indexOf(cloud.name) > -1) {
$window.location.href = "/auth/switch_services_region/"+ cloud.name + "/?next=/admin/fault_management/";
$window.location.href = "/auth/switch_services_region/"+ cloud.name + "/?next=/admin/active_alarms/";
} else {
toast.add('error', ctrl.endpointErrorMsg);
// TODO(tsmith) should we force a logout here with an reason message?

View File

@ -1,48 +0,0 @@
/**
* Copyright (c) 2018 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function () {
'use strict';
angular
.module('horizon.app.core.openstack-service-api')
.factory('horizon.app.core.openstack-service-api.fm', FmAPI);
FmAPI.$inject = [
'$q',
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service',
'$http'
];
function FmAPI($q, apiService, toastService, $http) {
var service = {
getAlarmSummary: getAlarmSummary
};
var csrf_token = $('input[name=csrfmiddlewaretoken]').val();
$http.defaults.headers.post['X-CSRFToken'] = csrf_token;
$http.defaults.headers.common['X-CSRFToken'] = csrf_token;
$http.defaults.headers.put['X-CSRFToken'] = csrf_token;
return service;
///////////////////
// Alarm Summary //
///////////////////
function getAlarmSummary() {
return apiService.get('/api/fm/alarm_summary/')
.error(function () {
toastService.clearErrors();
toastService.add('error', gettext("Unable to retrieve the System Controller's alarm summary."));
});
}
}
}());

View File

@ -0,0 +1,6 @@
# A list of applications to be added to INSTALLED_APPS.
ADD_INSTALLED_APPS = ['starlingx_dashboard']
FEATURE = ['starlingx_dashboard']
AUTO_DISCOVER_STATIC_FILES = True

View File

@ -0,0 +1,18 @@
from django.utils.translation import ugettext_lazy as _
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
PANEL_GROUP = 'fault_management'
# The display name of the PANEL_GROUP. Required.
PANEL_GROUP_NAME = _('Fault Management')
# The slug of the dashboard the PANEL_GROUP associated with. Required.
PANEL_GROUP_DASHBOARD = 'admin'
ADD_ANGULAR_MODULES = [
'horizon.dashboard.fault_management'
]
ADD_SCSS_FILES = [
'dashboard/fault_management/fault_management.scss'
]
AUTO_DISCOVER_STATIC_FILES = True

View File

@ -1,10 +1,10 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'fault_management'
PANEL = 'active_alarms'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'fault_management'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'platform'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'starlingx_dashboard.dashboards.admin.' \
'fault_management.panel.FaultManagement'
'active_alarms.panel.ActiveAlarms'

View File

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'events'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'fault_management'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'starlingx_dashboard.dashboards.admin.events.panel.Events'

View File

@ -0,0 +1,10 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'events_suppression'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'fault_management'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'starlingx_dashboard.dashboards.admin.events_suppression.panel.' \
'EventsSuppression'

View File

@ -0,0 +1,116 @@
/**
* Copyright (c) 2018-2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function () {
'use strict';
angular
.module('horizon.app.core.openstack-service-api')
.factory('horizon.app.core.openstack-service-api.fm', FmAPI);
FmAPI.$inject = [
'$q',
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service',
'$http'
];
function FmAPI($q, apiService, toastService, $http) {
var service = {
getAlarmSummary: getAlarmSummary,
getAlarms: getAlarms,
getAlarm: getAlarm,
getEvents: getEvents,
getEvent: getEvent,
getEventsSuppression: getEventsSuppression,
updateEventSuppression: updateEventSuppression
};
var csrf_token = $('input[name=csrfmiddlewaretoken]').val();
$http.defaults.headers.post['X-CSRFToken'] = csrf_token;
$http.defaults.headers.common['X-CSRFToken'] = csrf_token;
$http.defaults.headers.put['X-CSRFToken'] = csrf_token;
return service;
///////////////////////////////
// Alarms
function getAlarmSummary() {
return apiService.get('/api/fm/alarm_summary/')
.error(function () {
toastService.clearErrors();
toastService.add('error', gettext("Unable to retrieve alarm summary."));
});
}
function getAlarms() {
var results = apiService.get('/api/fm/alarm_list/?include_suppress=True')
return results
.error(function () {
toastService.clearErrors();
toastService.add('error', gettext("Unable to retrieve alarms."));
});
}
function getAlarm(uuid) {
var results = apiService.get('/api/fm/alarm_get/' + uuid)
return results
.error(function() {
var msg = gettext("Unable to retrieve alarm with uuid: %(uuid)s.");
toastService.add('error', interpolate(msg, {uuid: uuid}, true));
});
}
///////////////////////////////
// Events
function getEvents() {
var results = apiService.get('/api/fm/event_log_list/')
return results
.error(function () {
toastService.clearErrors();
toastService.add('error', gettext("Unable to retrieve events."));
});
}
function getEvent(uuid) {
var results = apiService.get('/api/fm/event_log_get/' + uuid)
return results
.error(function() {
var msg = gettext("Unable to retrieve event with uuid: %(uuid)s.");
toastService.add('error', interpolate(msg, {uuid: uuid}, true));
});
}
///////////////////////////////
// Events Suppression
function getEventsSuppression(include_unsuppressed) {
var query_string = '/api/fm/events_suppression_list/'
if (include_unsuppressed) {
query_string = query_string + '?include_unsuppressed'
}
var results = apiService.get(query_string);
return results
.error(function () {
toastService.clearErrors();
toastService.add('error', gettext("Unable to retrieve events suppression."));
});
}
function updateEventSuppression(uuid, params) {
var results = apiService.patch('/api/fm/event_suppression/' + uuid, params)
// Errors are expected to be processed by the caller, just return
return results;
}
}
}());

View File

@ -0,0 +1,194 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @name horizon.dashboard.fault_management.active_alarms
* @ngModule
* @description
* Provides all the services and widgets require to display the Alarm
* panel
*/
angular
.module('horizon.dashboard.fault_management.active_alarms', [
'ngRoute',
'horizon.dashboard.fault_management.active_alarms.details'
])
.constant('horizon.dashboard.fault_management.active_alarms.resourceType', 'OS::StarlingX::ActiveAlarms')
.run(run)
.config(config);
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.fault_management.active_alarms.service',
'horizon.dashboard.fault_management.active_alarms.basePath',
'horizon.dashboard.fault_management.active_alarms.resourceType'
];
function run(registry, service, basePath, resourceType) {
registry.getResourceType(resourceType)
.setNames(gettext('Active Alarm'), gettext('Active Alarms'))
// for detail summary view on table row
.setSummaryTemplateUrl(basePath + 'details/drawer.html')
// set default url for index view. this will be used for reproducing
// sidebar and breadcrumb when refreshing or accessing directly
// details view.
// TODO(kbujold): Uncomment when we rebase to Stein, to fix upstream bug 1746706
//.setDefaultIndexUrl('/admin/fault_management/')
// specify items for table row items, summary view and details view
.setProperties(properties())
// get items for table
.setListFunction(service.getPromise)
// specify table columns
.tableColumns
.append({
id: 'alarm_id',
priority: 1,
urlFunction: service.urlFunction
})
.append({
id: 'reason_text',
priority: 1
})
.append({
id: 'entity_instance_id',
priority: 1
})
.append({
id: 'severity',
priority: 1,
sortDefault: 'true'
})
.append({
id: 'suppression_status',
priority: 1,
allowed: service.suppressColAllowedPromiseFunction
})
.append({
id: 'timestamp',
priority: 1
});
// for magic-search
registry.getResourceType(resourceType).filterFacets
.append({
'label': gettext('Alarm ID'),
'name': 'alarm_id',
'singleton': true
})
.append({
'label': gettext('Entity Instance ID'),
'name': 'entity_instance_id',
'singleton': true
})
.append({
'label': gettext('Reason Text'),
'name': 'reason_text',
'singleton': true
})
.append({
'label': gettext('Severity'),
'name': 'severity',
'singleton': true,
'options': [
{label: gettext('critical'), key: 'critical'},
{label: gettext('major'), key: 'major'},
{label: gettext('minor'), key: 'minor'},
{label: gettext('warning'), key: 'warning'}
]
})
.append({
'label': gettext('Timestamp'),
'name': 'timestamp',
'singleton': true
})
.append({
'label': gettext('Management Affecting'),
'name': 'mgmt_affecting',
'singleton': true,
'options': [
{label: gettext('True'), key: 'True'},
{label: gettext('False'), key: 'False'}
]
})
.append({
'label': gettext('UUID'),
'name': 'uuid',
'singleton': true
});
}
function properties() {
return {
uuid: { label: gettext('Alarm UUID'), filters: ['noValue'] },
alarm_id: { label: gettext('Alarm ID'), filters: ['noValue'] },
reason_text: { label: gettext('Reason Text'), filters: ['noValue'] },
entity_instance_id: { label: gettext('Entity Instance ID'), filters: ['noValue'] },
severity: { label: gettext('Severity'), filters: ['noValue'] },
timestamp: { label: gettext('Timestamp'), filters: ['noValue'] },
mgmt_affecting: { label: gettext('Management Affecting'), filters: ['noValue'] },
suppression_status: { label: gettext('Suppression Status'), filters: ['noValue'] },
alarm_state: { label: gettext('Alarm State'), filters: ['noValue'] },
service_affecting: { label: gettext('Service Affecting'), filters: ['noValue'] },
alarm_type: { label: gettext('Alarm Type'), filters: ['noValue'] },
probable_cause: { label: gettext('Probable Cause'), filters: ['noValue'] },
entity_type_id: { label: gettext('Entity Type ID'), filters: ['noValue'] },
proposed_repair_action: { label: gettext('Proposed Repair Action'), filters: ['noValue'] },
is_suppressed: { label: gettext('IsSuppressed'), filters: ['noValue'] },
};
}
config.$inject = [
'$provide',
'$windowProvider',
'$routeProvider'
];
/**
* @name config
* @param {Object} $provide
* @param {Object} $windowProvider
* @param {Object} $routeProvider
* @description Routes used by this module.
* @returns {undefined} Returns nothing
*/
function config($provide, $windowProvider, $routeProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/fault_management/active_alarms/';
$provide.constant('horizon.dashboard.fault_management.active_alarms.basePath', path);
$routeProvider.when('/admin/active_alarms', {
templateUrl: path + 'panel.html',
resolve: {
searchResults: ['horizon.dashboard.fault_management.active_alarms.service', function (searchService) {
return searchService.getSuppressionList();
}]
}
});
}
})();

View File

@ -0,0 +1,30 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
describe('horizon.dashboard.fault_management.active_alarms', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.fault_management.active_alarms')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,136 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
angular.module('horizon.dashboard.fault_management.active_alarms')
.factory('horizon.dashboard.fault_management.active_alarms.service',
service);
service.$inject = [
'$filter',
'horizon.app.core.detailRoute',
'horizon.app.core.openstack-service-api.fm',
'$q',
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.fault_management.active_alarms.resourceType'
];
/*
* @ngdoc factory
* @name horizon.dashboard.fault_management.active_alarms.service
*
* @description
* This service provides functions that are used through the Alarms
* features. These are primarily used in the module registrations
* but do not need to be restricted to such use. Each exposed function
* is documented below.
*/
function service($filter, detailRoute, api, $q, registry, resourceType) {
var showSuppressColumn = null;
return {
getPromise: getPromise,
urlFunction: urlFunction,
suppressColAllowedPromiseFunction: suppressColAllowedPromiseFunction,
getSuppressionList: getSuppressionList
};
function getPromise(params) {
return api.getAlarms(params).then(modifyResponse);
}
function modifyResponse(response) {
return {data: {items: response.data.items.map(modifyItem)}};
function modifyItem(item) {
item.trackBy = item.uuid;
if (item.suppression_status == 'suppressed') {
item.is_suppressed = true;
}
else {
item.is_suppressed = false;
}
return item;
}
}
function urlFunction(item) {
return detailRoute + 'OS::StarlingX::ActiveAlarms/' + item.uuid;
}
function getSuppressionList() {
var include_unsuppressed = false;
return api.getEventsSuppression(include_unsuppressed).then(modifyResponseSupp);
}
function modifyResponseSupp(response) {
if (response.data.items.length == 0) {
showSuppressColumn = false;
}
else {
showSuppressColumn = true;
}
return;
}
/**
* @name suppressColAllowedPromiseFunction
* @description
* If there are no unsuppressed events then we do not show the Suppression
* Status column. We also do show the the filter associated with that column.
*/
function suppressColAllowedPromiseFunction() {
var filters = registry.getResourceType(resourceType).filterFacets;
var index = filters.findIndex(obj => obj['id'] === 'suppression_status');
if (showSuppressColumn === false) {
if (index >= 0) {
filters.remove('suppression_status')
}
return $q.reject();
}
else {
if (index < 0) {
filters.append({
'label': gettext('Suppression Status'),
'id': 'suppression_status',
'name': 'is_suppressed',
'singleton': false,
'options': [
{label: gettext('suppressed'), key: 'true'},
{label: gettext('unsuppressed'), key: 'false'}
]
});
}
return $q.resolve();
}
}
}
})();

View File

@ -0,0 +1,59 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
describe('Active Alarms service', function() {
var service;
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.fault_management.active_alarms'));
beforeEach(inject(function($injector) {
service = $injector.get('horizon.dashboard.fault_management.active_alarms.service');
}));
describe('getPromise', function() {
it("provides a promise", inject(function($q, $injector, $timeout) {
var api = $injector.get('horizon.app.core.openstack-service-api.fm');
var deferred = $q.defer();
spyOn(api, 'getAlarms').and.returnValue(deferred.promise);
var result = service.getPromise({});
deferred.resolve({
data:{
items: [{uuid: '123abc', reason_text: 'resource1'}]
}
});
$timeout.flush();
expect(api.getAlarms).toHaveBeenCalled();
expect(result.$$state.value.data.items[0].reason_text).toBe('resource1');
}));
});
describe('urlFunction', function() {
it("get url", inject(function($injector) {
var detailRoute = $injector.get('horizon.app.core.detailRoute');
var result = service.urlFunction({uuid:"123abc"});
expect(result).toBe(detailRoute + "OS::StarlingX::ActiveAlarms/123abc");
}));
});
});
})();

View File

@ -0,0 +1,64 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @ngname horizon.dashboard.fault_management.active_alarms.details
*
* @description
* Provides details features for Alarm.
*/
angular
.module('horizon.dashboard.fault_management.active_alarms.details', [
'horizon.app.core',
'horizon.framework.conf'
])
.run(registerDetails);
registerDetails.$inject = [
'horizon.app.core.openstack-service-api.fm',
'horizon.dashboard.fault_management.active_alarms.basePath',
'horizon.dashboard.fault_management.active_alarms.resourceType',
'horizon.framework.conf.resource-type-registry.service'
];
function registerDetails(
api,
basePath,
resourceType,
registry
) {
registry.getResourceType(resourceType)
.setLoadFunction(loadFunction)
.detailsViews.append({
id: 'alarmsDetailsOverview',
name: gettext('Overview'),
template: basePath + 'details/overview.html'
});
function loadFunction(uuid) {
return api.getAlarm(uuid);
}
}
})();

View File

@ -0,0 +1,5 @@
<hz-resource-property-list
resource-type-name="OS::StarlingX::ActiveAlarms"
item="item"
property-groups="[['uuid', 'mgmt_affecting']]">
</hz-resource-property-list>

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
angular
.module('horizon.dashboard.fault_management.active_alarms')
.controller('horizon.dashboard.fault_management.active_alarms.OverviewController', controller);
controller.$inject = [
'$scope'
];
function controller(
$scope
) {
var ctrl = this;
ctrl.alarm = {};
$scope.context.loadPromise.then(onGetAlarm);
function onGetAlarm(item) {
ctrl.alarm = item.data;
}
}
})();

View File

@ -0,0 +1,17 @@
<div ng-controller="horizon.dashboard.fault_management.active_alarms.OverviewController as ctrl">
<div class="row">
<div class="col-md-12 detail">
<h3> {$ ctrl.alarm.alarm_id $} - {$ ctrl.alarm.reason_text $} </h3>
<hr>
<hz-resource-property-list
resource-type-name="OS::StarlingX::ActiveAlarms"
cls="dl-horizontal"
item="ctrl.alarm"
property-groups="[['uuid', 'alarm_id', 'severity', 'alarm_state', 'alarm_type', 'timestamp',
'suppression_status', 'entity_instance_id', 'entity_type_id',
'probable_cause', 'proposed_repair_action', 'service_affecting',
'mgmt_affecting', 'reason_text']]">
</hz-resource-property-list>
</div>
</div>
</div>

View File

@ -0,0 +1,6 @@
<hz-resource-panel resource-type-name="OS::StarlingX::ActiveAlarms">
<hz-resource-table resource-type-name="OS::StarlingX::ActiveAlarms"
track-by="trackBy">
</hz-resource-table>
</hz-resource-panel>

View File

@ -0,0 +1,64 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @ngname horizon.dashboard.fault_management.events.details
*
* @description
* Provides details features for Event.
*/
angular
.module('horizon.dashboard.fault_management.events.details', [
'horizon.app.core',
'horizon.framework.conf'
])
.run(registerDetails);
registerDetails.$inject = [
'horizon.app.core.openstack-service-api.fm',
'horizon.dashboard.fault_management.events.basePath',
'horizon.dashboard.fault_management.events.resourceType',
'horizon.framework.conf.resource-type-registry.service'
];
function registerDetails(
api,
basePath,
resourceType,
registry
) {
registry.getResourceType(resourceType)
.setLoadFunction(loadFunction)
.detailsViews.append({
id: 'eventsDetailsOverview',
name: gettext('Overview'),
template: basePath + 'details/overview.html'
});
function loadFunction(uuid) {
return api.getEvent(uuid);
}
}
})();

View File

@ -0,0 +1,5 @@
<hz-resource-property-list
resource-type-name="OS::StarlingX::Events"
item="item"
property-groups="[['uuid', 'event_type']]">
</hz-resource-property-list>

View File

@ -0,0 +1,48 @@
/*
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
angular
.module('horizon.dashboard.fault_management.events')
.controller('horizon.dashboard.fault_management.events.OverviewController', controller);
controller.$inject = [
'$scope',
'horizon.dashboard.fault_management.events.service'
];
function controller(
$scope, eventService
) {
var ctrl = this;
ctrl.event = {};
$scope.context.loadPromise.then(onGetEvent);
function onGetEvent(item) {
eventService.setEventType(item.data);
ctrl.event = item.data;
}
}
})();

View File

@ -0,0 +1,17 @@
<div ng-controller="horizon.dashboard.fault_management.events.OverviewController as ctrl">
<div class="row">
<div class="col-md-12 detail">
<h3> {$ ctrl.event.state $} - {$ ctrl.event.event_log_id $} - {$ctrl.event.reason_text $} </h3>
<hr>
<hz-resource-property-list
resource-type-name="OS::StarlingX::Events"
cls="dl-horizontal"
item="ctrl.event"
property-groups="[['uuid', 'event_log_id', 'severity', 'state', 'event_log_type', 'timestamp',
'suppression', 'entity_instance_id', 'entity_type_id',
'probable_cause', 'proposed_repair_action', 'service_affecting',
'reason_text', 'event_type']]">
</hz-resource-property-list>
</div>
</div>
</div>

View File

@ -0,0 +1,208 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @name horizon.dashboard.fault_management.events
* @ngModule
* @description
* Provides all the services and widgets require to display the Event panel
*/
angular
.module('horizon.dashboard.fault_management.events', [
'ngRoute',
'horizon.dashboard.fault_management.events.details'
])
.constant('horizon.dashboard.fault_management.events.resourceType', 'OS::StarlingX::Events')
.run(run)
.config(config);
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.fault_management.events.service',
'horizon.dashboard.fault_management.events.basePath',
'horizon.dashboard.fault_management.events.resourceType'
];
function run(registry, service, basePath, resourceType) {
registry.getResourceType(resourceType)
.setNames(gettext('Event'), gettext('Events'))
// for detail summary view on table row
.setSummaryTemplateUrl(basePath + 'details/drawer.html')
// set default url for index view. this will be used for reproducing
// sidebar and breadcrumb when refreshing or accessing directly
// details view.
// TODO(kbujold): Uncomment when we rebase to Stein, to fix upstream bug 1746706
//.setDefaultIndexUrl('/admin/fault_management/')
// specify items for table row items, summary view and details view
.setProperties(properties())
// get items for table
.setListFunction(service.getPromise)
// specify table columns
.tableColumns
.append({
id: 'timestamp',
priority: 1,
sortDefault: 'reverse'
})
.append({
id: 'state',
priority: 1
})
.append({
id: 'event_log_id',
priority: 1,
urlFunction: service.urlFunction
})
.append({
id: 'reason_text',
priority: 1
})
.append({
id: 'entity_instance_id',
priority: 1
})
.append({
id: 'suppression_status',
priority: 1,
allowed: service.suppressColAllowedPromiseFunction
})
.append({
id: 'severity',
priority: 2
});
// for magic-search
registry.getResourceType(resourceType).filterFacets
.append({
'label': gettext('Event Type'),
'name': 'event_type',
'singleton': true,
'options': [
{label: gettext('log'), key: 'log'},
{label: gettext('alarm'), key: 'alarm'}
]
})
.append({
'label': gettext('ID'),
'name': 'event_log_id',
'singleton': true
})
.append({
'label': gettext('Entity Instance ID'),
'name': 'entity_instance_id',
'singleton': true
})
.append({
'label': gettext('Reason Text'),
'name': 'reason_text',
'singleton': true
})
.append({
'label': gettext('State'),
'name': 'state',
'singleton': true,
'options': [
{label: gettext('clear'), key: 'clear'},
{label: gettext('log'), key: 'log'},
{label: gettext('set'), key: 'set'}
]
})
.append({
'label': gettext('Severity'),
'name': 'severity',
'singleton': true,
'options': [
{label: gettext('critical'), key: 'critical'},
{label: gettext('major'), key: 'major'},
{label: gettext('minor'), key: 'minor'},
{label: gettext('warning'), key: 'warning'},
{label: gettext('not-applicable'), key: 'not-applicable'}
]
})
.append({
'label': gettext('Timestamp'),
'name': 'timestamp',
'singleton': true
})
.append({
'label': gettext('UUID'),
'name': 'uuid',
'singleton': true
});
}
function properties() {
return {
uuid: { label: gettext('Event UUID'), filters: ['noValue'] },
event_log_id: { label: gettext('ID'), filters: ['noValue'] },
reason_text: { label: gettext('Reason Text'), filters: ['noValue'] },
entity_instance_id: { label: gettext('Entity Instance ID'), filters: ['noValue'] },
severity: { label: gettext('Severity'), filters: ['noValue'] },
timestamp: { label: gettext('Timestamp'), filters: ['noValue'] },
suppression_status: { label: gettext('Suppression Status'), filters: ['noValue'] },
suppression: { label: gettext('Suppression'), filters: ['noValue'] },
state: { label: gettext('State'), filters: ['noValue'] },
service_affecting: { label: gettext('Service Affecting'), filters: ['noValue'] },
event_log_type: { label: gettext('Alarm Type'), filters: ['noValue'] },
probable_cause: { label: gettext('Probable Cause'), filters: ['noValue'] },
entity_type_id: { label: gettext('Entity Type ID'), filters: ['noValue'] },
proposed_repair_action: { label: gettext('Proposed Repair Action'), filters: ['noValue'] },
event_type: { label: gettext('Event Type'), filters: ['noValue'] },
_suppression_status: { label: gettext('_suppression_status'), filters: ['noValue'] }
};
}
config.$inject = [
'$provide',
'$windowProvider',
'$routeProvider'
];
/**
* @name config
* @param {Object} $provide
* @param {Object} $windowProvider
* @param {Object} $routeProvider
* @description Routes used by this module.
* @returns {undefined} Returns nothing
*/
function config($provide, $windowProvider, $routeProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/fault_management/events/';
$provide.constant('horizon.dashboard.fault_management.events.basePath', path);
$routeProvider.when('/admin/events', {
templateUrl: path + 'panel.html',
resolve: {
searchResults: ['horizon.dashboard.fault_management.events.service', function (searchService) {
return searchService.getSuppressionList();
}]
}
});
}
})();

View File

@ -0,0 +1,30 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
describe('horizon.dashboard.fault_management.events', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.fault_management.events')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,157 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
angular.module('horizon.dashboard.fault_management.events')
.factory('horizon.dashboard.fault_management.events.service',
service);
service.$inject = [
'$filter',
'horizon.app.core.detailRoute',
'horizon.app.core.openstack-service-api.fm',
'$q',
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.fault_management.events.resourceType'
];
/*
* @ngdoc factory
* @name horizon.dashboard.fault_management.events.service
*
* @description
* This service provides functions that are used through the Events
* features. These are primarily used in the module registrations
* but do not need to be restricted to such use. Each exposed function
* is documented below.
*/
function service($filter, detailRoute, api, $q, registry, resourceType) {
var showSuppressColumn = null;
return {
getPromise: getPromise,
urlFunction: urlFunction,
suppressColAllowedPromiseFunction: suppressColAllowedPromiseFunction,
getSuppressionList: getSuppressionList,
setEventType: setEventType
};
function getPromise(params) {
return api.getEvents(params).then(modifyResponse);
}
function modifyResponse(response) {
return {data: {items: response.data.items.map(modifyItem)}};
function modifyItem(item) {
var timestamp = item.updated_at ? item.updated_at : item.created_at;
item.trackBy = item.uuid + timestamp;
setEventType(item);
if (item.suppression_status == 'suppressed') {
item._suppression_status = 'True';
}
else if (item.suppression_status == 'unsuppressed') {
item._suppression_status = 'False';
}
else {
item._suppression_status = 'None';
}
return item;
}
}
function urlFunction(item) {
return detailRoute + 'OS::StarlingX::Events/' + item.uuid;
}
function getSuppressionList() {
var include_unsuppressed = false;
return api.getEventsSuppression(include_unsuppressed).then(modifyResponseSupp);
}
function modifyResponseSupp(response) {
if (response.data.items.length == 0) {
showSuppressColumn = false;
}
else {
showSuppressColumn = true;
}
return;
}
/**
* @name suppressColAllowedPromiseFunction
* @description
* If there are no unsuppressed events then we do not show the Suppression
* Status column. We also do show the the filter associated with that column.
*/
function suppressColAllowedPromiseFunction() {
var filters = registry.getResourceType(resourceType).filterFacets;
var index = filters.findIndex(obj => obj['id'] === 'suppression_status');
if (showSuppressColumn === false) {
if (index >= 0) {
filters.remove('suppression_status')
}
return $q.reject();
}
else {
if (index < 0) {
filters.append({
'label': gettext('Suppression Status'),
'id': 'suppression_status',
'name': '_suppression_status',
'singleton': false,
'options': [
{label: gettext('suppressed'), key: 'True'},
{label: gettext('unsuppressed'), key: 'False'},
{label: gettext('None'), key: 'None'}
]
});
}
return $q.resolve();
}
}
///////////
// Utils //
///////////
function setEventType(item) {
if (item.state == "clear" || item.state == "set") {
item.event_type = "alarm"
}
else if (item.state == "log" ) {
item.event_type = "log"
}
return item;
}
}
})();

View File

@ -0,0 +1,60 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
describe('Event service', function() {
var service;
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.fault_management.events'));
beforeEach(inject(function($injector) {
service = $injector.get('horizon.dashboard.fault_management.events.service');
}));
describe('getPromise', function() {
it("provides a promise", inject(function($q, $injector, $timeout) {
var api = $injector.get('horizon.app.core.openstack-service-api.fm');
var deferred = $q.defer();
spyOn(api, 'getEvents').and.returnValue(deferred.promise);
var result = service.getPromise({});
deferred.resolve({
data:{
items: [{uuid: '123abc', reason_text: 'resource1'}]
}
});
$timeout.flush();
expect(api.getEvents).toHaveBeenCalled();
expect(result.$$state.value.data.items[0].reason_text).toBe('resource1');
}));
});
describe('urlFunction', function() {
it("get url", inject(function($injector) {
var detailRoute = $injector.get('horizon.app.core.detailRoute');
var result = service.urlFunction({uuid:"123abc"});
expect(result).toBe(detailRoute + "OS::StarlingX::Events/123abc");
}));
});
});
})();

View File

@ -0,0 +1,6 @@
<hz-resource-panel resource-type-name="OS::StarlingX::Events">
<hz-resource-table resource-type-name="OS::StarlingX::Events"
track-by="trackBy">
</hz-resource-table>
</hz-resource-panel>

View File

@ -0,0 +1,76 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @ngname horizon.dashboard.fault_management.events_suppression.actions
*
* @description
* Provides all of the actions for Events Suppression.
*/
angular
.module('horizon.dashboard.fault_management.events_suppression.actions', [
'horizon.framework',
'horizon.dashboard.fault_management'
])
.run(registerEventsSuppressionActions);
registerEventsSuppressionActions.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.framework.util.i18n.gettext',
'horizon.dashboard.fault_management.events_suppression.suppress_event.service',
'horizon.dashboard.fault_management.events_suppression.unsuppress_event.service',
'horizon.dashboard.fault_management.events_suppression.resourceType'
];
function registerEventsSuppressionActions (
registry,
gettext,
suppressEventService,
unsuppressEventService,
resourceType
) {
var eventSuppressionResourceType = registry.getResourceType(resourceType);
eventSuppressionResourceType.itemActions
.append({
id: 'suppressEventAction',
service: suppressEventService,
template: {
type: 'danger',
text: gettext('Suppress Event')
}
});
eventSuppressionResourceType.itemActions
.append({
id: 'unsuppressEventAction',
service: unsuppressEventService,
template: {
type: 'danger',
text: gettext('Unsuppress Event')
}
});
}
})();

View File

@ -0,0 +1,88 @@
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
angular
.module('horizon.dashboard.fault_management')
.factory('horizon.dashboard.fault_management.events_suppression.suppress_event.service', updateService);
updateService.$inject = [
'$location',
'horizon.app.core.openstack-service-api.fm',
'horizon.framework.widgets.modal.simple-modal.service',
'horizon.framework.widgets.toast.service',
'horizon.framework.util.q.extensions'
];
function updateService($location, api, simpleModalService, toastService, $qExtensions) {
var scope;
var service = {
perform: perform,
allowed: allowed
};
var suppressedText = "Suppress Event";
var suppressed = "suppressed";
return service;
function perform(selected, newScope) {
var alarm_id = selected.alarm_id;
var options = {
title: 'Confirm ' + suppressedText,
body: 'You have selected: "' + alarm_id +
'". Please confirm your selection. Events with selected Alarm ID will be ' +
suppressed + '.',
submit: suppressedText,
cancel: 'Cancel'
};
selected = angular.isArray(selected) ? selected : [selected];
return simpleModalService.modal(options).result.then(onModalSubmit);
function onModalSubmit() {
return $qExtensions.allSettled(selected.map(updateEntityPromise)).then(notify);
}
function updateEntityPromise(selected) {
var status = {'suppression_status': suppressed};
return {promise: api.updateEventSuppression(selected.uuid, status)};
}
function notify(result) {
if (result.pass.length > 0) {
var msg = gettext("Events %(suppressed)s for Alarm ID: %(alarm_id)s");
toastService.add('success', interpolate(msg, {suppressed: suppressed, alarm_id: alarm_id}, true));
$location.path('/admin/events_suppression');
}
if (result.fail.length > 0) {
var msg = gettext("Failed to %(suppressed)s events for Alarm ID: %(alarm_id)s");
toastService.add('error', interpolate(msg, {suppressed: suppressed, alarm_id: alarm_id}, true));
return result;
}
}
}
function allowed($scope) {
if ($scope.suppression_status == suppressed) {
return $qExtensions.booleanAsPromise(false);
}
else {
return $qExtensions.booleanAsPromise(true);
}
}
}
})();

View File

@ -0,0 +1,88 @@
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
angular
.module('horizon.dashboard.fault_management')
.factory('horizon.dashboard.fault_management.events_suppression.unsuppress_event.service', updateService);
updateService.$inject = [
'$location',
'horizon.app.core.openstack-service-api.fm',
'horizon.framework.widgets.modal.simple-modal.service',
'horizon.framework.widgets.toast.service',
'horizon.framework.util.q.extensions'
];
function updateService($location, api, simpleModalService, toastService, $qExtensions) {
var scope;
var service = {
perform: perform,
allowed: allowed
};
var unsuppressedText = "Unsuppress Event";
var unsuppressed = "unsuppressed";
return service;
function perform(selected, newScope) {
var alarm_id = selected.alarm_id;
var options = {
title: 'Confirm ' + unsuppressedText,
body: 'You have selected: "' + alarm_id +
'". Please confirm your selection. Events with selected Alarm ID will be ' +
unsuppressed + '.',
submit: unsuppressedText,
cancel: 'Cancel'
};
selected = angular.isArray(selected) ? selected : [selected];
return simpleModalService.modal(options).result.then(onModalSubmit);
function onModalSubmit() {
return $qExtensions.allSettled(selected.map(updateEntityPromise)).then(notify);
}
function updateEntityPromise(selected) {
var status = {'suppression_status': unsuppressed};
return {promise: api.updateEventSuppression(selected.uuid, status)};
}
function notify(result) {
if (result.pass.length > 0) {
var msg = gettext("Events %(unsuppressed)s for Alarm ID: %(alarm_id)s");
toastService.add('success', interpolate(msg, {unsuppressed: unsuppressed, alarm_id: alarm_id}, true));
$location.path('/admin/events_suppression');
}
if (result.fail.length > 0) {
var msg = gettext("Failed to %(unsuppressed)s events for Alarm ID: %(alarm_id)s");
toastService.add('error', interpolate(msg, {unsuppressed: unsuppressed, alarm_id: alarm_id}, true));
return result;
}
}
}
function allowed($scope) {
if ($scope.suppression_status == unsuppressed) {
return $qExtensions.booleanAsPromise(false);
}
else {
return $qExtensions.booleanAsPromise(true);
}
}
}
})();

View File

@ -0,0 +1,131 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @name horizon.dashboard.fault_management.events_suppression
* @ngModule
* @description
* Provides all the services and widgets require to display the Events Suppression
* panel
*/
angular
.module('horizon.dashboard.fault_management.events_suppression', [
'ngRoute',
'horizon.dashboard.fault_management.events_suppression.actions'
])
.constant('horizon.dashboard.fault_management.events_suppression.resourceType', 'OS::StarlingX::EventsSuppression')
.run(run)
.config(config);
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.fault_management.events_suppression.service',
'horizon.dashboard.fault_management.events_suppression.basePath',
'horizon.dashboard.fault_management.events_suppression.resourceType'
];
function run(registry, service, basePath, resourceType) {
registry.getResourceType(resourceType)
.setNames(gettext('Events Suppression'), gettext('Events Suppression'))
// specify items for table row items, summary view and details view
.setProperties(properties())
// get items for table
.setListFunction(service.getPromise)
// specify table columns
.tableColumns
.append({
id: 'alarm_id',
priority: 1,
sortDefault: true
})
.append({
id: 'description',
priority: 1
})
.append({
id: 'suppression_status',
priority: 1
});
// for magic-search
registry.getResourceType(resourceType).filterFacets
.append({
'label': gettext('Event ID'),
'name': 'alarm_id',
'singleton': true
})
.append({
'label': gettext('Description'),
'name': 'description',
'singleton': true
})
.append({
'label': gettext('Status'),
'name': 'is_suppressed',
'singleton': true,
'options': [
{label: gettext('suppressed'), key: 'true'},
{label: gettext('unsuppressed'), key: 'false'}
]
})
;
}
function properties() {
return {
alarm_id: { label: gettext('Event ID'), filters: ['noValue'] },
description: { label: gettext('Description'), filters: ['noValue'] },
uuid: { label: gettext('Event UUID'), filters: ['noValue'] },
suppression_status: { label: gettext('Status'), filters: ['noValue'] },
links: { label: gettext('Links'), filters: ['noValue'] },
is_suppressed: { label: gettext('IsSuppressed'), filters: ['noValue'] }
};
}
config.$inject = [
'$provide',
'$windowProvider',
'$routeProvider'
];
/**
* @name config
* @param {Object} $provide
* @param {Object} $windowProvider
* @param {Object} $routeProvider
* @description Routes used by this module.
* @returns {undefined} Returns nothing
*/
function config($provide, $windowProvider, $routeProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/fault_management/events_suppression/';
$provide.constant('horizon.dashboard.fault_management.events_suppression.basePath', path);
$routeProvider.when('/admin/events_suppression', {
templateUrl: path + 'panel.html'
});
}
})();

View File

@ -0,0 +1,30 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
describe('horizon.dashboard.fault_management.events_suppression', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.fault_management.events_suppression')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,4 @@
// This enables \n to render properly, we use this for the description col
[resource-type-name="OS::StarlingX::EventsSuppression"] table hz-field.ng-scope.ng-isolate-scope {
white-space: pre-wrap;
}

View File

@ -0,0 +1,78 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
angular.module('horizon.dashboard.fault_management.events_suppression')
.factory('horizon.dashboard.fault_management.events_suppression.service',
service);
service.$inject = [
'$filter',
'horizon.app.core.detailRoute',
'horizon.app.core.openstack-service-api.fm'
];
/*
* @ngdoc factory
* @name horizon.dashboard.fault_management.events_suppression.service
*
* @description
* This service provides functions that are used through the Events Suppression
* features. These are primarily used in the module registrations
* but do not need to be restricted to such use. Each exposed function
* is documented below.
*/
function service($filter, detailRoute, api) {
return {
getPromise: getPromise
};
function getPromise(params) {
var include_unsuppressed = true;
return api.getEventsSuppression(include_unsuppressed).then(modifyResponse);
}
function modifyResponse(response) {
return {data: {items: response.data.items.map(modifyItem)}};
function modifyItem(item) {
// This enables the items to be updated on the view
item.trackBy = [
item.uuid,
item.suppression_status,
item.alarm_id
].join('/');
if (item.suppression_status == 'suppressed') {
item.is_suppressed = true;
}
else {
item.is_suppressed = false;
}
return item;
}
}
}
})();

View File

@ -0,0 +1,60 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
"use strict";
describe('Events Suppression service', function() {
var service;
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.fault_management.events_suppression'));
beforeEach(inject(function($injector) {
service = $injector.get('horizon.dashboard.fault_management.events_suppression.service');
}));
describe('getPromise', function() {
it("provides a promise", inject(function($q, $injector, $timeout) {
var api = $injector.get('horizon.app.core.openstack-service-api.fm');
var deferred = $q.defer();
spyOn(api, 'getEventsSuppression').and.returnValue(deferred.promise);
var result = service.getPromise({});
deferred.resolve({
data:{
items: [{uuid: '123abc', description: 'resource1'}]
}
});
$timeout.flush();
expect(api.getEventsSuppression).toHaveBeenCalled();
expect(result.$$state.value.data.items[0].description).toBe('resource1');
}));
});
describe('urlFunction', function() {
it("get url", inject(function($injector) {
var detailRoute = $injector.get('horizon.app.core.detailRoute');
var result = service.urlFunction({id:"123abc"});
expect(result).toBe(detailRoute + "OS::StarlingX::EventsSuppression/123abc");
}));
});
});
})();

View File

@ -0,0 +1,6 @@
<hz-resource-panel resource-type-name="OS::StarlingX::EventsSuppression">
<hz-resource-table resource-type-name="OS::StarlingX::EventsSuppression"
track-by="trackBy">
</hz-resource-table>
</hz-resource-panel>

View File

@ -0,0 +1,48 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @name horizon.dashboard.fault_management
* @description
* Dashboard module to host various platform panels.
*/
// fixme: if ngRoute and $routeProvider are unnecessary, remove them
/* eslint-disable no-unused-vars */
angular
.module('horizon.dashboard.fault_management', [
'horizon.dashboard.fault_management.active_alarms',
'horizon.dashboard.fault_management.events',
'horizon.dashboard.fault_management.events_suppression',
'ngRoute'
])
.config(config);
config.$inject = ['$provide', '$windowProvider', '$routeProvider'];
function config($provide, $windowProvider, $routeProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/fault_management/';
$provide.constant('horizon.dashboard.fault_management.basePath', path);
}
/* eslint-disable no-unused-vars */
})();

View File

@ -0,0 +1,30 @@
/**
* 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.
*/
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
(function() {
'use strict';
describe('horizon.dashboard.fault_management', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.fault_management')).toBeDefined();
});
});
})();

View File

@ -0,0 +1 @@
@import "events_suppression/events_suppression";

View File

@ -1,3 +1,10 @@
/**
* Copyright (c) 2019 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
/* Core functionality related to alam-banner. */
horizon.alarmbanner = {
@ -6,7 +13,7 @@ horizon.alarmbanner = {
var $old = $(location).attr('pathname');
var $url = $(location).attr('href');
$url = $url.replace($old, "/admin/fault_management/banner");
$url = $url.replace($old, "/admin/active_alarms/banner");
horizon.ajax.queue({
url: $url,
@ -49,7 +56,7 @@ horizon.alarmbanner = {
},
onclick: function() {
var $fm = "/admin/fault_management";
var $fm = "/admin/active_alarms";
var $dc = "/dc_admin/";
var $cur = document.location.href;
var $path = document.location.pathname;