diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/styles/_table.scss | 4 | ||||
-rw-r--r-- | src/components/AppNavigation/AppNavigation.vue | 2 | ||||
-rw-r--r-- | src/locales/en-US.json | 11 | ||||
-rw-r--r-- | src/router/index.js | 7 | ||||
-rw-r--r-- | src/store/index.js | 4 | ||||
-rw-r--r-- | src/store/modules/Health/SensorsStore.js | 113 | ||||
-rw-r--r-- | src/views/Health/Sensors/Sensors.vue | 126 | ||||
-rw-r--r-- | src/views/Health/Sensors/index.js | 2 |
8 files changed, 267 insertions, 2 deletions
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; |