diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue | 405 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.vue | 33 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.vue | 18 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/index.js | 2 | ||||
-rw-r--r-- | src/env/router/ibm.js | 234 | ||||
-rw-r--r-- | src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js | 161 | ||||
-rw-r--r-- | src/env/store/ibm.js | 6 | ||||
-rw-r--r-- | src/views/Overview/Overview.vue | 2 |
8 files changed, 845 insertions, 16 deletions
diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue new file mode 100644 index 00000000..f719631a --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue @@ -0,0 +1,405 @@ +<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" + :browse-text="$t('global.fileUpload.browseText')" + :drop-placeholder="$t('global.fileUpload.dropPlaceholder')" + :placeholder="$t('global.fileUpload.placeholder')" + :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.tftpServerAddress')" + label-for="tftp-ip" + > + <b-form-text id="server-address-help-block"> + {{ $t('pageFirmware.form.tftpServerAddressHelper') }} + </b-form-text> + <b-form-input + id="tftp-id" + v-model="tftpIpAddress" + type="text" + :state="getValidationState($v.tftpIpAddress)" + :disabled="isPageDisabled" + aria-describedby="server-address-help-block" + @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="systemFirmwareVersion" + :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 './FirmwareSingleImageModalUpload'; +import ModalRebootBackup from './FirmwareSingleImageModalRebootBackup'; + +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; + +export default { + name: 'FirmwareSingleImage', + 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('firmwareSingleImage', [ + '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('firmwareSingleImage/getUpdateServiceApplyTime'); + Promise.all([ + this.$store.dispatch('global/getHostStatus'), + this.$store.dispatch('firmwareSingleImage/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('firmwareSingleImage/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('firmwareSingleImage/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('firmwareSingleImage/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/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.vue new file mode 100644 index 00000000..a8fb3ad5 --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.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/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.vue new file mode 100644 index 00000000..d092becd --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.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/env/components/FirmwareSingleImage/index.js b/src/env/components/FirmwareSingleImage/index.js new file mode 100644 index 00000000..06f31f91 --- /dev/null +++ b/src/env/components/FirmwareSingleImage/index.js @@ -0,0 +1,2 @@ +import FirmwareSingleImage from './FirmwareSingleImage.vue'; +export default FirmwareSingleImage; diff --git a/src/env/router/ibm.js b/src/env/router/ibm.js index 213920db..bfba0f0e 100644 --- a/src/env/router/ibm.js +++ b/src/env/router/ibm.js @@ -1,15 +1,225 @@ -import { remove } from 'lodash'; -import routes from '@/router/routes'; +import AppLayout from '@/layouts/AppLayout.vue'; +import ChangePassword from '@/views/ChangePassword'; +import ConsoleLayout from '@/layouts/ConsoleLayout.vue'; +import DateTimeSettings from '@/views/Configuration/DateTimeSettings'; +import EventLogs from '@/views/Health/EventLogs'; +import HardwareStatus from '@/views/Health/HardwareStatus'; +import Ldap from '@/views/AccessControl/Ldap'; +import LocalUserManagement from '@/views/AccessControl/LocalUserManagement'; +import Login from '@/views/Login'; +import LoginLayout from '@/layouts/LoginLayout'; +import ManagePowerUsage from '@/views/Control/ManagePowerUsage'; +import NetworkSettings from '@/views/Configuration/NetworkSettings'; +import Overview from '@/views/Overview'; +import PageNotFound from '@/views/PageNotFound'; +import ProfileSettings from '@/views/ProfileSettings'; +import RebootBmc from '@/views/Control/RebootBmc'; +import Sensors from '@/views/Health/Sensors'; +import SerialOverLan from '@/views/Control/SerialOverLan'; +import SerialOverLanConsole from '@/views/Control/SerialOverLan/SerialOverLanConsole'; +import ServerLed from '@/views/Control/ServerLed'; +import ServerPowerOperations from '@/views/Control/ServerPowerOperations'; +import SslCertificates from '@/views/AccessControl/SslCertificates'; +import VirtualMedia from '@/views/Control/VirtualMedia'; +import i18n from '@/i18n'; -const customRoutes = routes.map(route => { - // Removes router definition that includes kvm - // in name property (main and console layouts) +// Custom components +import FirmwareSingleImage from '../components/FirmwareSingleImage'; - // TODO: will revisit this, removing the definition - // removes the route from the application but - // the component is still included in the final build - remove(route.children, ({ name }) => name.includes('kvm')); - return route; -}); +const routes = [ + { + path: '/login', + component: LoginLayout, + children: [ + { + path: '', + name: 'login', + component: Login, + meta: { + title: i18n.t('appPageTitle.login') + } + }, + { + path: '/change-password', + name: 'change-password', + component: ChangePassword, + meta: { + title: i18n.t('appPageTitle.changePassword'), + requiresAuth: true + } + } + ] + }, + { + path: '/console', + component: ConsoleLayout, + meta: { + requiresAuth: true + }, + children: [ + { + path: 'serial-over-lan-console', + name: 'serial-over-lan-console', + component: SerialOverLanConsole, + meta: { + title: i18n.t('appPageTitle.serialOverLan') + } + } + ] + }, + { + path: '/', + meta: { + requiresAuth: true + }, + component: AppLayout, + children: [ + { + path: '', + name: 'overview', + component: Overview, + meta: { + title: i18n.t('appPageTitle.overview') + } + }, + { + path: '/profile-settings', + name: 'profile-settings', + component: ProfileSettings, + meta: { + title: i18n.t('appPageTitle.profileSettings') + } + }, + { + path: '/health/event-logs', + name: 'event-logs', + component: EventLogs, + meta: { + title: i18n.t('appPageTitle.eventLogs') + } + }, + { + path: '/health/hardware-status', + name: 'hardware-status', + component: HardwareStatus, + meta: { + title: i18n.t('appPageTitle.hardwareStatus') + } + }, + { + path: '/health/sensors', + name: 'sensors', + component: Sensors, + meta: { + title: i18n.t('appPageTitle.sensors') + } + }, + { + path: '/access-control/ldap', + name: 'ldap', + component: Ldap, + meta: { + title: i18n.t('appPageTitle.ldap') + } + }, + { + path: '/access-control/local-user-management', + name: 'local-users', + component: LocalUserManagement, + meta: { + title: i18n.t('appPageTitle.localUserManagement') + } + }, + { + path: '/access-control/ssl-certificates', + name: 'ssl-certificates', + component: SslCertificates, + meta: { + title: i18n.t('appPageTitle.sslCertificates') + } + }, + { + path: '/configuration/date-time-settings', + name: 'date-time-settings', + component: DateTimeSettings, + meta: { + title: i18n.t('appPageTitle.dateTimeSettings') + } + }, + { + path: '/configuration/firmware', + name: 'firmware', + component: FirmwareSingleImage, + meta: { + title: i18n.t('appPageTitle.firmware') + } + }, + { + path: '/control/manage-power-usage', + name: 'manage-power-usage', + component: ManagePowerUsage, + meta: { + title: i18n.t('appPageTitle.managePowerUsage') + } + }, + { + path: '/configuration/network-settings', + name: 'network-settings', + component: NetworkSettings, + meta: { + title: i18n.t('appPageTitle.networkSettings') + } + }, + { + path: '/control/reboot-bmc', + name: 'reboot-bmc', + component: RebootBmc, + meta: { + title: i18n.t('appPageTitle.rebootBmc') + } + }, + { + path: '/control/server-led', + name: 'server-led', + component: ServerLed, + meta: { + title: i18n.t('appPageTitle.serverLed') + } + }, + { + path: '/control/serial-over-lan', + name: 'serial-over-lan', + component: SerialOverLan, + meta: { + title: i18n.t('appPageTitle.serialOverLan') + } + }, + { + path: '/control/server-power-operations', + name: 'server-power-operations', + component: ServerPowerOperations, + meta: { + title: i18n.t('appPageTitle.serverPowerOperations') + } + }, + { + path: '/control/virtual-media', + name: 'virtual-media', + component: VirtualMedia, + meta: { + title: i18n.t('appPageTitle.virtualMedia') + } + }, + { + path: '*', + name: 'page-not-found', + component: PageNotFound, + meta: { + title: i18n.t('appPageTitle.pageNotFound') + } + } + ] + } +]; -export default customRoutes; +export default routes; diff --git a/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js b/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js new file mode 100644 index 00000000..d00c5f71 --- /dev/null +++ b/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js @@ -0,0 +1,161 @@ +import api from '@/store/api'; +import i18n from '@/i18n'; + +const FirmwareSingleImageStore = { + namespaced: true, + state: { + activeFirmware: { + version: '--', + id: null, + location: null + }, + backupFirmware: { + version: '--', + id: null, + location: null, + status: '--' + }, + applyTime: null + }, + getters: { + systemFirmwareVersion: state => state.activeFirmware.version, + backupFirmwareVersion: state => state.backupFirmware.version, + backupFirmwareStatus: state => state.backupFirmware.status, + isRebootFromBackupAvailable: state => + state.backupFirmware.id ? true : false + }, + mutations: { + 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 getSystemFirwareVersion({ commit }) { + return await api + .get('/redfish/v1/Managers/bmc') + .then(({ data: { Links } }) => { + const currentLocation = Links.ActiveSoftwareImage['@odata.id']; + // Check SoftwareImages list for not ActiveSoftwareImage id + const backupLocation = Links.SoftwareImages.map( + item => item['@odata.id'] + ).find(location => { + const id = location.split('/').pop(); + const currentId = currentLocation.split('/').pop(); + return id !== currentId; + }); + return { currentLocation, backupLocation }; + }) + .then(async ({ currentLocation, backupLocation }) => { + const currentData = await api.get(currentLocation); + let backupData = {}; + + if (backupLocation) { + backupData = await api.get(backupLocation); + } + + commit('setActiveFirmware', { + version: currentData?.data?.Version, + id: currentData?.data?.Id, + location: currentData?.data?.['@odata.id'] + }); + commit('setBackupFirmware', { + version: backupData.data?.Version, + id: backupData.data?.Id, + location: backupData.data?.['@odata.id'], + status: backupData.data?.Status?.State + }); + }) + .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/Actions/UpdateService.SimpleUpdate', + 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')); + }); + } + } +}; + +export default FirmwareSingleImageStore; diff --git a/src/env/store/ibm.js b/src/env/store/ibm.js index 70c9b98a..12fd0865 100644 --- a/src/env/store/ibm.js +++ b/src/env/store/ibm.js @@ -1,7 +1,7 @@ import store from '@/store'; +import FirmwareSingleImageStore from './FirmwareSingleImage/FirmwareSingleImageStore'; -// Use store.registerModule() to register env specific -// store modules -// https://vuex.vuejs.org/api/#registermodule +store.unregisterModule('firmware'); +store.registerModule('firmwareSingleImage', FirmwareSingleImageStore); export default store; diff --git a/src/views/Overview/Overview.vue b/src/views/Overview/Overview.vue index 7385b0db..13998297 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.activeFirmware.version, + bmcFirmwareVersion: state => state.firmware?.activeFirmware.version, powerCapValue: state => state.powerControl.powerCapValue, powerConsumptionValue: state => state.powerControl.powerConsumptionValue, serverManufacturer() { |