diff options
author | Yoshie Muranaka <yoshiemuranaka@gmail.com> | 2020-07-15 20:30:31 +0300 |
---|---|---|
committer | Yoshie Muranaka <yoshiemuranaka@gmail.com> | 2020-08-13 19:32:03 +0300 |
commit | 92a0a4ac1f25271c1861d7c97a0d8344dd578905 (patch) | |
tree | d028a659a04223c6659a201044cee1f490bbf0ca /src | |
parent | e9fc6125da79176d32aa0d0b89e36aff67347fc8 (diff) | |
download | webui-vue-92a0a4ac1f25271c1861d7c97a0d8344dd578905.tar.xz |
Add Firmware page
Adds ability to upload a fimware image by local workstation
or TFTP. Also adds ability to reboot BMC from the backup image.
- Add route definition, component view, and store for
Firmware page
- Get ActiveSoftwareImage location at /redfish/v1/Managers/bmc
- Get backup by checking for an image id that is not the same as
the active image /redfish/v1/UpdateService/FirmwareInventory
- Switch running firmware image by making PATCH request to
/redfish/v1/Managers/bmc
Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I04450e5a170d374122908c4c0349ba3b6e93ed2c
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/styles/bmc/custom/_card.scss | 5 | ||||
-rw-r--r-- | src/assets/styles/bmc/custom/_index.scss | 1 | ||||
-rw-r--r-- | src/components/AppNavigation/AppNavigation.vue | 2 | ||||
-rw-r--r-- | src/locales/en-US.json | 63 | ||||
-rw-r--r-- | src/main.js | 2 | ||||
-rw-r--r-- | src/router/index.js | 8 | ||||
-rw-r--r-- | src/store/modules/Configuration/FirmwareStore.js | 149 | ||||
-rw-r--r-- | src/views/Configuration/Firmware/Firmware.vue | 401 | ||||
-rw-r--r-- | src/views/Configuration/Firmware/FirmwareModalRebootBackup.vue | 33 | ||||
-rw-r--r-- | src/views/Configuration/Firmware/FirmwareModalUpload.vue | 18 | ||||
-rw-r--r-- | src/views/Configuration/Firmware/index.js | 2 | ||||
-rw-r--r-- | src/views/Overview/Overview.vue | 4 |
12 files changed, 676 insertions, 12 deletions
diff --git a/src/assets/styles/bmc/custom/_card.scss b/src/assets/styles/bmc/custom/_card.scss new file mode 100644 index 00000000..12721890 --- /dev/null +++ b/src/assets/styles/bmc/custom/_card.scss @@ -0,0 +1,5 @@ +.card { + .bg-success { + background-color: $success-light !important; + } +}
\ No newline at end of file diff --git a/src/assets/styles/bmc/custom/_index.scss b/src/assets/styles/bmc/custom/_index.scss index 0c393c57..b67712bb 100644 --- a/src/assets/styles/bmc/custom/_index.scss +++ b/src/assets/styles/bmc/custom/_index.scss @@ -6,6 +6,7 @@ @import "./bootstrap-grid"; @import "./buttons"; @import "./calendar"; +@import "./card"; @import "./dropdown"; @import "./forms"; @import "./modal"; diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue index 51b586cd..5101d82a 100644 --- a/src/components/AppNavigation/AppNavigation.vue +++ b/src/components/AppNavigation/AppNavigation.vue @@ -105,7 +105,7 @@ {{ $t('appNavigation.dateTimeSettings') }} </b-nav-item> <b-nav-item - href="javascript:void(0)" + to="/configuration/firmware" data-test-id="nav-container-firmware" > {{ $t('appNavigation.firmware') }} diff --git a/src/locales/en-US.json b/src/locales/en-US.json index fa00c86e..5ad6b78b 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -173,6 +173,69 @@ "successDelete": "Successfully deleted %{count} log. | Successfully deleted %{count} logs." } }, + "pageFirmware": { + "backup": "Backup:", + "backupImage": "Backup image", + "bmcStatus": "BMC status", + "changeAndRebootBmc": "Change image and reboot BMC", + "changeToBackupImage": "Change to backup image", + "current": "Current:", + "firmwareOnSystem": "Firmware on system", + "hostStatus": "Host status", + "pageDescription": "Update firmware by uploading a system image file from your workstation or TFTP server", + "running": "Running", + "state": "State", + "updateCode": "Update code", + "alert": { + "operationInProgress": "Server power operation in progress.", + "serverShutdownRequiredBeforeUpdate": "Server shutdown required before update", + "serverShutdownRequiredInfo": "Shutdown will be orderly - OS will shutdown before the server shuts down.", + "shutDownServer": "Shut down server", + "updateProcess": "Update process", + "updateProcessInfo": "The new image will be uploaded and activated. After that, the BMC will reboot automatically to run from the new image." + }, + "form": { + "imageFile": "Image file", + "imageFileName": "Image file name", + "onlyTarFilesAccepted": "Only .tar files accepted", + "tftpServer": "TFTP server", + "tftpServerIpAddress": "TFTP server IP address", + "uploadAndRebootBmc": "Upload and reboot BMC", + "uploadLocation": "Upload location", + "workstation": "Workstation" + }, + "modal": { + "connectionToBmcWillBeLost": "Connection to BMC will be lost", + "serverShutdownMessage": "There will be a server outage until the server is powered back on. Are you sure you want to shut down?", + "serverShutdownWillCauseOutage": "Server shutdown will cause outage", + "shutDownServer": "Shut down server", + "rebootFromBackup": { + "message1": "A BMC reboot is required before the system can run the backup image %{backup}. The reboot will cause a disconnection, and may require logging in again.", + "message2": "The current firmware image %{current} will be moved to backup. During the reboot, server cannot be powered back on.", + "message3": "Are you sure you want to reboot the BMC from backup image %{backup}?", + "primaryAction": "Reboot BMC from backup image", + "title": "@:pageFirmware.modal.connectionToBmcWillBeLost" + }, + "uploadAndReboot": { + "message1": "A BMC reboot is required before the system can run the new firmware image. The reboot will cause a disconnection, and may require logging in again.", + "message2": "During the reboot, the server cannot be powered back on. The backup image will be permanently deleted.", + "message3": "Are you sure you want to upload the new firmware image and reboot the BMC?", + "primaryAction": "Upload and reboot BMC", + "title": "@:pageFirmware.modal.connectionToBmcWillBeLost" + } + }, + "toast": { + "errorRebootFromBackup": "Error rebooting from backup image.", + "errorUploadAndReboot": "Error uploading image.", + "infoRefreshApplicationMessage": "Refresh the application to confirm the code update has completed and was successful.", + "infoRefreshApplicationTitle": "Verify code update", + "infoUploadStartTimeMessage": "Start time: %{startTime}", + "infoUploadStartTimeTitle": "Upload started", + "successRebootFromBackup": "Successfully started reboot from backup image.", + "successUploadMessage": "The upload was successful. During code update, the BMC will be not be responsive. Wait for the code update notification before making any changes.", + "successUploadTitle": "Code update started" + } + }, "pageHardwareStatus": { "dimmSlot": "DIMM slot", "fans": "Fans", diff --git a/src/main.js b/src/main.js index 8336cb3d..497c751c 100644 --- a/src/main.js +++ b/src/main.js @@ -7,6 +7,7 @@ import { BadgePlugin, ButtonPlugin, BVConfigPlugin, + CardPlugin, CollapsePlugin, DropdownPlugin, FormPlugin, @@ -94,6 +95,7 @@ Vue.use(BVConfigPlugin, { variant: 'primary' } }); +Vue.use(CardPlugin); Vue.use(CollapsePlugin); Vue.use(DropdownPlugin); Vue.use(FormPlugin); diff --git a/src/router/index.js b/src/router/index.js index eace2bb6..f3b8d8a3 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -90,6 +90,14 @@ const routes = [ } }, { + path: '/configuration/firmware', + name: 'firmware', + component: () => import('@/views/Configuration/Firmware'), + meta: { + title: 'appPageTitle.firmware' + } + }, + { path: '/control/kvm', name: 'kvm', component: () => import('@/views/Control/Kvm'), diff --git a/src/store/modules/Configuration/FirmwareStore.js b/src/store/modules/Configuration/FirmwareStore.js index 5ec9173a..ead51994 100644 --- a/src/store/modules/Configuration/FirmwareStore.js +++ b/src/store/modules/Configuration/FirmwareStore.js @@ -1,27 +1,158 @@ -import api from '../../api'; +import api from '@/store/api'; +import i18n from '@/i18n'; const FirmwareStore = { namespaced: true, state: { - bmcFirmwareVersion: '--' + activeFirmware: { + version: '--', + id: null, + location: null + }, + backupFirmware: { + version: '--', + id: null, + location: null, + status: '--' + }, + applyTime: null }, getters: { - bmcFirmwareVersion: state => state.bmcFirmwareVersion + systemFirmwareVersion: state => state.activeFirmware.version, + backupFirmwareVersion: state => state.backupFirmware.version, + backupFirmwareStatus: state => state.backupFirmware.status, + isRebootFromBackupAvailable: state => + state.backupFirmware.id ? true : false }, mutations: { - setBmcFirmwareVersion: (state, bmcFirmwareVersion) => - (state.bmcFirmwareVersion = bmcFirmwareVersion) + setActiveFirmware: (state, { version, id, location }) => { + state.activeFirmware.version = version; + state.activeFirmware.id = id; + state.activeFirmware.location = location; + }, + setBackupFirmware: (state, { version, id, location, status }) => { + state.backupFirmware.version = version; + state.backupFirmware.id = id; + state.backupFirmware.location = location; + state.backupFirmware.status = status; + }, + setApplyTime: (state, applyTime) => (state.applyTime = applyTime) }, actions: { - async getBmcFirmware({ commit }) { + async getSystemFirwareVersion({ commit, state }) { return await api .get('/redfish/v1/Managers/bmc') - .then(response => { - const bmcFirmwareVersion = response.data.FirmwareVersion; - commit('setBmcFirmwareVersion', bmcFirmwareVersion); + .then(({ data: { Links: { ActiveSoftwareImage } } }) => { + const location = ActiveSoftwareImage['@odata.id']; + return api.get(location); }) + .then(({ data }) => { + const version = data.Version; + const id = data.Id; + const location = data['@odata.id']; + commit('setActiveFirmware', { version, id, location }); + // TODO: temporary workaround to get 'Backup' Firmware + // information + return api.get('/redfish/v1/UpdateService/FirmwareInventory'); + }) + .then(({ data: { Members } }) => { + // TODO: temporary workaround to get 'Backup' Firmware + // information + // Check FirmwareInventory list for not ActiveSoftwareImage id + const backupLocation = Members.map(item => item['@odata.id']).find( + location => { + const id = location.split('/').pop(); + return id !== state.activeFirmware.id; + } + ); + if (backupLocation) { + return api.get(backupLocation); + } + }) + .then(({ data } = {}) => { + if (!data) return; + const version = data.Version; + const id = data.Id; + const location = data['@odata.id']; + const status = data.Status ? data.Status.State : '--'; + commit('setBackupFirmware', { version, id, location, status }); + }) + .catch(error => console.log(error)); + }, + getUpdateServiceApplyTime({ commit }) { + api + .get('/redfish/v1/UpdateService') + .then(({ data }) => { + const applyTime = + data.HttpPushUriOptions.HttpPushUriApplyTime.ApplyTime; + commit('setApplyTime', applyTime); + }) + .catch(error => console.log(error)); + }, + setApplyTimeImmediate({ commit }) { + const data = { + HttpPushUriOptions: { + HttpPushUriApplyTime: { + ApplyTime: 'Immediate' + } + } + }; + return api + .patch('/redfish/v1/UpdateService', data) + .then(() => commit('setApplyTime', 'Immediate')) + .catch(error => console.log(error)); + }, + async uploadFirmware({ state, dispatch }, image) { + if (state.applyTime !== 'Immediate') { + // ApplyTime must be set to Immediate before making + // request to update firmware + await dispatch('setApplyTimeImmediate'); + } + return await api + .post('/redfish/v1/UpdateService', image, { + headers: { 'Content-Type': 'application/octet-stream' } + }) + .then(() => dispatch('getSystemFirwareVersion')) + .then(() => i18n.t('pageFirmware.toast.successUploadMessage')) + .catch(error => { + console.log(error); + throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot')); + }); + }, + async uploadFirmwareTFTP({ state, dispatch }, { address, filename }) { + const data = { + TransferProtocol: 'TFTP', + ImageURI: `${address}/${filename}` + }; + if (state.applyTime !== 'Immediate') { + // ApplyTime must be set to Immediate before making + // request to update firmware + await dispatch('setApplyTimeImmediate'); + } + return await api + .post('/redfish/v1/UpdateService', data) + .then(() => dispatch('getSystemFirwareVersion')) + .then(() => i18n.t('pageFirmware.toast.successUploadMessage')) + .catch(error => { + console.log(error); + throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot')); + }); + }, + async switchFirmwareAndReboot({ state }) { + const backupLoaction = state.backupFirmware.location; + const data = { + Links: { + ActiveSoftwareImage: { + '@odata.id': backupLoaction + } + } + }; + return await api + .patch('/redfish/v1/Managers/bmc', data) + .then(() => i18n.t('pageFirmware.toast.successRebootFromBackup')) .catch(error => { console.log(error); + throw new Error(i18n.t('pageFirmware.toast.errorRebootFromBackup')); }); } } diff --git a/src/views/Configuration/Firmware/Firmware.vue b/src/views/Configuration/Firmware/Firmware.vue new file mode 100644 index 00000000..248b0ab2 --- /dev/null +++ b/src/views/Configuration/Firmware/Firmware.vue @@ -0,0 +1,401 @@ +<template> + <b-container fluid="xl"> + <page-title :description="$t('pageFirmware.pageDescription')" /> + <!-- Operation in progress alert --> + <alert v-if="isOperationInProgress" variant="info" class="mb-5"> + <p> + {{ $t('pageFirmware.alert.operationInProgress') }} + </p> + </alert> + <!-- Shutdown server warning alert --> + <alert v-else-if="!isHostOff" variant="warning" class="mb-5"> + <p class="font-weight-bold mb-1"> + {{ $t('pageFirmware.alert.serverShutdownRequiredBeforeUpdate') }} + </p> + {{ $t('pageFirmware.alert.serverShutdownRequiredInfo') }} + <template v-slot:action> + <b-btn variant="link" class="text-nowrap" @click="onClickShutDown"> + {{ $t('pageFirmware.alert.shutDownServer') }} + </b-btn> + </template> + </alert> + <b-row class="mb-4"> + <!-- Firmware on system --> + <b-col md="10" lg="12" xl="8" class="pr-xl-4"> + <page-section :section-title="$t('pageFirmware.firmwareOnSystem')"> + <b-card-group deck> + <!-- Current FW --> + <b-card header-bg-variant="success"> + <template v-slot:header> + <dl class="mb-0"> + <dt>{{ $t('pageFirmware.current') }}</dt> + <dd class="mb-0">{{ systemFirmwareVersion }}</dd> + </dl> + </template> + <b-row> + <b-col xs="6"> + <dl class="my-0"> + <dt>{{ $t('pageFirmware.bmcStatus') }}</dt> + <dd>{{ $t('pageFirmware.running') }}</dd> + </dl> + </b-col> + <b-col xs="6"> + <dl class="my-0"> + <dt>{{ $t('pageFirmware.hostStatus') }}</dt> + <dd v-if="hostStatus === 'on'"> + {{ $t('global.status.on') }} + </dd> + <dd v-else-if="hostStatus === 'off'"> + {{ $t('global.status.off') }} + </dd> + <dd v-else> + {{ $t('global.status.notAvailable') }} + </dd> + </dl> + </b-col> + </b-row> + </b-card> + + <!-- Backup FW --> + <b-card> + <template v-slot:header> + <dl class="mb-0"> + <dt>{{ $t('pageFirmware.backup') }}</dt> + <dd class="mb-0">{{ backupFirmwareVersion }}</dd> + </dl> + </template> + <b-row> + <b-col xs="6"> + <dl class="my-0"> + <dt>{{ $t('pageFirmware.state') }}</dt> + <dd>{{ backupFirmwareStatus }}</dd> + </dl> + </b-col> + </b-row> + </b-card> + </b-card-group> + </page-section> + + <!-- Change to backup image --> + <page-section :section-title="$t('pageFirmware.changeToBackupImage')"> + <dl class="mb-5"> + <dt> + {{ $t('pageFirmware.backupImage') }} + </dt> + <dd>{{ backupFirmwareVersion }}</dd> + </dl> + <b-btn + v-b-modal.modal-reboot-backup + type="button" + variant="primary" + :disabled="isPageDisabled || !isRebootFromBackupAvailable" + > + {{ $t('pageFirmware.changeAndRebootBmc') }} + </b-btn> + </page-section> + </b-col> + + <!-- Update code --> + <b-col sm="8" xl="4" class="update-code pl-xl-4"> + <page-section :section-title="$t('pageFirmware.updateCode')"> + <b-form @submit.prevent="onSubmitUpload"> + <b-form-group + :label="$t('pageFirmware.form.uploadLocation')" + :disabled="isPageDisabled" + > + <b-form-radio v-model="isWorkstationSelected" :value="true"> + {{ $t('pageFirmware.form.workstation') }} + </b-form-radio> + <b-form-radio v-model="isWorkstationSelected" :value="false"> + {{ $t('pageFirmware.form.tftpServer') }} + </b-form-radio> + </b-form-group> + + <!-- Workstation Upload --> + <template v-if="isWorkstationSelected"> + <b-form-group + :label="$t('pageFirmware.form.imageFile')" + label-for="image-file" + > + <b-form-text id="image-file-help-block"> + {{ $t('pageFirmware.form.onlyTarFilesAccepted') }} + </b-form-text> + <b-form-file + id="image-file" + v-model="file" + accept=".tar" + aria-describedby="image-file-help-block" + :disabled="isPageDisabled" + :state="getValidationState($v.file)" + @input="$v.file.$touch()" + /> + <b-form-invalid-feedback role="alert"> + {{ $t('global.form.required') }} + </b-form-invalid-feedback> + </b-form-group> + </template> + + <!-- TFTP Server Upload --> + <template v-else> + <b-form-group + :label="$t('pageFirmware.form.tftpServerIpAddress')" + label-for="tftp-ip" + > + <b-form-input + id="tftp-id" + v-model="tftpIpAddress" + type="text" + :browse-text="$t('global.fileUpload.browseText')" + :drop-placeholder="$t('global.fileUpload.dropPlaceholder')" + :placeholder="$t('global.fileUpload.placeholder')" + :state="getValidationState($v.tftpIpAddress)" + :disabled="isPageDisabled" + @input="$v.tftpIpAddress.$touch()" + /> + <b-form-invalid-feedback role="alert"> + {{ $t('global.form.fieldRequired') }} + </b-form-invalid-feedback> + </b-form-group> + <b-form-group + :label="$t('pageFirmware.form.imageFileName')" + label-for="tftp-file-name" + > + <b-form-input + id="tftp-file-name" + v-model="tftpFileName" + type="text" + :state="getValidationState($v.tftpFileName)" + :disabled="isPageDisabled" + @input="$v.tftpFileName.$touch()" + /> + <b-form-invalid-feedback role="alert"> + {{ $t('global.form.fieldRequired') }} + </b-form-invalid-feedback> + </b-form-group> + </template> + + <!-- Info alert --> + <alert variant="info" class="mt-4 mb-5"> + <p class="font-weight-bold mb-1"> + {{ $t('pageFirmware.alert.updateProcess') }} + </p> + <p>{{ $t('pageFirmware.alert.updateProcessInfo') }}</p> + </alert> + <b-form-group> + <b-btn type="submit" variant="primary" :disabled="isPageDisabled"> + {{ $t('pageFirmware.form.uploadAndRebootBmc') }} + </b-btn> + </b-form-group> + </b-form> + </page-section> + </b-col> + </b-row> + + <!-- Modals --> + <modal-upload @ok="uploadFirmware" /> + <modal-reboot-backup + :current="currentFirmwareVersion" + :backup="backupFirmwareVersion" + @ok="rebootFromBackup" + /> + </b-container> +</template> + +<script> +import { requiredIf } from 'vuelidate/lib/validators'; +import { mapGetters } from 'vuex'; + +import PageSection from '@/components/Global/PageSection'; +import PageTitle from '@/components/Global/PageTitle'; +import Alert from '@/components/Global/Alert'; +import ModalUpload from './FirmwareModalUpload'; +import ModalRebootBackup from './FirmwareModalRebootBackup'; + +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; + +export default { + name: 'Firmware', + components: { + Alert, + ModalRebootBackup, + ModalUpload, + PageSection, + PageTitle + }, + mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin], + data() { + return { + isWorkstationSelected: true, + file: null, + tftpIpAddress: null, + tftpFileName: null, + timeoutId: null + }; + }, + computed: { + hostStatus() { + return this.$store.getters['global/hostStatus']; + }, + isHostOff() { + return this.hostStatus === 'off' ? true : false; + }, + isOperationInProgress() { + return this.$store.getters['controls/isOperationInProgress']; + }, + ...mapGetters('firmware', [ + 'backupFirmwareStatus', + 'backupFirmwareVersion', + 'isRebootFromBackupAvailable', + 'systemFirmwareVersion' + ]), + isPageDisabled() { + return !this.isHostOff || this.loading || this.isOperationInProgress; + } + }, + watch: { + isWorkstationSelected: function() { + this.$v.$reset(); + this.file = null; + this.tftpIpAddress = null; + this.tftpFileName = null; + } + }, + created() { + this.startLoader(); + this.$store.dispatch('firmware/getUpdateServiceApplyTime'); + Promise.all([ + this.$store.dispatch('global/getHostStatus'), + this.$store.dispatch('firmware/getSystemFirwareVersion') + ]).finally(() => this.endLoader()); + }, + beforeRouteLeave(to, from, next) { + this.hideLoader(); + this.clearRebootTimeout(); + next(); + }, + validations() { + return { + file: { + required: requiredIf(function() { + return this.isWorkstationSelected; + }) + }, + tftpIpAddress: { + required: requiredIf(function() { + return !this.isWorkstationSelected; + }) + }, + tftpFileName: { + required: requiredIf(function() { + return !this.isWorkstationSelected; + }) + } + }; + }, + methods: { + uploadFirmware() { + const startTime = this.$options.filters.formatTime(new Date()); + this.setRebootTimeout(360000); //6 minute timeout + this.infoToast( + this.$t('pageFirmware.toast.infoUploadStartTimeMessage', { startTime }), + this.$t('pageFirmware.toast.infoUploadStartTimeTitle') + ); + if (this.isWorkstationSelected) { + this.dispatchWorkstationUpload(); + } else { + this.dispatchTftpUpload(); + } + }, + dispatchWorkstationUpload() { + this.$store + .dispatch('firmware/uploadFirmware', this.file) + .then(success => + this.infoToast( + success, + this.$t('pageFirmware.toast.successUploadTitle') + ) + ) + .catch(({ message }) => { + this.errorToast(message); + this.clearRebootTimeout(); + }); + }, + dispatchTftpUpload() { + const data = { + address: this.tftpIpAddress, + filename: this.tftpFileName + }; + this.$store + .dispatch('firmware/uploadFirmwareTFTP', data) + .then(success => + this.infoToast( + success, + this.$t('pageFirmware.toast.successUploadTitle') + ) + ) + .catch(({ message }) => { + this.errorToast(message); + this.clearRebootTimeout(); + }); + }, + rebootFromBackup() { + this.setRebootTimeout(); + this.$store + .dispatch('firmware/switchFirmwareAndReboot') + .then(success => + this.infoToast(success, this.$t('global.status.success')) + ) + .catch(({ message }) => { + this.errorToast(message); + this.clearRebootTimeout(); + }); + }, + setRebootTimeout(timeoutMs = 60000) { + // Set a timeout to disable page interactions while + // an upload or BMC reboot is in progress + this.startLoader(); + this.timeoutId = setTimeout(() => { + this.endLoader(); + this.infoToast( + this.$t('pageFirmware.toast.infoRefreshApplicationMessage'), + this.$t('pageFirmware.toast.infoRefreshApplicationTitle') + ); + }, timeoutMs); + }, + clearRebootTimeout() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.endLoader(); + } + }, + onSubmitUpload() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.$bvModal.show('modal-upload'); + }, + onClickShutDown() { + this.$bvModal + .msgBoxConfirm(this.$t('pageFirmware.modal.serverShutdownMessage'), { + title: this.$t('pageFirmware.modal.serverShutdownWillCauseOutage'), + okTitle: this.$t('pageFirmware.modal.shutDownServer'), + okVariant: 'danger' + }) + .then(shutdownConfirmed => { + if (shutdownConfirmed) + this.$store.dispatch('controls/hostSoftPowerOff'); + }); + } + } +}; +</script> + +<style lang="scss" scoped> +.update-code { + border-left: none; + @include media-breakpoint-up(xl) { + border-left: 1px solid gray('300'); + } +} +</style> diff --git a/src/views/Configuration/Firmware/FirmwareModalRebootBackup.vue b/src/views/Configuration/Firmware/FirmwareModalRebootBackup.vue new file mode 100644 index 00000000..a8fb3ad5 --- /dev/null +++ b/src/views/Configuration/Firmware/FirmwareModalRebootBackup.vue @@ -0,0 +1,33 @@ +<template> + <b-modal + id="modal-reboot-backup" + :ok-title="$t('pageFirmware.modal.rebootFromBackup.primaryAction')" + :title="$t('pageFirmware.modal.rebootFromBackup.title')" + @ok="$emit('ok')" + > + <p> + {{ $t('pageFirmware.modal.rebootFromBackup.message1', { backup }) }} + </p> + <p> + {{ $t('pageFirmware.modal.rebootFromBackup.message2', { current }) }} + </p> + <p class="font-weight-bold"> + {{ $t('pageFirmware.modal.rebootFromBackup.message3', { backup }) }} + </p> + </b-modal> +</template> + +<script> +export default { + props: { + current: { + type: String, + required: true + }, + backup: { + type: String, + required: true + } + } +}; +</script> diff --git a/src/views/Configuration/Firmware/FirmwareModalUpload.vue b/src/views/Configuration/Firmware/FirmwareModalUpload.vue new file mode 100644 index 00000000..d092becd --- /dev/null +++ b/src/views/Configuration/Firmware/FirmwareModalUpload.vue @@ -0,0 +1,18 @@ +<template> + <b-modal + id="modal-upload" + :title="$t('pageFirmware.modal.uploadAndReboot.title')" + :ok-title="$t('pageFirmware.modal.uploadAndReboot.primaryAction')" + @ok="$emit('ok')" + > + <p> + {{ $t('pageFirmware.modal.uploadAndReboot.message1') }} + </p> + <p> + {{ $t('pageFirmware.modal.uploadAndReboot.message2') }} + </p> + <p class="font-weight-bold"> + {{ $t('pageFirmware.modal.uploadAndReboot.message3') }} + </p> + </b-modal> +</template> diff --git a/src/views/Configuration/Firmware/index.js b/src/views/Configuration/Firmware/index.js new file mode 100644 index 00000000..ad15cc03 --- /dev/null +++ b/src/views/Configuration/Firmware/index.js @@ -0,0 +1,2 @@ +import Firmware from './Firmware.vue'; +export default Firmware; diff --git a/src/views/Overview/Overview.vue b/src/views/Overview/Overview.vue index 46944cc8..ac484816 100644 --- a/src/views/Overview/Overview.vue +++ b/src/views/Overview/Overview.vue @@ -106,7 +106,7 @@ export default { mixins: [LoadingBarMixin], computed: mapState({ server: state => state.system.systems[0], - bmcFirmwareVersion: state => state.firmware.bmcFirmwareVersion, + bmcFirmwareVersion: state => state.firmware.activeFirmware.version, powerCapValue: state => state.powerControl.powerCapValue, powerConsumptionValue: state => state.powerControl.powerConsumptionValue, serverManufacturer() { @@ -139,7 +139,7 @@ export default { }); Promise.all([ this.$store.dispatch('system/getSystem'), - this.$store.dispatch('firmware/getBmcFirmware'), + this.$store.dispatch('firmware/getSystemFirwareVersion'), this.$store.dispatch('powerControl/getPowerControl'), quicklinksPromise, networkPromise, |