| <template> |
| <b-container fluid="xl"> |
| <page-title :description="$t('pageFirmware.pageDescriptionSingleImage')" /> |
| <!-- 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 #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 #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 #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.updateProcessInfoSingleImage') }}</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/getFirmwareInformation'), |
| ]).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> |