summaryrefslogtreecommitdiff
path: root/src/env
diff options
context:
space:
mode:
authorYoshie Muranaka <yoshiemuranaka@gmail.com>2021-02-11 20:59:13 +0300
committerDerick Montague <derick.montague@ibm.com>2021-02-25 18:06:40 +0300
commit7bc85e4936f1fe0178bae3bedcdc11c5e00564d6 (patch)
treeb1bfd50d875b2b7442b4d816bdf989caa3852cdc /src/env
parenta36a32f112b2554109502c569fb4b9d64e88053d (diff)
downloadwebui-vue-7bc85e4936f1fe0178bae3bedcdc11c5e00564d6.tar.xz
Update single file firmware card layout
Updates will enable a more dynamic firmware page layout by inspecting all firmware images for host images. GETs all firmware inventory at Redfish endpoint '/redfish/v1/UpdateService/FirmwareInventory' then checks if any image includes RelatedItem '/redfish/v1/Systems/system/Bios' to determine whether the UI should show combined or separate firmware cards for BMC and Host. This is part of an effort to make the firmware page more dynamic. These changes are only visisble with ibm dotenv variables. Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com> Change-Id: I8542a27c6ff421bcb24c8b2570dbe150d5c1ce6c
Diffstat (limited to 'src/env')
-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
9 files changed, 690 insertions, 452 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: {