summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json6
-rw-r--r--package.json1
-rw-r--r--src/assets/styles/_table.scss4
-rw-r--r--src/components/AppNavigation/AppNavigation.vue2
-rw-r--r--src/locales/en-US.json11
-rw-r--r--src/router/index.js7
-rw-r--r--src/store/index.js4
-rw-r--r--src/store/modules/Health/SensorsStore.js113
-rw-r--r--src/views/Health/Sensors/Sensors.vue126
-rw-r--r--src/views/Health/Sensors/index.js2
10 files changed, 271 insertions, 5 deletions
diff --git a/package-lock.json b/package-lock.json
index 5222244d..bcd1bbcb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5429,7 +5429,8 @@
"date-fns": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
- "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
+ "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
+ "dev": true
},
"de-indent": {
"version": "1.0.2",
@@ -10698,8 +10699,7 @@
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
- "dev": true
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
diff --git a/package.json b/package.json
index 2d5f4d5e..b2a24237 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"bootstrap-vue": "2.5.0",
"core-js": "3.3.2",
"js-cookie": "^2.2.1",
+ "lodash": "4.17.15",
"vue": "2.6.11",
"vue-i18n": "8.15.3",
"vue-router": "3.1.3",
diff --git a/src/assets/styles/_table.scss b/src/assets/styles/_table.scss
index 528cb805..2372d257 100644
--- a/src/assets/styles/_table.scss
+++ b/src/assets/styles/_table.scss
@@ -1,6 +1,10 @@
table {
position: relative;
z-index: $zindex-dropdown;
+ .status-icon svg {
+ width: 1rem;
+ height: auto;
+ }
}
.table-light {
diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue
index 48b94c3d..d0fee43f 100644
--- a/src/components/AppNavigation/AppNavigation.vue
+++ b/src/components/AppNavigation/AppNavigation.vue
@@ -21,7 +21,7 @@
<b-nav-item href="javascript:void(0)">
{{ $t('appNavigation.hardwareStatus') }}
</b-nav-item>
- <b-nav-item href="javascript:void(0)">
+ <b-nav-item to="/health/sensors">
{{ $t('appNavigation.sensors') }}
</b-nav-item>
</b-collapse>
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 63247da6..adc11850 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -180,6 +180,17 @@
"successRebootStart": "Rebooting BMC."
}
},
+ "pageSensors": {
+ "table": {
+ "currentValue": "Current value",
+ "lowerWarning": "Lower warning",
+ "lowerCritical": "Lower critical",
+ "name": "Name",
+ "status": "Status",
+ "upperWarning": "Upper warning",
+ "upperCritical": "Upper critical"
+ }
+ },
"pageServerPowerOperations": {
"currentStatus": "Current status",
"hostname": "Hostname",
diff --git a/src/router/index.js b/src/router/index.js
index 0d246cda..cd6cf8bf 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -24,6 +24,13 @@ const routes = [
}
},
{
+ path: '/health/sensors',
+ component: () => import('@/views/Health/Sensors'),
+ meta: {
+ title: 'appPageTitle.sensors'
+ }
+ },
+ {
path: '/access-control/local-user-management',
name: 'local-users',
component: () => import('@/views/AccessControl/LocalUserManagement'),
diff --git a/src/store/index.js b/src/store/index.js
index 27216990..08ada05e 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -11,6 +11,7 @@ import ControlStore from './modules/Control/ControlStore';
import PowerControlStore from './modules/Control/PowerControlStore';
import NetworkSettingStore from './modules/Configuration/NetworkSettingsStore';
import EventLogStore from './modules/Health/EventLogStore';
+import SensorsStore from './modules/Health/SensorsStore';
import WebSocketPlugin from './plugins/WebSocketPlugin';
@@ -30,7 +31,8 @@ export default new Vuex.Store({
controls: ControlStore,
powerControl: PowerControlStore,
networkSettings: NetworkSettingStore,
- eventLog: EventLogStore
+ eventLog: EventLogStore,
+ sensors: SensorsStore
},
plugins: [WebSocketPlugin]
});
diff --git a/src/store/modules/Health/SensorsStore.js b/src/store/modules/Health/SensorsStore.js
new file mode 100644
index 00000000..5da15156
--- /dev/null
+++ b/src/store/modules/Health/SensorsStore.js
@@ -0,0 +1,113 @@
+import api from '../../api';
+import { uniqBy } from 'lodash';
+
+const SensorsStore = {
+ namespaced: true,
+ state: {
+ sensors: []
+ },
+ getters: {
+ sensors: state => state.sensors
+ },
+ mutations: {
+ setSensors: (state, sensors) => {
+ state.sensors = uniqBy([...state.sensors, ...sensors], 'name');
+ }
+ },
+ actions: {
+ getAllSensors({ dispatch }) {
+ dispatch('getChassisCollection').then(collection => {
+ collection.forEach(item => {
+ dispatch('getSensors', item);
+ dispatch('getThermalSensors', item);
+ dispatch('getPowerSensors', item);
+ });
+ });
+ },
+ getChassisCollection() {
+ return api
+ .get('/redfish/v1/Chassis')
+ .then(({ data: { Members } }) =>
+ Members.map(member => member['@odata.id'])
+ )
+ .catch(error => console.log(error));
+ },
+ getSensors({ commit }, id) {
+ api
+ .get(`${id}/Sensors`)
+ .then(({ data: { Members = [] } }) => {
+ const promises = Members.map(sensor => api.get(sensor['@odata.id']));
+ api.all(promises).then(
+ api.spread((...responses) => {
+ const sensorData = responses.map(({ data }) => {
+ return {
+ name: data.Name,
+ status: data.Status.Health,
+ currentValue: data.Reading,
+ lowerCaution: data.Thresholds.LowerCaution.Reading,
+ upperCaution: data.Thresholds.UpperCaution.Reading,
+ lowerCritical: data.Thresholds.LowerCritical.Reading,
+ upperCritical: data.Thresholds.UpperCritical.Reading,
+ units: data.ReadingUnits
+ };
+ });
+ commit('setSensors', sensorData);
+ })
+ );
+ })
+ .catch(error => console.log(error));
+ },
+ getThermalSensors({ commit }, id) {
+ api
+ .get(`${id}/Thermal`)
+ .then(({ data: { Fans = [], Temperatures = [] } }) => {
+ const sensorData = [];
+ Fans.forEach(sensor => {
+ sensorData.push({
+ // TODO: add upper/lower threshold
+ name: sensor.Name,
+ status: sensor.Status.Health,
+ currentValue: sensor.Reading,
+ units: sensor.ReadingUnits
+ });
+ });
+ Temperatures.forEach(sensor => {
+ sensorData.push({
+ name: sensor.Name,
+ status: sensor.Status.Health,
+ currentValue: sensor.ReadingCelsius,
+ lowerCaution: sensor.LowerThresholdNonCritical,
+ upperCaution: sensor.UpperThresholdNonCritical,
+ lowerCritical: sensor.LowerThresholdCritical,
+ upperCritical: sensor.UpperThresholdCritical,
+ units: '℃'
+ });
+ });
+ commit('setSensors', sensorData);
+ })
+ .catch(error => console.log(error));
+ },
+ getPowerSensors({ commit }, id) {
+ api
+ .get(`${id}/Power`)
+ .then(({ data: { Voltages = [] } }) => {
+ const sensorData = Voltages.map(sensor => {
+ return {
+ name: sensor.Name,
+ status: sensor.Status.Health,
+ currentValue: sensor.ReadingVolts,
+ lowerCaution: sensor.LowerThresholdNonCritical,
+ upperCaution: sensor.UpperThresholdNonCritical,
+ lowerCritical: sensor.LowerThresholdCritical,
+ upperCritical: sensor.UpperThresholdCritical,
+ units: 'Volts'
+ };
+ });
+ commit('setSensors', sensorData);
+ })
+ .catch(error => console.log(error));
+ }
+ }
+};
+
+export default SensorsStore;
diff --git a/src/views/Health/Sensors/Sensors.vue b/src/views/Health/Sensors/Sensors.vue
new file mode 100644
index 00000000..70d4f90d
--- /dev/null
+++ b/src/views/Health/Sensors/Sensors.vue
@@ -0,0 +1,126 @@
+<template>
+ <b-container fluid>
+ <page-title />
+ <b-row>
+ <b-col xl="12">
+ <b-table
+ sort-icon-left
+ no-sort-reset
+ sticky-header="75vh"
+ sort-by="status"
+ :items="allSensors"
+ :fields="fields"
+ :sort-desc="true"
+ :sort-compare="sortCompare"
+ >
+ <template v-slot:cell(status)="{ value }">
+ <status-icon :status="statusIcon(value)" />
+ {{ value }}
+ </template>
+ <template v-slot:cell(currentValue)="data">
+ {{ data.value }} {{ data.item.units }}
+ </template>
+ <template v-slot:cell(lowerCaution)="data">
+ {{ data.value }} {{ data.item.units }}
+ </template>
+ <template v-slot:cell(upperCaution)="data">
+ {{ data.value }} {{ data.item.units }}
+ </template>
+ <template v-slot:cell(lowerCritical)="data">
+ {{ data.value }} {{ data.item.units }}
+ </template>
+ <template v-slot:cell(upperCritical)="data">
+ {{ data.value }} {{ data.item.units }}
+ </template>
+ </b-table>
+ </b-col>
+ </b-row>
+ </b-container>
+</template>
+
+<script>
+import PageTitle from '../../../components/Global/PageTitle';
+import StatusIcon from '../../../components/Global/StatusIcon';
+
+const valueFormatter = value => {
+ if (value === null || value === undefined) {
+ return '--';
+ }
+ return parseFloat(value.toFixed(3));
+};
+
+export default {
+ name: 'Sensors',
+ components: { PageTitle, StatusIcon },
+ data() {
+ return {
+ fields: [
+ {
+ key: 'name',
+ sortable: true,
+ label: this.$t('pageSensors.table.name')
+ },
+ {
+ key: 'status',
+ sortable: true,
+ label: this.$t('pageSensors.table.status')
+ },
+ {
+ key: 'lowerCritical',
+ formatter: valueFormatter,
+ label: this.$t('pageSensors.table.lowerCritical')
+ },
+ {
+ key: 'lowerCaution',
+ formatter: valueFormatter,
+ label: this.$t('pageSensors.table.lowerWarning')
+ },
+
+ {
+ key: 'currentValue',
+ formatter: valueFormatter,
+ label: this.$t('pageSensors.table.currentValue')
+ },
+ {
+ key: 'upperCaution',
+ formatter: valueFormatter,
+ label: this.$t('pageSensors.table.upperWarning')
+ },
+ {
+ key: 'upperCritical',
+ formatter: valueFormatter,
+ label: this.$t('pageSensors.table.upperCritical')
+ }
+ ]
+ };
+ },
+ computed: {
+ allSensors() {
+ return this.$store.getters['sensors/sensors'];
+ }
+ },
+ created() {
+ this.$store.dispatch('sensors/getAllSensors');
+ },
+ methods: {
+ statusIcon(status) {
+ switch (status) {
+ case 'OK':
+ return 'success';
+ case 'Warning':
+ return 'warning';
+ case 'Critical':
+ return 'danger';
+ default:
+ return '';
+ }
+ },
+ sortCompare(a, b, key) {
+ if (key === 'status') {
+ const status = ['OK', 'Warning', 'Critical'];
+ return status.indexOf(a.status) - status.indexOf(b.status);
+ }
+ }
+ }
+};
+</script>
diff --git a/src/views/Health/Sensors/index.js b/src/views/Health/Sensors/index.js
new file mode 100644
index 00000000..fc71b611
--- /dev/null
+++ b/src/views/Health/Sensors/index.js
@@ -0,0 +1,2 @@
+import Sensors from './Sensors.vue';
+export default Sensors;