summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/env/components/FirmwareSingleImage/FirmwareAlertServerPower.vue60
-rw-r--r--src/env/components/FirmwareSingleImage/FirmwareCardsBmc.vue145
-rw-r--r--src/env/components/FirmwareSingleImage/FirmwareCardsHost.vue75
-rw-r--r--src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue235
-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.vue48
-rw-r--r--src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue389
-rw-r--r--src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpdateFirmware.vue44
-rw-r--r--src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js146
-rw-r--r--src/locales/en-US.json32
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.",