diff options
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareAlertServerPower.vue | 60 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareCardsBmc.vue | 145 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareCardsHost.vue | 75 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue | 235 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareModalSwitchToRunning.vue (renamed from src/env/components/FirmwareSingleImage/FirmwareSingleImageModalSwitchToRunning.vue) | 0 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareModalUpdateFirmware.vue | 48 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue | 389 | ||||
-rw-r--r-- | src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpdateFirmware.vue | 44 | ||||
-rw-r--r-- | src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js | 146 | ||||
-rw-r--r-- | src/locales/en-US.json | 32 |
10 files changed, 712 insertions, 462 deletions
diff --git a/src/env/components/FirmwareSingleImage/FirmwareAlertServerPower.vue b/src/env/components/FirmwareSingleImage/FirmwareAlertServerPower.vue new file mode 100644 index 00000000..f7ac0fc9 --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareAlertServerPower.vue @@ -0,0 +1,60 @@ +<template> + <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> +</template> + +<script> +import Alert from '@/components/Global/Alert'; + +export default { + components: { Alert }, + props: { + isHostOff: { + required: true, + type: Boolean, + }, + }, + computed: { + isOperationInProgress() { + return this.$store.getters['controls/isOperationInProgress']; + }, + }, +}; +</script> diff --git a/src/env/components/FirmwareSingleImage/FirmwareCardsBmc.vue b/src/env/components/FirmwareSingleImage/FirmwareCardsBmc.vue new file mode 100644 index 00000000..857adf0f --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareCardsBmc.vue @@ -0,0 +1,145 @@ +<template> + <div> + <page-section :section-title="sectionTitle"> + <b-card-group deck> + <!-- Running image --> + <b-card> + <template #header> + <p class="font-weight-bold m-0"> + {{ $t('pageFirmware.singleFileUpload.cardTitleRunning') }} + </p> + </template> + <dl class="mb-0"> + <dt>{{ $t('pageFirmware.singleFileUpload.cardBodyVersion') }}</dt> + <dd class="mb-0">{{ runningVersion }}</dd> + </dl> + </b-card> + + <!-- Backup image --> + <b-card> + <template #header> + <p class="font-weight-bold m-0"> + {{ $t('pageFirmware.singleFileUpload.cardTitleBackup') }} + </p> + </template> + <dl> + <dt>{{ $t('pageFirmware.singleFileUpload.cardBodyVersion') }}</dt> + <dd> + <status-icon v-if="showBackupImageStatus" status="danger" /> + <span v-if="showBackupImageStatus" class="sr-only"> + {{ backupStatus }} + </span> + {{ backupVersion }} + </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 || !backup" + > + <icon-switch class="d-none d-sm-inline-block" /> + {{ $t('pageFirmware.singleFileUpload.cardActionSwitchToRunning') }} + </b-btn> + </b-card> + </b-card-group> + </page-section> + <modal-switch-to-running :backup="backupVersion" @ok="switchToRunning" /> + </div> +</template> + +<script> +import IconSwitch from '@carbon/icons-vue/es/arrows--horizontal/20'; +import PageSection from '@/components/Global/PageSection'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; + +import ModalSwitchToRunning from './FirmwareModalSwitchToRunning'; + +export default { + components: { IconSwitch, ModalSwitchToRunning, PageSection }, + mixins: [BVToastMixin, LoadingBarMixin], + props: { + isPageDisabled: { + required: true, + type: Boolean, + default: false, + }, + }, + data() { + return { + loading, + }; + }, + computed: { + isSingleFileUploadEnabled() { + return this.$store.getters[ + 'firmwareSingleImage/isSingleFileUploadEnabled' + ]; + }, + sectionTitle() { + if (this.isSingleFileUploadEnabled) { + return this.$t( + 'pageFirmware.singleFileUpload.sectionTitleBmcCardsCombined' + ); + } + return this.$t('pageFirmware.singleFileUpload.sectionTitleBmcCards'); + }, + running() { + return this.$store.getters['firmwareSingleImage/activeBmcFirmware']; + }, + backup() { + return this.$store.getters['firmwareSingleImage/backupBmcFirmware']; + }, + runningVersion() { + return this.running?.version || '--'; + }, + backupVersion() { + return this.backup?.version || '--'; + }, + backupStatus() { + return this.backup?.status || null; + }, + showBackupImageStatus() { + return ( + this.backupStatus === 'Critical' || this.backupStatus === 'Warning' + ); + }, + }, + methods: { + switchToRunning() { + this.startLoader(); + const timerId = setTimeout(() => { + this.endLoader(); + this.infoToast( + this.$t('pageFirmware.singleFileUpload.toast.verifySwitchMessage'), + { + title: this.$t('pageFirmware.singleFileUpload.toast.verifySwitch'), + refreshAction: true, + } + ); + }, 60000); + + 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); + clearTimeout(timerId); + this.endLoader(); + }); + }, + }, +}; +</script> diff --git a/src/env/components/FirmwareSingleImage/FirmwareCardsHost.vue b/src/env/components/FirmwareSingleImage/FirmwareCardsHost.vue new file mode 100644 index 00000000..c47f60f5 --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareCardsHost.vue @@ -0,0 +1,75 @@ +<template> + <page-section + :section-title="$t('pageFirmware.singleFileUpload.sectionTitleHostCards')" + > + <b-card-group deck> + <!-- Running image --> + <b-card> + <template #header> + <p class="font-weight-bold m-0"> + {{ $t('pageFirmware.singleFileUpload.cardTitleRunning') }} + </p> + </template> + <dl class="mb-0"> + <dt>{{ $t('pageFirmware.singleFileUpload.cardBodyVersion') }}</dt> + <dd class="mb-0">{{ runningVersion }}</dd> + </dl> + </b-card> + + <!-- Backup image --> + <b-card> + <template #header> + <p class="font-weight-bold m-0"> + {{ $t('pageFirmware.singleFileUpload.cardTitleBackup') }} + </p> + </template> + <dl class="mb-0"> + <dt>{{ $t('pageFirmware.singleFileUpload.cardBodyVersion') }}</dt> + <dd class="mb-0"> + <status-icon v-if="showBackupImageStatus" status="danger" /> + <span v-if="showBackupImageStatus" class="sr-only"> + {{ backupStatus }} + </span> + {{ backupVersion }} + </dd> + </dl> + </b-card> + </b-card-group> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; + +export default { + components: { PageSection }, + computed: { + running() { + return this.$store.getters['firmwareSingleImage/activeHostFirmware']; + }, + backup() { + return this.$store.getters['firmwareSingleImage/backupHostFirmware']; + }, + runningVersion() { + return this.running?.version || '--'; + }, + backupVersion() { + return this.backup?.version || '--'; + }, + backupStatus() { + return this.backup?.status || null; + }, + showBackupImageStatus() { + return ( + this.backupStatus === 'Critical' || this.backupStatus === 'Warning' + ); + }, + }, +}; +</script> + +<style lang="scss" scoped> +.page-section { + margin-top: -$spacer * 1.5; +} +</style> diff --git a/src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue b/src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue new file mode 100644 index 00000000..f13b8e00 --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue @@ -0,0 +1,235 @@ +<template> + <div> + <div class="form-background p-3"> + <b-form @submit.prevent="onSubmitUpload"> + <b-form-group + v-if="isTftpUploadAvailable" + :label=" + $t('pageFirmware.singleFileUpload.form.updateFirmware.fileSource') + " + :disabled="isPageDisabled" + > + <b-form-radio v-model="isWorkstationSelected" :value="true"> + {{ + $t( + 'pageFirmware.singleFileUpload.form.updateFirmware.workstation' + ) + }} + </b-form-radio> + <b-form-radio v-model="isWorkstationSelected" :value="false"> + {{ + $t('pageFirmware.singleFileUpload.form.updateFirmware.tftpServer') + }} + </b-form-radio> + </b-form-group> + + <!-- Workstation Upload --> + <template v-if="isWorkstationSelected"> + <b-form-group + :label=" + $t('pageFirmware.singleFileUpload.form.updateFirmware.imageFile') + " + label-for="image-file" + > + <b-form-text id="image-file-help-block"> + {{ + $t( + 'pageFirmware.singleFileUpload.form.updateFirmware.imageFileHelperText' + ) + }} + </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.form.updateFirmware.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.form.updateFirmware.startUpdate') + }} + </b-btn> + <alert + v-if="isServerPowerOffRequired && !isHostOff" + variant="warning" + :small="true" + class="mt-4" + > + <p class="col-form-label"> + {{ + $t( + 'pageFirmware.singleFileUpload.alert.serverMustBePoweredOffToUpdateFirmware' + ) + }} + </p> + </alert> + </b-form> + </div> + + <!-- Modals --> + <modal-update-firmware @ok="updateFirmware" /> + </div> +</template> + +<script> +import { requiredIf } from 'vuelidate/lib/validators'; + +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; + +import Alert from '@/components/Global/Alert'; +import FormFile from '@/components/Global/FormFile'; +import ModalUpdateFirmware from './FirmwareModalUpdateFirmware'; + +export default { + components: { Alert, FormFile, ModalUpdateFirmware }, + mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin], + props: { + isPageDisabled: { + required: true, + type: Boolean, + default: false, + }, + isHostOff: { + required: true, + type: Boolean, + }, + }, + data() { + return { + loading, + isWorkstationSelected: true, + file: null, + tftpFileAddress: null, + isServerPowerOffRequired: + process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true', + }; + }, + computed: { + isTftpUploadAvailable() { + return this.$store.getters['firmwareSingleImage/isTftpUploadAvailable']; + }, + }, + watch: { + isWorkstationSelected: function () { + this.$v.$reset(); + this.file = null; + this.tftpFileAddress = null; + }, + }, + validations() { + return { + file: { + required: requiredIf(function () { + return this.isWorkstationSelected; + }), + }, + tftpFileAddress: { + required: requiredIf(function () { + return !this.isWorkstationSelected; + }), + }, + }; + }, + created() { + this.$store.dispatch('firmwareSingleImage/getUpdateServiceSettings'); + }, + methods: { + updateFirmware() { + this.startLoader(); + const timerId = setTimeout(() => { + this.endLoader(); + this.infoToast( + this.$t('pageFirmware.singleFileUpload.toast.verifyUpdateMessage'), + { + title: this.$t('pageFirmware.singleFileUpload.toast.verifyUpdate'), + refreshAction: true, + } + ); + }, 360000); + this.infoToast( + this.$t('pageFirmware.singleFileUpload.toast.updateStartedMessage'), + { + title: this.$t('pageFirmware.singleFileUpload.toast.updateStarted'), + timestamp: true, + } + ); + if (this.isWorkstationSelected) { + this.dispatchWorkstationUpload(timerId); + } else { + this.dispatchTftpUpload(timerId); + } + }, + dispatchWorkstationUpload(timerId) { + this.$store + .dispatch('firmwareSingleImage/uploadFirmware', this.file) + .catch(({ message }) => { + this.endLoader(); + this.errorToast(message); + clearTimeout(timerId); + }); + }, + dispatchTftpUpload(timerId) { + this.$store + .dispatch( + 'firmwareSingleImage/uploadFirmwareTFTP', + this.tftpFileAddress + ) + .catch(({ message }) => { + this.endLoader(); + this.errorToast(message); + clearTimeout(timerId); + }); + }, + 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> diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalSwitchToRunning.vue b/src/env/components/FirmwareSingleImage/FirmwareModalSwitchToRunning.vue index 56f505d0..56f505d0 100644 --- a/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalSwitchToRunning.vue +++ b/src/env/components/FirmwareSingleImage/FirmwareModalSwitchToRunning.vue diff --git a/src/env/components/FirmwareSingleImage/FirmwareModalUpdateFirmware.vue b/src/env/components/FirmwareSingleImage/FirmwareModalUpdateFirmware.vue new file mode 100644 index 00000000..d6c52f9c --- /dev/null +++ b/src/env/components/FirmwareSingleImage/FirmwareModalUpdateFirmware.vue @@ -0,0 +1,48 @@ +<template> + <b-modal + id="modal-update-firmware" + :title="$t('pageFirmware.singleFileUpload.sectionTitleUpdateFirmware')" + :ok-title=" + $t('pageFirmware.singleFileUpload.form.updateFirmware.startUpdate') + " + :cancel-title="$t('global.action.cancel')" + @ok="$emit('ok')" + > + <template v-if="isSingleFileUploadEnabled"> + <p> + {{ $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfo') }} + </p> + <p> + {{ + $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfo2', { + running: runningBmcVersion, + }) + }} + </p> + <p class="m-0"> + {{ $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfo3') }} + </p> + </template> + <template v-else> + {{ $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfoDefault') }} + </template> + </b-modal> +</template> + +<script> +export default { + computed: { + runningBmc() { + return this.$store.getters['firmwareSingleImage/activeBmcFirmware']; + }, + runningBmcVersion() { + return this.runningBmc?.version || '--'; + }, + isSingleFileUploadEnabled() { + return this.$store.getters[ + 'firmwareSingleImage/isSingleFileUploadEnabled' + ]; + }, + }, +}; +</script> diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue index b7ad4c53..2e601bd4 100644 --- a/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue +++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue @@ -1,244 +1,68 @@ <template> <b-container fluid="xl"> <page-title /> - <b-row v-if="isServerPowerOffRequired"> - <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> + <alerts-server-power + v-if="isServerPowerOffRequired" + :is-host-off="isHostOff" + /> - <!-- 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> + <!-- Firmware cards --> <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 - v-if="isTftpUploadAvailable" - :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> + <b-col xl="10"> + <!-- BMC Firmware --> + <bmc-cards :is-page-disabled="isPageDisabled" /> - <!-- 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="isServerPowerOffRequired && !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> + <!-- Host Firmware --> + <host-cards v-if="!isSingleFileUploadEnabled" /> </b-col> </b-row> - <!-- Modals --> - <modal-update-firmware - :running="systemFirmwareVersion" - :backup="backupFirmwareVersion" - @ok="updateFirmware" - /> - <modal-switch-to-running - :backup="backupFirmwareVersion" - @ok="switchToRunning" - /> + <!-- Update firmware--> + <page-section + :section-title=" + $t('pageFirmware.singleFileUpload.sectionTitleUpdateFirmware') + " + > + <b-row> + <b-col sm="8" md="6" xl="4"> + <!-- Update form --> + <form-update + :is-host-off="isHostOff" + :is-page-disabled="isPageDisabled" + /> + </b-col> + </b-row> + </page-section> </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 AlertsServerPower from './FirmwareAlertServerPower'; +import BmcCards from './FirmwareCardsBmc'; +import FormUpdate from './FirmwareFormUpdate'; +import HostCards from './FirmwareCardsHost'; 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, + AlertsServerPower, + BmcCards, + FormUpdate, + HostCards, PageSection, PageTitle, - StatusIcon, }, - mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin], + mixins: [LoadingBarMixin], beforeRouteLeave(to, from, next) { this.hideLoader(); next(); }, data() { return { - isWorkstationSelected: true, - file: null, - tftpFileAddress: null, - timeoutId: null, loading, isServerPowerOffRequired: process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true', @@ -251,152 +75,23 @@ export default { isHostOff() { return this.hostStatus === 'off' ? true : false; }, - isOperationInProgress() { - return this.$store.getters['controls/isOperationInProgress']; + isSingleFileUploadEnabled() { + return this.$store.getters[ + 'firmwareSingleImage/isSingleFileUploadEnabled' + ]; }, - ...mapGetters('firmwareSingleImage', [ - 'backupFirmwareStatus', - 'backupFirmwareVersion', - 'isRebootFromBackupAvailable', - 'systemFirmwareVersion', - 'isTftpUploadAvailable', - ]), isPageDisabled() { if (this.isServerPowerOffRequired) { return !this.isHostOff || this.loading || this.isOperationInProgress; } return 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/getUpdateServiceSettings'); - 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(); - }, + this.$store + .dispatch('firmwareSingleImage/getFirmwareInformation') + .finally(() => this.endLoader()); }, }; </script> diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpdateFirmware.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpdateFirmware.vue deleted file mode 100644 index 51575253..00000000 --- a/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpdateFirmware.vue +++ /dev/null @@ -1,44 +0,0 @@ -<template> - <b-modal - id="modal-update-firmware" - :title="$t('pageFirmware.singleFileUpload.updateFirmware')" - :ok-title="$t('pageFirmware.singleFileUpload.startUpdate')" - :cancel-title="$t('global.action.cancel')" - @ok="$emit('ok')" - > - <p> - {{ $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfo') }} - </p> - <p v-if="showMessage"> - {{ - $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfo2', { - backup, - running, - }) - }} - </p> - <p class="m-0"> - {{ $t('pageFirmware.singleFileUpload.modal.updateFirmwareInfo3') }} - </p> - </b-modal> -</template> - -<script> -export default { - props: { - backup: { - type: String, - required: true, - }, - running: { - type: String, - required: true, - }, - }, - computed: { - showMessage() { - return this.backup !== this.running; - }, - }, -}; -</script> diff --git a/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js b/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js index b16cac8a..ae4d6333 100644 --- a/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js +++ b/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js @@ -4,83 +4,107 @@ import i18n from '@/i18n'; const FirmwareSingleImageStore = { namespaced: true, state: { - activeFirmware: { - version: '--', - id: null, - location: null, - }, - backupFirmware: { - version: '--', - id: null, - location: null, - status: null, - }, + bmcFirmware: [], + hostFirmware: [], + bmcActiveFirmwareId: null, + hostActiveFirmwareId: null, applyTime: null, tftpAvailable: false, }, getters: { - systemFirmwareVersion: (state) => state.activeFirmware.version, - backupFirmwareVersion: (state) => state.backupFirmware.version, - backupFirmwareStatus: (state) => state.backupFirmware.status, - isRebootFromBackupAvailable: (state) => - state.backupFirmware.id ? true : false, - bmcFirmwareCurrentVersion: (state) => state.activeFirmware.version, //this getter is needed for the Overview page, isTftpUploadAvailable: (state) => state.tftpAvailable, - }, - mutations: { - setActiveFirmware: (state, { version, id, location }) => { - state.activeFirmware.version = version; - state.activeFirmware.id = id; - state.activeFirmware.location = location; + isSingleFileUploadEnabled: (state) => state.hostFirmware.length === 0, + activeBmcFirmware: (state) => { + return state.bmcFirmware.find( + (firmware) => firmware.id === state.bmcActiveFirmwareId + ); }, - setBackupFirmware: (state, { version, id, location, status }) => { - state.backupFirmware.version = version; - state.backupFirmware.id = id; - state.backupFirmware.location = location; - state.backupFirmware.status = status; + activeHostFirmware: (state) => { + return state.hostFirmware.find( + (firmware) => firmware.id === state.hostActiveFirmwareId + ); }, + backupBmcFirmware: (state) => { + return state.bmcFirmware.find( + (firmware) => firmware.id !== state.bmcActiveFirmwareId + ); + }, + backupHostFirmware: (state) => { + return state.hostFirmware.find( + (firmware) => firmware.id !== state.hostActiveFirmwareId + ); + }, + bmcFirmwareCurrentVersion: (_, getters) => + getters.activeBmcFirmware?.version, //this getter is needed for the Overview page, + }, + mutations: { + setActiveBmcFirmwareId: (state, id) => (state.bmcActiveFirmwareId = id), + setActiveHostFirmwareId: (state, id) => (state.hostActiveFirmwareId = id), + setBmcFirmware: (state, firmware) => (state.bmcFirmware = firmware), + setHostFirmware: (state, firmware) => (state.hostFirmware = firmware), setApplyTime: (state, applyTime) => (state.applyTime = applyTime), setTftpUploadAvailable: (state, tftpAvailable) => (state.tftpAvailable = tftpAvailable), }, actions: { - async getFirmwareInformation({ commit }) { - return await api + async getFirmwareInformation({ dispatch }) { + dispatch('getActiveHostFirmware'); + dispatch('getActiveBmcFirmware'); + return await dispatch('getFirmwareInventory'); + }, + getActiveBmcFirmware({ commit }) { + return 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 }; + const id = Links?.ActiveSoftwareImage['@odata.id'].split('/').pop(); + commit('setActiveBmcFirmwareId', id); }) - .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?.Health, - }); + .catch((error) => console.log(error)); + }, + getActiveHostFirmware({ commit }) { + return api + .get('/redfish/v1/Systems/system/Bios') + .then(({ data: { Links } }) => { + const id = Links?.ActiveSoftwareImage['@odata.id'].split('/').pop(); + commit('setActiveHostFirmwareId', id); }) .catch((error) => console.log(error)); }, + async getFirmwareInventory({ commit }) { + const inventoryList = await api + .get('/redfish/v1/UpdateService/FirmwareInventory') + .then(({ data: { Members = [] } = {} }) => + Members.map((item) => api.get(item['@odata.id'])) + ) + .catch((error) => console.log(error)); + await api + .all(inventoryList) + .then((response) => { + const bmcFirmware = []; + const hostFirmware = []; + response.forEach(({ data }) => { + const firmwareType = data?.RelatedItem?.[0]?.['@odata.id'] + .split('/') + .pop(); + const item = { + version: data?.Version, + id: data?.Id, + location: data?.['@odata.id'], + status: data?.Status?.Health, + }; + if (firmwareType === 'bmc') { + bmcFirmware.push(item); + } else if (firmwareType === 'Bios') { + hostFirmware.push(item); + } + }); + commit('setBmcFirmware', bmcFirmware); + commit('setHostFirmware', hostFirmware); + }) + .catch((error) => { + console.log(error); + }); + }, getUpdateServiceSettings({ commit }) { api .get('/redfish/v1/UpdateService') @@ -147,8 +171,8 @@ const FirmwareSingleImageStore = { throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot')); }); }, - async switchFirmwareAndReboot({ state }) { - const backupLoaction = state.backupFirmware.location; + async switchFirmwareAndReboot({ getters }) { + const backupLoaction = getters.backupBmcFirmware.location; const data = { Links: { ActiveSoftwareImage: { diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 8ca98bf0..714027bb 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -317,14 +317,14 @@ "successUploadTitle": "Code update started" }, "singleFileUpload": { - "backupImage": "Backup image", - "bmcAndServer": "BMC and server:", - "fileAddress": "File address", - "fileSource": "File source", - "runningImage": "Running image", - "startUpdate": "Start update", - "switchToRunning": "Switch to running", - "updateFirmware": "Update firmware", + "cardActionSwitchToRunning": "Switch to running", + "cardBodyVersion": "Version", + "cardTitleBackup": "Backup image", + "cardTitleRunning": "Running image", + "sectionTitleBmcCards": "BMC", + "sectionTitleBmcCardsCombined": "BMC and server", + "sectionTitleHostCards": "Host", + "sectionTitleUpdateFirmware": "Update firmware", "alert": { "operationInProgress": "Server power operation in progress.", "serverMustBePoweredOffTo": "Server must be powered off to:", @@ -333,14 +333,26 @@ "updateFirmware": "Update firmware", "viewServerPowerOperations": "View server power operations" }, + "form": { + "updateFirmware": { + "fileAddress": "File address", + "fileSource": "File source", + "imageFile": "Image file", + "imageFileHelperText": "Only .tar files accepted", + "startUpdate": "Start update", + "tftpServer": "TFTP server", + "workstation": "Workstation" + } + }, "modal": { "switchImages": "Switch images", "switchRunningImage": "Switch running image", "switchRunningImageInfo": "A BMC reboot is required to run the backup image. The application might be unresponsive during this time.", "switchRunningImageInfo2": "Are you sure you want to switch to the backup image (%{backup})?", "updateFirmwareInfo": "The BMC will reboot during the update process. The server cannot be powered on until the update is finished.", - "updateFirmwareInfo2": "The running image (%{running}) will be copied to backup and the backup image (%{backup}) will be permanently deleted.", - "updateFirmwareInfo3": "Are you sure you want to proceed with the update?" + "updateFirmwareInfo2": "The running image (%{running}) will be copied to backup. The backup image will be deleted.", + "updateFirmwareInfo3": "Are you sure you want to proceed with the update?", + "updateFirmwareInfoDefault": "The new image will be uploaded and activated. After that, the BMC or host will reboot automatically to run from the new image." }, "toast": { "errorSwitchImages": "Error switching running and backup images.", |