| <template> |
| <b-container fluid="xl"> |
| <page-title :description="$t('pageFirmware.pageDescription')" /> |
| <b-row class="mb-4"> |
| <b-col md="10" lg="12" xl="8" class="pr-xl-4"> |
| <!-- Firmware on BMC --> |
| <page-section :section-title="$t('pageFirmware.firmwareOnBmc')"> |
| <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">{{ bmcFirmwareCurrentVersion }}</dd> |
| </dl> |
| </template> |
| <dl class="my-0"> |
| <dt>{{ $t('pageFirmware.state') }}:</dt> |
| <dd>{{ bmcFirmwareCurrentState }}</dd> |
| </dl> |
| <template #footer></template> |
| </b-card> |
| |
| <!-- Backup FW --> |
| <b-card footer-class="p-0"> |
| <template #header> |
| <dl class="mb-0"> |
| <dt>{{ $t('pageFirmware.backup') }}</dt> |
| <dd class="mb-0">{{ bmcFirmwareBackupVersion }}</dd> |
| </dl> |
| </template> |
| <dl class="my-0"> |
| <dt>{{ $t('pageFirmware.state') }}:</dt> |
| <dd>{{ bmcFirmwareBackupState }}</dd> |
| </dl> |
| <template #footer> |
| <b-btn |
| v-b-modal.modal-reboot-backup-bmc |
| :disabled="!bmcFirmwareBackupVersion" |
| variant="link" |
| size="sm" |
| > |
| <icon-switch class="d-none d-sm-inline-block" /> |
| {{ $t('pageFirmware.makeCurrentVersion') }}</b-btn |
| > |
| </template> |
| </b-card> |
| </b-card-group> |
| </page-section> |
| |
| <!-- Firmware on Host --> |
| <page-section :section-title="$t('pageFirmware.firmwareOnHost')"> |
| <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">{{ hostFirmwareCurrentVersion }}</dd> |
| </dl> |
| </template> |
| <!-- State --> |
| <dl class="my-0"> |
| <dt>{{ $t('pageFirmware.state') }}:</dt> |
| <dd>{{ hostFirmwareCurrentState }}</dd> |
| </dl> |
| </b-card> |
| |
| <!-- Backup FW --> |
| <b-card> |
| <template #header> |
| <dl class="mb-0"> |
| <dt>{{ $t('pageFirmware.backup') }}</dt> |
| <dd class="mb-0">{{ hostFirmwareBackupVersion }}</dd> |
| </dl> |
| </template> |
| <!-- State --> |
| <dl class="my-0"> |
| <dt>{{ $t('pageFirmware.state') }}:</dt> |
| <dd>{{ hostFirmwareBackupState }}</dd> |
| </dl> |
| </b-card> |
| </b-card-group> |
| </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')"> |
| <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')" |
| :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)" |
| @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"> |
| {{ $t('pageFirmware.form.uploadAndRebootBmcOrHost') }} |
| </b-btn> |
| </b-form-group> |
| </b-form> |
| </page-section> |
| </b-col> |
| </b-row> |
| |
| <!-- Modals --> |
| <modal-upload @ok="uploadFirmware" /> |
| <modal-reboot-backup-bmc |
| :current="bmcFirmwareCurrentVersion || '--'" |
| :backup="bmcFirmwareBackupVersion || '--'" |
| @ok="switchBmcFirmware" |
| /> |
| </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 ModalUpload from './FirmwareModalUpload'; |
| import ModalRebootBackupBmc from './FirmwareModalRebootBackupBmc'; |
| |
| 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, |
| IconSwitch, |
| ModalRebootBackupBmc, |
| ModalUpload, |
| PageSection, |
| PageTitle, |
| }, |
| mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin], |
| beforeRouteLeave(to, from, next) { |
| this.hideLoader(); |
| this.clearRebootTimeout(); |
| next(); |
| }, |
| data() { |
| return { |
| isWorkstationSelected: true, |
| file: null, |
| tftpIpAddress: null, |
| tftpFileName: null, |
| timeoutId: null, |
| }; |
| }, |
| computed: { |
| ...mapGetters('firmware', [ |
| 'bmcFirmwareCurrentVersion', |
| 'bmcFirmwareCurrentState', |
| 'bmcFirmwareBackupVersion', |
| 'bmcFirmwareBackupState', |
| 'hostFirmwareCurrentVersion', |
| 'hostFirmwareCurrentState', |
| 'hostFirmwareBackupVersion', |
| 'hostFirmwareBackupState', |
| ]), |
| }, |
| watch: { |
| isWorkstationSelected: function () { |
| this.$v.$reset(); |
| this.file = null; |
| this.tftpIpAddress = null; |
| this.tftpFileName = null; |
| }, |
| }, |
| created() { |
| this.startLoader(); |
| this.$store.dispatch('firmware/getUpdateServiceApplyTime'); |
| this.$store |
| .dispatch('firmware/getFirmwareInformation') |
| .finally(() => this.endLoader()); |
| }, |
| 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(); |
| }); |
| }, |
| switchBmcFirmware() { |
| this.setRebootTimeout(); |
| this.$store |
| .dispatch('firmware/switchBmcFirmware') |
| .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'); |
| }, |
| }, |
| }; |
| </script> |
| |
| <style lang="scss" scoped> |
| .update-code { |
| border-left: none; |
| @include media-breakpoint-up(xl) { |
| border-left: 1px solid gray('300'); |
| } |
| } |
| .card-footer { |
| height: 41px; |
| } |
| .card-body { |
| padding: 0.75rem 1.25rem; |
| } |
| </style> |