From dc04feb5596a85619e98d2d594b065e92c8b8fa4 Mon Sep 17 00:00:00 2001 From: Yoshie Muranaka Date: Wed, 4 Dec 2019 08:41:22 -0800 Subject: Add host status plugin - Create WebSocket and get host state changes from server - Changed webpack devServer to https to allow for secure WebSocket creation (wss) - Updates to AppHeader to visually indicate changes in host state - Cleaned up api.js file - Check if user is logged in when creating WebSocket - Adds check if user is already authenticated so WebSocket is created when browser refreshed. - Add appliation header styles - Add sass loader config changes to allow sass variables to be used in single file components URL must use https protocol when running locally or the page will not load. Signed-off-by: Yoshie Muranaka Signed-off-by: Derick Montague Change-Id: I35e89bdc09e1aa35a6215ef952409a8ed16dd9e1 --- src/assets/styles/_colors.scss | 2 +- src/components/AppHeader/AppHeader.vue | 89 ++++++++++++++-------------------- src/components/Global/StatusIcon.vue | 38 +++++++++++++++ src/store/api.js | 7 +-- src/store/index.js | 5 +- src/store/modules/GlobalStore.js | 35 ++++++++++++- src/store/plugins/WebSocketPlugin.js | 46 ++++++++++++++++++ vue.config.js | 13 +++++ 8 files changed, 174 insertions(+), 61 deletions(-) create mode 100644 src/components/Global/StatusIcon.vue create mode 100644 src/store/plugins/WebSocketPlugin.js diff --git a/src/assets/styles/_colors.scss b/src/assets/styles/_colors.scss index 04351231..4a8b62c9 100644 --- a/src/assets/styles/_colors.scss +++ b/src/assets/styles/_colors.scss @@ -91,7 +91,7 @@ $info: $teal; $warning: $yellow; $danger: $red; $light: $gray-100; -$dark: $gray-800; +$dark: $black; // Bootstrap will generate CSS variables for // all of the colors in this map. diff --git a/src/components/AppHeader/AppHeader.vue b/src/components/AppHeader/AppHeader.vue index 7974f70a..244eeb32 100644 --- a/src/components/AppHeader/AppHeader.vue +++ b/src/components/AppHeader/AppHeader.vue @@ -1,49 +1,32 @@ diff --git a/src/components/Global/StatusIcon.vue b/src/components/Global/StatusIcon.vue new file mode 100644 index 00000000..bb208409 --- /dev/null +++ b/src/components/Global/StatusIcon.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/store/api.js b/src/store/api.js index da6f3982..463e0d86 100644 --- a/src/store/api.js +++ b/src/store/api.js @@ -4,10 +4,6 @@ const api = Axios.create({ withCredentials: true }); -// TODO: Permanent authentication solution -// Using defaults to set auth for sending -// auth object in header - export default { get(path) { return api.get(path); @@ -26,6 +22,5 @@ export default { }, all(promises) { return Axios.all(promises); - }, - defaults: api.defaults + } }; diff --git a/src/store/index.js b/src/store/index.js index 4ef1c9d0..889e52b4 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -5,6 +5,8 @@ import GlobalStore from './modules/GlobalStore'; import AuthenticationStore from './modules/Authentication/AuthenticanStore'; import LocalUserManagementStore from './modules/AccessControl/LocalUserMangementStore'; +import WebSocketPlugin from './plugins/WebSocketPlugin'; + Vue.use(Vuex); export default new Vuex.Store({ @@ -15,5 +17,6 @@ export default new Vuex.Store({ global: GlobalStore, authentication: AuthenticationStore, localUsers: LocalUserManagementStore - } + }, + plugins: [WebSocketPlugin] }); diff --git a/src/store/modules/GlobalStore.js b/src/store/modules/GlobalStore.js index 8cf2e8eb..80d9c1a3 100644 --- a/src/store/modules/GlobalStore.js +++ b/src/store/modules/GlobalStore.js @@ -1,10 +1,31 @@ import api from '../api'; +const HOST_STATE = { + on: 'xyz.openbmc_project.State.Host.HostState.Running', + off: 'xyz.openbmc_project.State.Host.HostState.Off', + error: 'xyz.openbmc_project.State.Host.HostState.Quiesced', + diagnosticMode: 'xyz.openbmc_project.State.Host.HostState.DiagnosticMode' +}; + +const hostStateMapper = hostState => { + switch (hostState) { + case HOST_STATE.on: + return 'on'; + case HOST_STATE.off: + return 'off'; + case HOST_STATE.error: + return 'error'; + // TODO: Add mapping for DiagnosticMode + default: + return 'unreachable'; + } +}; + const GlobalStore = { namespaced: true, state: { hostName: '--', - hostStatus: null + hostStatus: 'unreachable' }, getters: { hostName(state) { @@ -17,6 +38,9 @@ const GlobalStore = { mutations: { setHostName(state, hostName) { state.hostName = hostName; + }, + setHostStatus(state, hostState) { + state.hostStatus = hostStateMapper(hostState); } }, actions: { @@ -28,6 +52,15 @@ const GlobalStore = { commit('setHostName', hostName); }) .catch(error => console.log(error)); + }, + getHostStatus({ commit }) { + api + .get('/xyz/openbmc_project/state/host0/attr/CurrentHostState') + .then(response => { + const hostState = response.data.data; + commit('setHostStatus', hostState); + }) + .catch(error => console.log(error)); } } }; diff --git a/src/store/plugins/WebSocketPlugin.js b/src/store/plugins/WebSocketPlugin.js new file mode 100644 index 00000000..3e2139dd --- /dev/null +++ b/src/store/plugins/WebSocketPlugin.js @@ -0,0 +1,46 @@ +/** + * 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. + * + * 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'] + }; + + const initWebSocket = () => { + ws = new WebSocket(`wss://${window.location.host}/subscribe`); + ws.onopen = () => { + ws.send(JSON.stringify(data)); + }; + ws.onerror = event => { + console.error(event); + }; + ws.onmessage = event => { + const { + properties: { CurrentHostState, RequestedHostTransition } = {} + } = JSON.parse(event.data); + const hostState = CurrentHostState || RequestedHostTransition; + store.commit('global/setHostStatus', hostState); + }; + }; + + store.subscribe(({ type }) => { + if (type === 'authentication/authSuccess') { + initWebSocket(); + } + if (type === 'authentication/logout') { + if (ws) ws.close(); + } + }); + + if (store.getters['authentication/isLoggedIn']) initWebSocket(); +}; + +export default WebSocketPlugin; diff --git a/vue.config.js b/vue.config.js index 9e1e1e1a..429b273e 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,11 +1,24 @@ const CompressionPlugin = require('compression-webpack-plugin'); module.exports = { + css: { + loaderOptions: { + scss: { + prependData: ` + @import "@/assets/styles/_obmc-custom.scss"; + ` + } + } + }, devServer: { + https: true, proxy: { '/': { target: process.env.BASE_URL, onProxyRes: proxyRes => { + // This header is igorned in the browser so removing + // it so we don't see warnings in the browser console + delete proxyRes.headers['strict-transport-security']; if (proxyRes.headers['set-cookie']) { // Need to remove 'Secure' flag on set-cookie value so browser // can create cookie for local development -- cgit v1.2.3