| <template> |
| <b-container fluid="xl"> |
| <page-title /> |
| <b-row> |
| <b-col xl="10"> |
| <!-- Operation in progress alert --> |
| <alert v-if="isOperationInProgress" variant="info" class="mb-5"> |
| <p> |
| {{ $t('pageFirmware.singleFileUpload.alert.operationInProgress') }} |
| </p> |
| </alert> |
| <!-- Power off server warning alert --> |
| <alert v-else-if="!isHostOff" variant="warning" class="mb-5"> |
| <p class="mb-0"> |
| {{ |
| $t('pageFirmware.singleFileUpload.alert.serverMustBePoweredOffTo') |
| }} |
| </p> |
| <ul class="m-0"> |
| <li> |
| {{ |
| $t( |
| 'pageFirmware.singleFileUpload.alert.switchRunningAndBackupImages' |
| ) |
| }} |
| </li> |
| <li> |
| {{ $t('pageFirmware.singleFileUpload.alert.updateFirmware') }} |
| </li> |
| </ul> |
| <template #action> |
| <b-link to="/control/server-power-operations"> |
| {{ |
| $t( |
| 'pageFirmware.singleFileUpload.alert.viewServerPowerOperations' |
| ) |
| }} |
| </b-link> |
| </template> |
| </alert> |
| </b-col> |
| </b-row> |
| <b-row> |
| <b-col xl="10"> |
| <page-section> |
| <b-card-group deck> |
| <!-- Running image --> |
| <b-card> |
| <template #header> |
| <p class="font-weight-bold m-0"> |
| {{ $t('pageFirmware.singleFileUpload.runningImage') }} |
| </p> |
| </template> |
| <dl class="mb-0"> |
| <dt>{{ $t('pageFirmware.singleFileUpload.bmcAndServer') }}</dt> |
| <dd class="mb-0">{{ systemFirmwareVersion }}</dd> |
| </dl> |
| </b-card> |
| |
| <!-- Backup image --> |
| <b-card> |
| <template #header> |
| <p class="font-weight-bold m-0"> |
| {{ $t('pageFirmware.singleFileUpload.backupImage') }} |
| </p> |
| </template> |
| <dl> |
| <dt> |
| {{ $t('pageFirmware.singleFileUpload.bmcAndServer') }} |
| </dt> |
| <dd> |
| <status-icon v-if="showBackupImageStatus" status="danger" /> |
| <span v-if="showBackupImageStatus" class="sr-only"> |
| {{ backupFirmwareStatus }} |
| </span> |
| {{ backupFirmwareVersion }} |
| </dd> |
| </dl> |
| <b-btn |
| v-b-modal.modal-switch-to-running |
| data-test-id="firmware-button-switchToRunning" |
| variant="link" |
| size="sm" |
| class="py-0 px-1 mt-2" |
| :disabled="isPageDisabled || !isRebootFromBackupAvailable" |
| > |
| <icon-switch class="d-none d-sm-inline-block" /> |
| {{ $t('pageFirmware.singleFileUpload.switchToRunning') }} |
| </b-btn> |
| </b-card> |
| </b-card-group> |
| </page-section> |
| </b-col> |
| </b-row> |
| <b-row> |
| <!-- Update firmware --> |
| <b-col sm="8" md="6" xl="4"> |
| <page-section |
| :section-title="$t('pageFirmware.singleFileUpload.updateFirmware')" |
| > |
| <div class="form-background p-3"> |
| <b-form @submit.prevent="onSubmitUpload"> |
| <b-form-group |
| :label="$t('pageFirmware.singleFileUpload.fileSource')" |
| :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> |
| <form-file |
| id="image-file" |
| accept=".tar" |
| :disabled="isPageDisabled" |
| :state="getValidationState($v.file)" |
| aria-describedby="image-file-help-block" |
| @input="onFileUpload($event)" |
| > |
| <template #invalid> |
| <b-form-invalid-feedback role="alert"> |
| {{ $t('global.form.required') }} |
| </b-form-invalid-feedback> |
| </template> |
| </form-file> |
| </b-form-group> |
| </template> |
| |
| <!-- TFTP Server Upload --> |
| <template v-else> |
| <b-form-group |
| :label="$t('pageFirmware.singleFileUpload.fileAddress')" |
| label-for="tftp-address" |
| > |
| <b-form-input |
| id="tftp-address" |
| v-model="tftpFileAddress" |
| type="text" |
| :state="getValidationState($v.tftpFileAddress)" |
| :disabled="isPageDisabled" |
| @input="$v.tftpFileAddress.$touch()" |
| /> |
| <b-form-invalid-feedback role="alert"> |
| {{ $t('global.form.fieldRequired') }} |
| </b-form-invalid-feedback> |
| </b-form-group> |
| </template> |
| <b-btn |
| data-test-id="firmware-button-startUpdate" |
| type="submit" |
| variant="primary" |
| :disabled="isPageDisabled" |
| > |
| {{ $t('pageFirmware.singleFileUpload.startUpdate') }} |
| </b-btn> |
| <alert |
| v-if="!isHostOff" |
| variant="warning" |
| :small="true" |
| class="mt-4" |
| > |
| <p class="col-form-label"> |
| {{ |
| $t( |
| 'pageFirmware.singleFileUpload.alert.serverMustBePoweredOffToUpdateFirmware' |
| ) |
| }} |
| </p> |
| </alert> |
| </b-form> |
| </div> |
| </page-section> |
| </b-col> |
| </b-row> |
| |
| <!-- Modals --> |
| <modal-update-firmware |
| :running="systemFirmwareVersion" |
| :backup="backupFirmwareVersion" |
| @ok="updateFirmware" |
| /> |
| <modal-switch-to-running |
| :backup="backupFirmwareVersion" |
| @ok="switchToRunning" |
| /> |
| </b-container> |
| </template> |
| |
| <script> |
| import { requiredIf } from 'vuelidate/lib/validators'; |
| import { mapGetters } from 'vuex'; |
| import IconSwitch from '@carbon/icons-vue/es/arrows--horizontal/20'; |
| |
| import PageSection from '@/components/Global/PageSection'; |
| import PageTitle from '@/components/Global/PageTitle'; |
| import Alert from '@/components/Global/Alert'; |
| import FormFile from '@/components/Global/FormFile'; |
| import StatusIcon from '@/components/Global/StatusIcon'; |
| import ModalUpdateFirmware from './FirmwareSingleImageModalUpdateFirmware'; |
| import ModalSwitchToRunning from './FirmwareSingleImageModalSwitchToRunning'; |
| |
| import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; |
| import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; |
| import BVToastMixin from '@/components/Mixins/BVToastMixin'; |
| |
| export default { |
| name: 'FirmwareSingleImage', |
| components: { |
| Alert, |
| FormFile, |
| IconSwitch, |
| ModalSwitchToRunning, |
| ModalUpdateFirmware, |
| PageSection, |
| PageTitle, |
| StatusIcon, |
| }, |
| mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin], |
| beforeRouteLeave(to, from, next) { |
| this.hideLoader(); |
| next(); |
| }, |
| data() { |
| return { |
| isWorkstationSelected: true, |
| file: null, |
| tftpFileAddress: null, |
| timeoutId: null, |
| loading, |
| }; |
| }, |
| 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; |
| }, |
| showBackupImageStatus() { |
| return ( |
| this.backupFirmwareStatus === 'Critical' || |
| this.backupFirmwareStatus === 'Warning' |
| ); |
| }, |
| }, |
| watch: { |
| isWorkstationSelected: function () { |
| this.$v.$reset(); |
| this.file = null; |
| this.tftpFileAddress = null; |
| }, |
| }, |
| created() { |
| this.startLoader(); |
| this.$store.dispatch('firmwareSingleImage/getUpdateServiceApplyTime'); |
| Promise.all([ |
| this.$store.dispatch('global/getHostStatus'), |
| this.$store.dispatch('firmwareSingleImage/getFirmwareInformation'), |
| ]).finally(() => this.endLoader()); |
| }, |
| validations() { |
| return { |
| file: { |
| required: requiredIf(function () { |
| return this.isWorkstationSelected; |
| }), |
| }, |
| tftpFileAddress: { |
| required: requiredIf(function () { |
| return !this.isWorkstationSelected; |
| }), |
| }, |
| }; |
| }, |
| methods: { |
| updateFirmware() { |
| this.setRebootTimeout(360000, () => { |
| this.infoToast( |
| this.$t('pageFirmware.singleFileUpload.toast.verifyUpdateMessage'), |
| { |
| title: this.$t('pageFirmware.singleFileUpload.toast.verifyUpdate'), |
| refreshAction: true, |
| } |
| ); |
| }); |
| this.infoToast( |
| this.$t('pageFirmware.singleFileUpload.toast.updateStartedMessage'), |
| { |
| title: this.$t('pageFirmware.singleFileUpload.toast.updateStarted'), |
| timestamp: true, |
| } |
| ); |
| if (this.isWorkstationSelected) { |
| this.dispatchWorkstationUpload(); |
| } else { |
| this.dispatchTftpUpload(); |
| } |
| }, |
| dispatchWorkstationUpload() { |
| this.$store |
| .dispatch('firmwareSingleImage/uploadFirmware', this.file) |
| .catch(({ message }) => { |
| this.errorToast(message); |
| this.clearRebootTimeout(); |
| }); |
| }, |
| dispatchTftpUpload() { |
| this.$store |
| .dispatch( |
| 'firmwareSingleImage/uploadFirmwareTFTP', |
| this.tftpFileAddress |
| ) |
| .catch(({ message }) => { |
| this.errorToast(message); |
| this.clearRebootTimeout(); |
| }); |
| }, |
| switchToRunning() { |
| this.setRebootTimeout(60000, () => { |
| this.infoToast( |
| this.$t('pageFirmware.singleFileUpload.toast.verifySwitchMessage'), |
| { |
| title: this.$t('pageFirmware.singleFileUpload.toast.verifySwitch'), |
| refreshAction: true, |
| } |
| ); |
| }); |
| this.$store |
| .dispatch('firmwareSingleImage/switchFirmwareAndReboot') |
| .then(() => |
| this.infoToast( |
| this.$t('pageFirmware.singleFileUpload.toast.rebootStartedMessage'), |
| { |
| title: this.$t( |
| 'pageFirmware.singleFileUpload.toast.rebootStarted' |
| ), |
| } |
| ) |
| ) |
| .catch(({ message }) => { |
| this.errorToast(message); |
| this.clearRebootTimeout(); |
| }); |
| }, |
| setRebootTimeout(timeoutMs = 60000, callback) { |
| // Set a timeout to disable page interactions |
| // during a BMC reboot |
| this.startLoader(); |
| this.timeoutId = setTimeout(() => { |
| this.endLoader(); |
| if (callback) callback(); |
| }, timeoutMs); |
| }, |
| clearRebootTimeout() { |
| if (this.timeoutId) { |
| clearTimeout(this.timeoutId); |
| this.endLoader(); |
| } |
| }, |
| onSubmitUpload() { |
| this.$v.$touch(); |
| if (this.$v.$invalid) return; |
| this.$bvModal.show('modal-update-firmware'); |
| }, |
| onFileUpload(file) { |
| this.file = file; |
| this.$v.file.$touch(); |
| }, |
| }, |
| }; |
| </script> |