summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYoshie Muranaka <yoshiemuranaka@gmail.com>2020-02-07 00:47:28 +0300
committerYoshie Muranaka <yoshiemuranaka@gmail.com>2020-02-17 21:48:43 +0300
commit1ace1d91e80425eeed482b33e21838acb7f7325a (patch)
tree86700b60471ae41222bdba6902b3cb308978953a /src
parentc031b6988d6381ae1d2ecefd04e8c2af47f0ff4a (diff)
downloadwebui-vue-1ace1d91e80425eeed482b33e21838acb7f7325a.tar.xz
Add Health status to app header
Added logging path and interface to websocket subscription data filter, to dynamically indicate Health status in the app header. - Update OverviewEvents to use highPriorityEvents data - Refactor EventLogStore Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com> Change-Id: I35ad30b005c70625a5f6a69488d45db0fa049374
Diffstat (limited to 'src')
-rw-r--r--src/components/AppHeader/AppHeader.vue27
-rw-r--r--src/components/Global/StatusIcon.vue9
-rw-r--r--src/store/modules/GlobalStore.js2
-rw-r--r--src/store/modules/Health/EventLogStore.js134
-rw-r--r--src/store/plugins/WebSocketPlugin.js31
-rw-r--r--src/views/Overview/OverviewEvents.vue4
6 files changed, 148 insertions, 59 deletions
diff --git a/src/components/AppHeader/AppHeader.vue b/src/components/AppHeader/AppHeader.vue
index 880c428f..d411c1f1 100644
--- a/src/components/AppHeader/AppHeader.vue
+++ b/src/components/AppHeader/AppHeader.vue
@@ -14,7 +14,7 @@
<b-nav>
<b-nav-item>
Health
- <status-icon :status="'danger'" />
+ <status-icon :status="healthStatusIcon" />
</b-nav-item>
<b-nav-item>
Power
@@ -46,6 +46,9 @@ export default {
hostStatus() {
return this.$store.getters['global/hostStatus'];
},
+ healthStatus() {
+ return this.$store.getters['eventLog/healthStatus'];
+ },
hostStatusIcon() {
switch (this.hostStatus) {
case 'on':
@@ -56,15 +59,31 @@ export default {
default:
return 'secondary';
}
+ },
+ healthStatusIcon() {
+ switch (this.healthStatus) {
+ case 'good':
+ return 'success';
+ case 'warning':
+ return 'warning';
+ case 'critical':
+ return 'danger';
+ default:
+ return 'secondary';
+ }
}
},
created() {
this.getHostInfo();
+ this.getEvents();
},
methods: {
getHostInfo() {
this.$store.dispatch('global/getHostStatus');
},
+ getEvents() {
+ this.$store.dispatch('eventLog/getEventLogData');
+ },
logout() {
this.$store.dispatch('authentication/logout');
}
@@ -84,6 +103,12 @@ export default {
transition-timing-function: cubic-bezier(0, 0, 0.3, 1);
}
}
+.navbar-dark {
+ .navbar-text,
+ .nav-link {
+ color: $white !important;
+ }
+}
.nav-item {
svg {
fill: $light;
diff --git a/src/components/Global/StatusIcon.vue b/src/components/Global/StatusIcon.vue
index a2c7f04f..d59eaec2 100644
--- a/src/components/Global/StatusIcon.vue
+++ b/src/components/Global/StatusIcon.vue
@@ -1,6 +1,7 @@
<template>
<span :class="['status-icon', status]">
<icon-success v-if="status === 'success'" />
+ <icon-warning v-else-if="status === 'warning'" />
<icon-danger v-else-if="status === 'danger'" />
<icon-secondary v-else />
</span>
@@ -15,8 +16,9 @@ export default {
name: 'StatusIcon',
components: {
iconSuccess: IconCheckmark,
- iconDanger: IconWarning,
- iconSecondary: IconError
+ iconDanger: IconError,
+ iconSecondary: IconError, //TODO: swap with right asset when available
+ iconWarning: IconWarning
},
props: {
status: {
@@ -39,5 +41,8 @@ export default {
&.secondary {
fill: $secondary;
}
+ &.warning {
+ fill: $warning;
+ }
}
</style>
diff --git a/src/store/modules/GlobalStore.js b/src/store/modules/GlobalStore.js
index 18d5043d..21ea796f 100644
--- a/src/store/modules/GlobalStore.js
+++ b/src/store/modules/GlobalStore.js
@@ -37,7 +37,7 @@ const GlobalStore = {
setHostName: (state, hostName) => (state.hostName = hostName),
setBmcTime: (state, bmcTime) => (state.bmcTime = bmcTime),
setHostStatus: (state, hostState) =>
- (state.hostState = hostStateMapper(hostState))
+ (state.hostStatus = hostStateMapper(hostState))
},
actions: {
getHostName({ commit }) {
diff --git a/src/store/modules/Health/EventLogStore.js b/src/store/modules/Health/EventLogStore.js
index 404a9639..3f32ab16 100644
--- a/src/store/modules/Health/EventLogStore.js
+++ b/src/store/modules/Health/EventLogStore.js
@@ -1,70 +1,116 @@
import api from '../../api';
-const severityToPriorityMap = {
- Emergency: 'High',
- Alert: 'High',
- Critical: 'High',
- Error: 'High',
- Warning: 'Medium',
- Notice: 'Low',
- Debug: 'Low',
- Informational: 'Low'
+const EVENT_SEVERITY = {
+ emergency: 'xyz.openbmc_project.Logging.Entry.Level.Emergency',
+ alert: 'xyz.openbmc_project.Logging.Entry.Level.Alert',
+ critical: 'xyz.openbmc_project.Logging.Entry.Level.Critical',
+ error: 'xyz.openbmc_project.Logging.Entry.Level.Error',
+ warning: 'xyz.openbmc_project.Logging.Entry.Level.Warning',
+ notice: 'xyz.openbmc_project.Logging.Entry.Level.Notice',
+ informational: 'xyz.openbmc_project.Logging.Entry.Level.Informational',
+ debug: 'xyz.openbmc_project.Logging.Entry.Level.Debug'
+};
+
+const priorityMapper = severity => {
+ switch (severity) {
+ case EVENT_SEVERITY.emergency:
+ case EVENT_SEVERITY.alert:
+ case EVENT_SEVERITY.critical:
+ case EVENT_SEVERITY.error:
+ return 'high';
+ case EVENT_SEVERITY.warning:
+ return 'medium';
+ case EVENT_SEVERITY.notice:
+ case EVENT_SEVERITY.debug:
+ case EVENT_SEVERITY.informational:
+ return 'low';
+ default:
+ return '';
+ }
+};
+
+const getHealthStatus = allEvents => {
+ let status = 'good';
+ for (const event of allEvents) {
+ if (!event.Resolved && event.priority === 'medium') {
+ status = 'warning';
+ }
+ if (!event.Resolved && event.priority === 'high') {
+ status = 'critical';
+ break;
+ }
+ }
+ return status;
};
const EventLogStore = {
namespaced: true,
state: {
- eventLogData: null
+ allEvents: [],
+ highPriorityEvents: [],
+ healthStatus: null
},
getters: {
- eventLogData: state => state.eventLogData
+ allEvents: state => state.allEvents,
+ highPriorityEvents: state => state.highPriorityEvents,
+ healthStatus: state => state.healthStatus
},
mutations: {
- setEventLogData: (state, eventLogData) =>
- (state.eventLogData = eventLogData)
+ setAllEvents: (state, allEvents) => (state.allEvents = allEvents),
+ setHighPriorityEvents: (state, highPriorityEvents) =>
+ (state.highPriorityEvents = highPriorityEvents),
+ setHealthStatus: (state, status) => (state.healthStatus = status)
},
actions: {
getEventLogData({ commit }) {
api
.get('/xyz/openbmc_project/logging/enumerate')
.then(response => {
- const eventLog = response.data.data;
- const entryNumber = /[1-9]/;
- const eventLogEntries = [];
- /**
- * Entry log endpoints:
- * 'entry' + entry id contain event log entry information
- * 'callout' contains part number and serial number for part affected
- */
- for (let key in eventLog) {
- // Check for event log entry:
- if (
- key.includes('entry') &&
- key.match(entryNumber) &&
- !key.includes('callout')
- ) {
- const eventKey = eventLog[key];
- const eventSeverity = eventKey.Severity.split('.').pop();
- const eventPriority = severityToPriorityMap[eventSeverity];
- eventLogEntries.push(
- Object.assign(
- {
- logId: eventKey.Id,
- priority: eventPriority,
- timestamp: eventKey.Timestamp,
- eventID: eventKey.EventID,
- description: eventKey.Description
- },
- eventKey
- )
- );
- commit('setEventLogData', eventLogEntries);
+ const responseData = response.data.data;
+ const eventLogs = [];
+ for (const key in responseData) {
+ const event = responseData[key];
+ const { Id } = event;
+ if (responseData.hasOwnProperty(key) && Id) {
+ const { EventID, Description, Timestamp, Severity } = event;
+ eventLogs.push({
+ logId: Id,
+ priority: priorityMapper(Severity),
+ timestamp: Timestamp,
+ eventID: EventID,
+ description: Description,
+ ...event
+ });
}
}
+
+ const healthStatus = getHealthStatus(eventLogs);
+ const highPriorityEvents = eventLogs.filter(
+ ({ priority, Resolved }) => priority === 'high' && !Resolved
+ );
+
+ commit('setAllEvents', eventLogs);
+ commit('setHighPriorityEvents', highPriorityEvents);
+ commit('setHealthStatus', healthStatus);
})
.catch(error => {
console.log('Event Log Data:', error);
});
+ },
+ checkHealth({ commit, getters }, interfaces) {
+ if (getters['healthStatus'] === 'critical') return;
+ for (const key in interfaces) {
+ const event = interfaces[key];
+ const eventPriority = priorityMapper(event.Severity);
+ const isEventResolved = event.Resolved;
+ if (!isEventResolved) {
+ if (eventPriority === 'high') {
+ commit('setHealthStatus', 'critical');
+ break;
+ }
+ if (eventPriority === 'medium') commit('setHealthStatus', 'warning');
+ }
+ }
}
}
};
diff --git a/src/store/plugins/WebSocketPlugin.js b/src/store/plugins/WebSocketPlugin.js
index 3e2139dd..409b1686 100644
--- a/src/store/plugins/WebSocketPlugin.js
+++ b/src/store/plugins/WebSocketPlugin.js
@@ -2,16 +2,19 @@
* WebSocketPlugin will allow us to get new data from the server
* without having to poll for changes on the frontend.
*
- * This plugin is subscribed to host state property changes, which
- * is indicated in the app header Power status.
+ * This plugin is subscribed to host state property and logging
+ * changes, indicated in the app header Health and Power status.
*
* https://github.com/openbmc/docs/blob/b41aff0fabe137cdb0cfff584b5fe4a41c0c8e77/rest-api.md#event-subscription-protocol
*/
const WebSocketPlugin = store => {
let ws;
const data = {
- paths: ['/xyz/openbmc_project/state/host0'],
- interfaces: ['xyz.openbmc_project.State.Host']
+ paths: ['/xyz/openbmc_project/state/host0', '/xyz/openbmc_project/logging'],
+ interfaces: [
+ 'xyz.openbmc_project.State.Host',
+ 'xyz.openbmc_project.Logging.Entry'
+ ]
};
const initWebSocket = () => {
@@ -23,11 +26,21 @@ const WebSocketPlugin = store => {
console.error(event);
};
ws.onmessage = event => {
- const {
- properties: { CurrentHostState, RequestedHostTransition } = {}
- } = JSON.parse(event.data);
- const hostState = CurrentHostState || RequestedHostTransition;
- store.commit('global/setHostStatus', hostState);
+ const data = JSON.parse(event.data);
+ const eventInterface = data.interface;
+
+ if (eventInterface === 'xyz.openbmc_project.State.Host') {
+ const { properties: { CurrentHostState } = {} } = data;
+ store.commit('global/setHostStatus', CurrentHostState);
+ } else {
+ const { interfaces, event } = data;
+ if (event === 'InterfacesAdded' && interfaces) {
+ // Checking for 'InterfacesAdded' events
+ // since they have all properties needed to
+ // change health status
+ store.dispatch('eventLog/checkHealth', interfaces);
+ }
+ }
};
};
diff --git a/src/views/Overview/OverviewEvents.vue b/src/views/Overview/OverviewEvents.vue
index 5820e612..d15e158f 100644
--- a/src/views/Overview/OverviewEvents.vue
+++ b/src/views/Overview/OverviewEvents.vue
@@ -13,7 +13,7 @@
<p class="mb-1">{{ logData.eventID }}: {{ logData.description }}</p>
</b-list-group-item>
</b-list-group>
- <b-list-group v-if="!eventLogData">
+ <b-list-group v-if="eventLogData.length === 0">
There are no high priority events to display at this time.
</b-list-group>
</div>
@@ -28,7 +28,7 @@ export default {
},
computed: {
eventLogData() {
- return this.$store.getters['eventLog/eventLogData'];
+ return this.$store.getters['eventLog/highPriorityEvents'];
}
},
created() {