Files

1966 lines
64 KiB
Diff
Raw Permalink Normal View History

2026-04-23 17:07:55 +08:00
From 64626ebac7529a4cc471781ed45f0782d1b0c79f Mon Sep 17 00:00:00 2001
From: roly <Rolyli.Li@luxshare-ict.com>
Date: Thu, 9 Jan 2025 19:49:05 +0800
Subject: [PATCH] Support luxshare oem firmware update
---
src/components/Global/LinkButton.vue | 57 ++
src/components/Global/UploadFile.vue | 146 ++++
src/locales/en-US.json | 84 +-
src/store/modules/Operations/FirmwareStore.js | 220 ++++-
src/utilities/bus.js | 2 +
src/views/Operations/Firmware/Firmware.vue | 104 ++-
.../Operations/Firmware/FirmwareCardsBmc.vue | 27 +
.../Operations/Firmware/FirmwareCardsFPGA.vue | 40 +
.../Firmware/FirmwareCardsFcbCPLD.vue | 40 +
.../Operations/Firmware/FirmwareCardsMe.vue | 40 +
.../Firmware/FirmwareModalUpdateFirmware.vue | 29 +
.../Firmware/FirmwareUpdateStatus.vue | 822 ++++++++++++++++++
12 files changed, 1590 insertions(+), 21 deletions(-)
create mode 100755 src/components/Global/LinkButton.vue
create mode 100755 src/components/Global/UploadFile.vue
create mode 100755 src/utilities/bus.js
create mode 100755 src/views/Operations/Firmware/FirmwareCardsFPGA.vue
create mode 100755 src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue
create mode 100755 src/views/Operations/Firmware/FirmwareCardsMe.vue
create mode 100755 src/views/Operations/Firmware/FirmwareUpdateStatus.vue
diff --git a/src/components/Global/LinkButton.vue b/src/components/Global/LinkButton.vue
new file mode 100755
index 0000000..2a2022b
--- /dev/null
+++ b/src/components/Global/LinkButton.vue
@@ -0,0 +1,57 @@
+<template>
+ <span
+ :class="{
+ 'action-btn': true,
+ 'action-btn-primary': !danger,
+ 'action-disabled': disabled,
+ 'action-btn-danger': danger,
+ }"
+ @click="handleClick()"
+ >
+ <slot></slot>
+ </span>
+</template>
+
+<script>
+export default {
+ props: {
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ danger: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ methods: {
+ handleClick() {
+ if (this.disabled) return;
+ else this.$emit('click');
+ },
+ },
+};
+</script>
+
+<style lang="css" scoped>
+.action-btn {
+ white-space: nowrap;
+}
+.action-disabled {
+ cursor: not-allowed !important;
+ color: #ccc !important;
+}
+.action-btn {
+ cursor: pointer;
+}
+.action-btn:hover {
+ opacity: 0.7;
+}
+.action-btn-primary {
+ color: var(--primary);
+}
+.action-btn-danger {
+ margin: 0 5px;
+ color: var(--danger);
+}
+</style>
diff --git a/src/components/Global/UploadFile.vue b/src/components/Global/UploadFile.vue
new file mode 100755
index 0000000..3e492bd
--- /dev/null
+++ b/src/components/Global/UploadFile.vue
@@ -0,0 +1,146 @@
+<template>
+ <b-form
+ class="d-flex align-items-center oem-upload-file"
+ style="max-width: 700px"
+ @submit.prevent="onSubmitUpload"
+ >
+ <b-form-file
+ v-model="file"
+ :class="disabled ? 'upload-file-disabled' : undefined"
+ :disabled="disabled"
+ :placeholder="$t('global.fileUpload.browseText')"
+ ></b-form-file>
+ <div class="d-flex align-items-center ml-2">
+ <b-button
+ :disabled="disabled"
+ class="btn-file"
+ variant="primary"
+ size="sm"
+ type="submit"
+ >{{ $t('pageFirmware.form.updateFirmware.startUpload') }}</b-button
+ >
+ <b-button
+ v-show="!!file"
+ :disabled="disabled"
+ class="btn-file"
+ variant="danger"
+ size="sm"
+ @click="file = null"
+ ><icon-trashcan class="delete-icon"
+ /></b-button>
+ </div>
+ <div class="file-error-tips">
+ {{ fileErrorTips }}
+ </div>
+ </b-form>
+</template>
+
+<script>
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+export default {
+ components: {
+ IconTrashcan,
+ },
+ props: {
+ value: {
+ type: File,
+ default: () => {},
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ required: {
+ type: Boolean,
+ default: false,
+ },
+ validtedFile: {
+ type: Function,
+ default: () => {},
+ },
+ },
+ data() {
+ return {
+ file: null,
+ fileErrorTips: '',
+ };
+ },
+ watch: {
+ value(newVal) {
+ this.file = newVal;
+ },
+ file: {
+ handler(newVal) {
+ this.$emit('input', newVal);
+ if (!newVal) {
+ this.fileErrorTips = '';
+ } else {
+ this.validtedFile(newVal)
+ .then(() => {
+ this.fileErrorTips = '';
+ })
+ .catch((e) => {
+ this.fileErrorTips = e;
+ });
+ }
+ },
+ },
+ },
+ methods: {
+ onSubmitUpload() {
+ if (!this.file && this.required) {
+ this.fileErrorTips = this.$t('global.form.required');
+ return;
+ }
+ this.validtedFile(this.file)
+ .then(() => {
+ this.$emit('submit', this.file);
+ })
+ .catch((e) => {
+ this.fileErrorTips = e;
+ });
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+$btn-height: 33px;
+.btn-file {
+ height: $btn-height;
+ white-space: nowrap;
+}
+.file-error-tips {
+ position: absolute;
+ top: 40px;
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ color: #da1416;
+}
+</style>
+<style lang="css" scoped>
+.oem-upload-file /deep/ .custom-file-label::after {
+ content: '. . .';
+ cursor: pointer;
+ color: #fff;
+ background: var(--primary);
+}
+.oem-upload-file /deep/ .custom-file-label {
+ margin-bottom: 0;
+}
+.oem-upload-file /deep/ .custom-file {
+ height: 35px !important;
+}
+.oem-upload-file /deep/ .custom-file-input {
+ height: 35px !important;
+ width: 400px;
+}
+.oem-upload-file /deep/ .custom-file-label {
+ height: 35px !important;
+}
+.upload-file-disabled /deep/ .custom-file-label::after {
+ cursor: not-allowed;
+ color: #999;
+ background: #ccc;
+}
+</style>
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 93ad806..0a67945 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -331,6 +331,12 @@
}
},
"pageFirmware": {
+ "primaryImage": "Primary",
+ "secondaryImage": "Secondary",
+ "both": "Both (Not recommended)",
+ "sectionTitleMeCards": "ME Firmware",
+ "sectionTitleFcbCPLDCards": "Fan Board CPLD",
+ "sectionTitleFPGACards": "FPGA",
"cardActionSwitchToRunning": "Switch to running",
"cardBodyVersion": "Version",
"cardBodyReleaseDate": "Release Date",
@@ -340,19 +346,41 @@
"cardTitleFBCpld": "Fan Board CPLD",
"sectionTitleBmcCards": "BMC",
"sectionTitleBmcCardsCombined": "BMC and server",
- "sectionTitleHostCards": "Host",
+ "sectionTitleHostCards": "BIOS",
"sectionTitleUpdateFirmware": "Update firmware",
"sectionTitleCPLDCards": "CPLD",
+ "sectionTitleUpdateFirmwareStatus": "Update firmware status",
"alert": {
"operationInProgress": "Server power operation in progress.",
"serverMustBePoweredOffTo": "Server must be powered off to:",
"serverMustBePoweredOffToUpdateFirmware": "Server must be powered off to update firmware",
"switchRunningAndBackupImages": "Switch running and backup images",
"updateFirmware": "Update firmware",
- "viewServerPowerOperations": "View server power operations"
+ "viewServerPowerOperations": "View server power operations",
+ "boxupdateFirmware": "Box Update firmware",
+ "updateTask": "Background Update Task"
},
"form": {
+ "invalidFiletype": "Invalid file type",
+ "updateOptions": {
+ "keep": "Keep all configurations update",
+ "clear": "Non keep configuration update",
+ "partialKeep": "Keep partial configuration update",
+ "factoryReset": "Factory reset"
+ },
"updateFirmware": {
+ "updateTimeout": "Update timeout",
+ "UploadedFirmware": "Uploaded %{type}",
+ "updateBmcRestFail": "BMC reset failed",
+ "updateSecondaryInfo": "Secondary BMC is now updating, please wait",
+ "updateFinished": "%{type} %{version} Updated",
+ "ok": "Ok",
+ "version": "Version",
+ "biosKeepConfig": "Keep Configuration",
+ "biosNonKeepConfig": "Non Keep Configuration",
+ "options": "Options",
+ "startUpload": "Upload image",
+ "updateAll": "Update all",
"fileAddress": "File address",
"fileSource": "File source",
"imageFile": "Image file",
@@ -362,6 +390,11 @@
}
},
"modal": {
+ "updateAllConfirm": "Are you sure you want to update all?",
+ "deleteConfirm": "Are you sure you want to delete the %{firmware} firmware?",
+ "updateFirmwareInfoOemBmc": "The primary(secondary) BMC firmware will be updated as you choose. In some scenarios, the BMC will reboot. Please confirm whether to update BMC firmware immediately?",
+ "updateSuccessOem": "update task is queued and will be activated when host DC power cycle by BMC (IPMI command or Redfish I/F)",
+ "updateFirmwareInfoOem": "The new image will be async activated. After that, the host will reboot automatically to run from the new image.",
"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.",
@@ -372,16 +405,61 @@
"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": {
+ "errorUpdateAsyncAndSync": "BMC and other firmware cannot be updated simultaneously.",
+ "errorUpdateInPost": "The unit is in POST stage, can not flash BMC. Please wait a moment and try again.",
+ "errorUpdateInPeerNodeUpdating": "The peer node is upgrading components. Shared components do not allow dual nodes to be upgraded at the same time. Please try again later.",
+ "bmcUpdateTips": "BMC updates can cause other firmware to be lost.",
+ "errorUpdateFirmwareSign": "The firmware update failed or the signature was invalid.",
+ "errorInPostStatus": "BMC firmware can not update in BIOS POST status",
+ "uploadStarted": "Upload started",
+ "uploadStartedMessage": "Wait for the firmware upload notification before making any changes.",
+ "timeoutUpload": "Update timeout",
+ "timeoutUploadMessage": "Upload firmware image timeout",
+ "uploadSuccess": "Upload success",
+ "uploadSuccessMessage": "The firmware upload successfully",
+ "uploadFailed": "Upload failed",
+ "uploadFailedMessage": "The firmware upload failed",
+ "errorVerifyFirmware": "Failed to verify the signature image",
+ "errorUploadFirmware": "Failed to extract or verify the image",
"errorSwitchImages": "Error switching running and backup images.",
"errorUpdateFirmware": "Error starting firmware update.",
"rebootStarted": "Reboot started",
"rebootStartedMessage": "Successfully started reboot from backup image.",
"updateStarted": "Update started",
"updateStartedMessage": "Wait for the firmware update notification before making any changes.",
+ "updateSuccess": "Update success",
+ "updateSuccessMessage": "Successfully update the firmware, Please wait for the image refresh",
+ "updateFailed": "Update failed",
+ "updateFailedMessage": "Failed in update the firmware.",
"verifySwitch": "Verify switch",
"verifySwitchMessage": "Refresh the application to verify the running and backup images switched.",
"verifyUpdate": "Verify update",
- "verifyUpdateMessage": "Refresh the application to verify firmware updated successfully"
+ "verifyUpdateMessage": "Refresh the application to verify firmware updated successfully",
+ "errorGetUpdateStatus": "Failed to retrieve firmware update status.",
+ "deleteSuccess": "Deletion successful.",
+ "firmwareDeleted": "Firmware %{firmware} has been deleted.",
+ "deleteFailed": "Failed to delete firmware %{firmware}.",
+ "bmcUpdatingTips": "The page functionality is temporarily unavailable during the BMC firmware update.",
+ "bmcUpdateCompleteTips": "BMC upgrade completed. Please refresh the page after a moment. Note that the IP address may change in some cases.",
+ "validateFirmwareFailed": "Failed to validate the %{firmware} firmware image.",
+ "firmwareIdMismatch": "The %{firmware} firmware does not match"
+ },
+ "table": {
+ "updateComplete": "Update completed",
+ "updateFailed": "Update failed",
+ "updating": "Updating",
+ "waitUpdate": "Wait for update",
+ "emptyMessage": "No firmware available",
+ "update": "update",
+ "delete": "delete",
+ "noupdate": "No update",
+ "name": "Unit name",
+ "status": "Update Status",
+ "time": "Estimated time",
+ "progress": "Progress",
+ "firmwareName": "Name",
+ "firmwareStatus": "Status",
+ "firmwareVersion": "Version"
}
},
"pageInventory": {
diff --git a/src/store/modules/Operations/FirmwareStore.js b/src/store/modules/Operations/FirmwareStore.js
index 001eaf6..39b71e8 100644
--- a/src/store/modules/Operations/FirmwareStore.js
+++ b/src/store/modules/Operations/FirmwareStore.js
@@ -1,20 +1,57 @@
import api from '@/store/api';
import i18n from '@/i18n';
+import { bus } from '@/utilities/bus';
+const FIRMWARE_PURPOSE = {
+ BMC: 'xyz.openbmc_project.Software.Version.VersionPurpose.BMC',
+ CPLD: 'xyz.openbmc_project.Software.Version.VersionPurpose.CPLD',
+ FPGA: 'xyz.openbmc_project.Software.Version.VersionPurpose.FPGA',
+ BIOS: 'xyz.openbmc_project.Software.Version.VersionPurpose.Host',
+ PSU: 'xyz.openbmc_project.Software.Version.VersionPurpose.PSU',
+ HSBP: 'xyz.openbmc_project.Software.Version.VersionPurpose.HSBP',
+ FCB: 'xyz.openbmc_project.Software.Version.VersionPurpose.FCB',
+};
const FirmwareStore = {
namespaced: true,
state: {
bmcFirmware: [],
hostFirmware: [],
+ uploadFirmwareType: null,
+ uploadFirmwareState: null,
+ uploadFirmwareId: null,
+ firmwareFunctionData: [],
+ meFirmware: [],
cpldFirmware: [],
- fbcpldFirmware: [],
+ fcbCpldFirmware: [],
+ fpgaFirmware: [],
bmcActiveFirmwareId: null,
hostActiveFirmwareId: null,
applyTime: null,
httpPushUri: null,
tftpAvailable: false,
+ firmwareStatus: null,
+ isFirmwareProgress: false,
+ isNeedCycle: false,
+ bmcIsUpdating: false,
+ bmcIsUpdatingfCurrentPartition: false,
},
getters: {
+ uploadFirmwareId: (state) => state.uploadFirmwareId,
+ uploadFirmwareState: (state) => state.uploadFirmwareState,
+ uploadFirmwareType: (state) => state.uploadFirmwareType,
+ priorityFirmware: (state) => {
+ return state.firmwareFunctionData.find(
+ (firmware) => firmware.Purpose === FIRMWARE_PURPOSE.BMC
+ );
+ },
+ meFirmware: (state) => {
+ return state.meFirmware.find((firmware) => firmware.id === 'me');
+ },
+ fpgaFirmware: (state) => {
+ return state.fpgaFirmware.find(
+ (firmware) => firmware.id === 'fpga_active'
+ );
+ },
isTftpUploadAvailable: (state) => state.tftpAvailable,
isSingleFileUploadEnabled: (state) => state.hostFirmware.length === 0,
activeBmcFirmware: (state) => {
@@ -29,7 +66,9 @@ const FirmwareStore = {
},
backupBmcFirmware: (state) => {
return state.bmcFirmware.find(
- (firmware) => firmware.id !== state.bmcActiveFirmwareId
+ (firmware) =>
+ firmware.id !== state.bmcActiveFirmwareId &&
+ firmware.state === 'Enabled'
);
},
backupHostFirmware: (state) => {
@@ -42,28 +81,53 @@ const FirmwareStore = {
(firmware) => firmware.id === 'cpld_active'
);
},
- fbcpldFirmware: (state) => {
- return state.fbcpldFirmware.find(
- (firmware) => firmware.id === 'fb_cpld_active'
+ fcbCpldFirmware: (state) => {
+ return state.fcbCpldFirmware.find(
+ (firmware) => firmware.id === 'fcb_cpld_active'
);
},
+ allFirmwareStatus: (state) => state.firmwareStatus,
+ isFirmwareProgress: (state) => state.isFirmwareProgress,
+ isNeedCycle: (state) => state.isNeedCycle,
+ bmcIsUpdating: (state) => state.bmcIsUpdating,
+ bmcIsUpdatingfCurrentPartition: (state) =>
+ state.bmcIsUpdatingfCurrentPartition,
},
mutations: {
+ setFirmwareFunctionData: (state, firmware) =>
+ (state.firmwareFunctionData = firmware),
+ setMeFirmware: (state, firmware) => (state.meFirmware = firmware),
+ setFPGAFirmware: (state, firmware) => (state.fpgaFirmware = firmware),
setActiveBmcFirmwareId: (state, id) => (state.bmcActiveFirmwareId = id),
setActiveHostFirmwareId: (state, id) => (state.hostActiveFirmwareId = id),
setBmcFirmware: (state, firmware) => (state.bmcFirmware = firmware),
setHostFirmware: (state, firmware) => (state.hostFirmware = firmware),
setCPLDFirmware: (state, firmware) => (state.cpldFirmware = firmware),
- setFBCPLDFirmware: (state, firmware) => (state.fbcpldFirmware = firmware),
+ setFcbCpldFirmware: (state, firmware) => (state.fcbCpldFirmware = firmware),
setApplyTime: (state, applyTime) => (state.applyTime = applyTime),
setHttpPushUri: (state, httpPushUri) => (state.httpPushUri = httpPushUri),
setTftpUploadAvailable: (state, tftpAvailable) =>
(state.tftpAvailable = tftpAvailable),
+ setFirmwareUpdateStatus: (state, status) => (state.firmwareStatus = status),
+ setIsFirmwareProgress: (state, isProgress) =>
+ (state.isFirmwareProgress = isProgress),
+ setIsNeedCycle: (state, isNeedCycle) => (state.isNeedCycle = isNeedCycle),
+ setBmcIsUpdating: (state, bmcIsUpdating) =>
+ (state.bmcIsUpdating = bmcIsUpdating),
+ setBmcIsUpdatingfCurrentPartition: (state, bool) => {
+ state.bmcIsUpdatingfCurrentPartition = bool;
+ },
+ setBmcUpdateProgress: (state, progress) => {
+ const bmcFirmware = state.firmwareStatus.find((it) => it.BMC);
+ bmcFirmware.BMC.UpdateProgress = progress;
+ bmcFirmware.BMC.UpdateStatus = progress === 100 ? 'Updated' : 'Updating';
+ },
},
actions: {
async getFirmwareInformation({ dispatch }) {
dispatch('getActiveHostFirmware');
dispatch('getActiveBmcFirmware');
+ dispatch('getFirmwareFunctionData');
return await dispatch('getFirmwareInventory');
},
getActiveBmcFirmware({ commit }) {
@@ -94,10 +158,12 @@ const FirmwareStore = {
await api
.all(inventoryList)
.then((response) => {
+ const meFirmware = [];
+ const fpgaFirmware = [];
const bmcFirmware = [];
const hostFirmware = [];
const cpldFirmware = [];
- const fbcpldFirmware = [];
+ const fcbCpldFirmware = [];
response.forEach(({ data }) => {
const firmwareType = data?.RelatedItem?.[0]?.['@odata.id']
.split('/')
@@ -108,24 +174,29 @@ const FirmwareStore = {
location: data?.['@odata.id'],
status: data?.Status?.Health,
reldate: data?.ReleaseDate,
+ state: data?.Status?.State,
};
if (firmwareType === 'bmc') {
bmcFirmware.push(item);
} else if (firmwareType === 'Bios') {
hostFirmware.push(item);
}
-
if (item.id === 'cpld_active') {
cpldFirmware.push(item);
- }
- if (item.id === 'fb_cpld_active') {
- fbcpldFirmware.push(item);
+ } else if (item.id === 'fpga_active') {
+ fpgaFirmware.push(item);
+ } else if (item.id === 'me') {
+ meFirmware.push(item);
+ } else if (item.id === 'fcb_cpld_active') {
+ fcbCpldFirmware.push(item);
}
});
commit('setBmcFirmware', bmcFirmware);
commit('setHostFirmware', hostFirmware);
commit('setCPLDFirmware', cpldFirmware);
- commit('setFBCPLDFirmware', fbcpldFirmware);
+ commit('setFcbCpldFirmware', fcbCpldFirmware);
+ commit('setFPGAFirmware', fpgaFirmware);
+ commit('setMeFirmware', meFirmware);
})
.catch((error) => {
console.log(error);
@@ -214,6 +285,131 @@ const FirmwareStore = {
throw new Error(i18n.t('pageFirmware.toast.errorSwitchImages'));
});
},
+ // ======================== firmware OEM ========================
+ getFirmwareFunctionData({ commit }) {
+ return api
+ .get('/xyz/openbmc_project/software/functional')
+ .then((response) =>
+ api.all(response.data.data.endpoints.map((bmcurl) => api.get(bmcurl)))
+ )
+ .then((bmcResult) => {
+ const bmcStateData = bmcResult.map((datas) => datas.data.data);
+ commit('setFirmwareFunctionData', bmcStateData);
+ })
+ .catch((error) => {
+ console.log(error);
+ });
+ },
+ // ----------------- firmware update ----------
+ async uploadFirmwareOEM({ commit }, image) {
+ console.log('image', image);
+ const data = new FormData();
+ data.append('fwimage', image);
+ commit('setIsFirmwareProgress', true);
+ return await api
+ .post(
+ '/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareFile',
+ data
+ )
+ .then((response) => {
+ console.log('upload success', response);
+ return response;
+ })
+ .catch((error) => {
+ console.log('upload error', error);
+ throw new Error(i18n.t('pageFirmware.toast.errorUploadFirmware'));
+ })
+ .finally(() => commit('setIsFirmwareProgress', false));
+ },
+ async getAllUpdateStatus({ commit }) {
+ return await api
+ .get('/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareStatus')
+ .then((response) => {
+ // if (state.bmcIsUpdating) return;
+ commit('setFirmwareUpdateStatus', response.data.Oem.Luxshare);
+ const isNeedCycle = response.data.Oem.Luxshare.find((firmware) => {
+ const entries = Object.entries(firmware);
+ return (
+ entries[0][1].UpdateStatus === 'Queued' ||
+ (entries[0][0] === 'Retimer' &&
+ entries[0][1].UpdateStatus === 'Updated')
+ );
+ });
+ commit('setIsNeedCycle', !!isNeedCycle);
+ })
+ .catch((error) => {
+ console.log(error);
+ throw new Error(i18n.t('pageFirmware.toast.errorGetUpdateStatus'));
+ });
+ },
+ async startUpdate({ commit }, updateConfigs) {
+ commit('setIsFirmwareProgress', true);
+ return await api
+ .post(
+ '/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate',
+ updateConfigs
+ )
+ .finally(() => commit('setIsFirmwareProgress', false));
+ },
+ async deleteFirmware(_, target) {
+ console.log('target', target);
+ return await api.delete(
+ '/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate',
+ {
+ data: {
+ Target: target,
+ },
+ }
+ );
+ },
+ async pollingBmcUpdateStatus() {
+ const timeout = 3000;
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ reject();
+ }, timeout);
+ api
+ .get('/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareStatus')
+ .then((res) => {
+ const progress = res.data.Oem.Luxshare?.[0]?.BMC?.UpdateProgress;
+ resolve(progress);
+ })
+ .catch((error) => {
+ reject(error);
+ });
+ });
+ },
+ getBmcUpdateStatus({ state, commit, dispatch }) {
+ dispatch('pollingBmcUpdateStatus')
+ .then((progress) => {
+ const bmcFirmware = state.firmwareStatus.find((it) => it.BMC);
+ if (bmcFirmware.BMC.UpdateProgress === 100) {
+ return;
+ } else if (
+ progress !== undefined &&
+ progress > bmcFirmware.BMC.UpdateProgress
+ ) {
+ commit('setBmcUpdateProgress', progress);
+ }
+ if (progress === 100) {
+ bus.$emit('bmcUpdateComplete');
+ localStorage.setItem('loggingStatus', 'logout');
+ commit('authentication/logout', null, { root: true });
+ } else {
+ setTimeout(() => {
+ dispatch('getBmcUpdateStatus');
+ }, 3000);
+ }
+ })
+ .catch(() => {
+ const bmcFirmware = state.firmwareStatus.find((it) => it.BMC);
+ if (bmcFirmware.BMC.UpdateProgress === 100) return;
+ setTimeout(() => {
+ dispatch('getBmcUpdateStatus');
+ }, 3000);
+ });
+ },
+ // ======================== END ========================
},
};
diff --git a/src/utilities/bus.js b/src/utilities/bus.js
new file mode 100755
index 0000000..68259ae
--- /dev/null
+++ b/src/utilities/bus.js
@@ -0,0 +1,2 @@
+import Vue from 'vue';
+export const bus = new Vue();
diff --git a/src/views/Operations/Firmware/Firmware.vue b/src/views/Operations/Firmware/Firmware.vue
index 25fe0bb..106da18 100644
--- a/src/views/Operations/Firmware/Firmware.vue
+++ b/src/views/Operations/Firmware/Firmware.vue
@@ -13,15 +13,37 @@
<bmc-cards :is-page-disabled="isPageDisabled" />
<!-- Host Firmware -->
- <host-cards v-if="!isSingleFileUploadEnabled" />
-
- <!-- CPLD Firmware -->
- <cpld-cards v-if="cpld" />
+ <b-row>
+ <b-col xl="6">
+ <!-- Host Firmware -->
+ <host-cards v-if="!isSingleFileUploadEnabled" />
+ </b-col>
+ <b-col xl="6">
+ <!-- Me Firmware -->
+ <me-cards v-if="me" />
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col xl="6">
+ <!-- CPLD Firmware -->
+ <cpld-cards v-if="cpld" />
+ </b-col>
+ <b-col xl="6">
+ <!--Fan CPLD Firmware -->
+ <fcb-cpld-cards v-if="fcbCpld" />
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col xl="6">
+ <fpga-cards v-if="fpga" />
+ </b-col>
+ </b-row>
</b-col>
</b-row>
<!-- Update firmware-->
<page-section
+ v-if="!isOEMUpdate"
:section-title="$t('pageFirmware.sectionTitleUpdateFirmware')"
>
<b-row>
@@ -34,17 +56,29 @@
</b-col>
</b-row>
</page-section>
+
+ <!-- Update firmware status-->
+ <page-section
+ v-if="isOEMUpdate"
+ :section-title="$t('pageFirmware.sectionTitleUpdateFirmwareStatus')"
+ >
+ <update-status :is-page-disabled="isPageDisabled"></update-status>
+ </page-section>
</b-container>
</template>
<script>
import AlertsServerPower from './FirmwareAlertServerPower';
+import MeCards from './FirmwareCardsMe';
+import FcbCpldCards from './FirmwareCardsFcbCPLD';
+import FpgaCards from './FirmwareCardsFPGA';
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 CpldCards from './FirmwareCardsCPLD';
+import UpdateStatus from './FirmwareUpdateStatus';
import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
@@ -52,12 +86,16 @@ export default {
name: 'FirmwareSingleImage',
components: {
AlertsServerPower,
+ MeCards,
+ FcbCpldCards,
+ FpgaCards,
BmcCards,
FormUpdate,
HostCards,
PageSection,
PageTitle,
CpldCards,
+ UpdateStatus,
},
mixins: [LoadingBarMixin],
beforeRouteLeave(to, from, next) {
@@ -66,12 +104,36 @@ export default {
},
data() {
return {
+ isOEMUpdate: true, //false: original openbmc update
+ isUploaded: false,
loading,
isServerPowerOffRequired:
process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true',
};
},
computed: {
+ // is in upload or update, Used to gray out when switching back and forth with other pages.
+ isFirmwareProgress() {
+ return this.$store.getters['firmware/isFirmwareProgress'];
+ },
+ uploadFirmwareId() {
+ return this.$store.getters['firmware/uploadFirmwareId'];
+ },
+ uploadFirmwareType() {
+ return this.$store.getters['firmware/uploadFirmwareType'];
+ },
+ uploadFirmwareState() {
+ return this.$store.getters['firmware/uploadFirmwareState'];
+ },
+ me() {
+ return this.$store.getters['firmware/meFirmware'];
+ },
+ fcbCpld() {
+ return this.$store.getters['firmware/fcbCpldFirmware'];
+ },
+ fpga() {
+ return this.$store.getters['firmware/fpgaFirmware'];
+ },
serverStatus() {
return this.$store.getters['global/serverStatus'];
},
@@ -81,11 +143,28 @@ export default {
isSingleFileUploadEnabled() {
return this.$store.getters['firmware/isSingleFileUploadEnabled'];
},
+ isOperationInProgress() {
+ return this.$store.getters['controls/isOperationInProgress'];
+ },
+ isReadOnly() {
+ return this.$store.getters['global/userPrivilege'] === 'ReadOnly';
+ },
isPageDisabled() {
if (this.isServerPowerOffRequired) {
- return !this.isServerOff || this.loading || this.isOperationInProgress;
+ return (
+ !this.isServerOff ||
+ this.loading ||
+ this.isOperationInProgress ||
+ this.isFirmwareProgress ||
+ this.isReadOnly
+ );
}
- return this.loading || this.isOperationInProgress;
+ return (
+ this.loading ||
+ this.isOperationInProgress ||
+ this.isFirmwareProgress ||
+ this.isReadOnly
+ );
},
cpld() {
return this.$store.getters['firmware/cpldFirmware'];
@@ -93,9 +172,22 @@ export default {
},
created() {
this.startLoader();
+ this.$root.$on('set-upload-event', (evt, isUploaded) => {
+ this.isUploaded = isUploaded;
+ this.loading = evt;
+ });
+ this.$root.$on('set-update-event', (evt) => {
+ this.loading = evt;
+ });
this.$store
.dispatch('firmware/getFirmwareInformation')
.finally(() => this.endLoader());
},
};
</script>
+
+<style lang="scss" scoped>
+.upload-cell {
+ padding-bottom: $spacer * 1;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareCardsBmc.vue b/src/views/Operations/Firmware/FirmwareCardsBmc.vue
index 8765f77..37c9cf8 100644
--- a/src/views/Operations/Firmware/FirmwareCardsBmc.vue
+++ b/src/views/Operations/Firmware/FirmwareCardsBmc.vue
@@ -10,6 +10,8 @@
</p>
</template>
<dl class="mb-0">
+ <dd>{{ priorityFirmware }}</dd>
+
<dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
<dd class="mb-0">{{ runningVersion }}</dd>
</dl>
@@ -28,6 +30,8 @@
</p>
</template>
<dl>
+ <dd>{{ backupFirmware }}</dd>
+
<dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
<dd>
<status-icon v-if="showBackupImageStatus" status="danger" />
@@ -82,6 +86,26 @@ export default {
};
},
computed: {
+ priorityFirmware() {
+ const pri = this.$store.getters['firmware/priorityFirmware'];
+ if (pri?.Priority === 0) {
+ return this.$t('pageFirmware.primaryImage');
+ }
+ if (pri?.Priority === 1) {
+ return this.$t('pageFirmware.secondaryImage');
+ }
+ return '';
+ },
+ backupFirmware() {
+ const pri = this.$store.getters['firmware/priorityFirmware'];
+ if (pri?.Priority === 0) {
+ return this.$t('pageFirmware.secondaryImage');
+ }
+ if (pri?.Priority === 1) {
+ return this.$t('pageFirmware.primaryImage');
+ }
+ return '';
+ },
isSingleFileUploadEnabled() {
return this.$store.getters['firmware/isSingleFileUploadEnabled'];
},
@@ -114,6 +138,9 @@ export default {
this.backupStatus === 'Critical' || this.backupStatus === 'Warning'
);
},
+ backupReleaseDate() {
+ return this.backup?.reldate || '--';
+ },
},
methods: {
switchToRunning() {
diff --git a/src/views/Operations/Firmware/FirmwareCardsFPGA.vue b/src/views/Operations/Firmware/FirmwareCardsFPGA.vue
new file mode 100755
index 0000000..f1f5ddd
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareCardsFPGA.vue
@@ -0,0 +1,40 @@
+<template>
+ <page-section :section-title="$t('pageFirmware.sectionTitleFPGACards')">
+ <b-card-group deck>
+ <!-- Running image -->
+ <b-card>
+ <template #header>
+ <p class="font-weight-bold m-0">
+ {{ $t('pageFirmware.cardTitleRunning') }}
+ </p>
+ </template>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd class="mb-0">{{ runningVersion }}</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['firmware/fpgaFirmware'];
+ },
+ runningVersion() {
+ return this.running?.version || '--';
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-section {
+ margin-top: -$spacer * 1.5;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue b/src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue
new file mode 100755
index 0000000..79294d5
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue
@@ -0,0 +1,40 @@
+<template>
+ <page-section :section-title="$t('pageFirmware.sectionTitleFcbCPLDCards')">
+ <b-card-group deck>
+ <!-- Running image -->
+ <b-card>
+ <template #header>
+ <p class="font-weight-bold m-0">
+ {{ $t('pageFirmware.cardTitleRunning') }}
+ </p>
+ </template>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd class="mb-0">{{ runningVersion }}</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['firmware/fcbCpldFirmware'];
+ },
+ runningVersion() {
+ return this.running?.version || '--';
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-section {
+ margin-top: -$spacer * 1.5;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareCardsMe.vue b/src/views/Operations/Firmware/FirmwareCardsMe.vue
new file mode 100755
index 0000000..8828463
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareCardsMe.vue
@@ -0,0 +1,40 @@
+<template>
+ <page-section :section-title="$t('pageFirmware.sectionTitleMeCards')">
+ <b-card-group deck>
+ <!-- Running image -->
+ <b-card>
+ <template #header>
+ <p class="font-weight-bold m-0">
+ {{ $t('pageFirmware.cardTitleRunning') }}
+ </p>
+ </template>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+ <dd class="mb-0">{{ runningVersion }}</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['firmware/meFirmware'];
+ },
+ runningVersion() {
+ return this.running?.version || '--';
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-section {
+ margin-top: -$spacer * 1.5;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue b/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue
index 1835521..79ee979 100644
--- a/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue
+++ b/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue
@@ -6,6 +6,9 @@
:cancel-title="$t('global.action.cancel')"
@ok="$emit('ok')"
>
+ <p v-if="uploadFirmwareType === 'BMC' && firmwareLength > 1">
+ {{ $t('pageFirmware.toast.bmcUpdateTips') }}
+ </p>
<template v-if="isSingleFileUploadEnabled">
<p>
{{ $t('pageFirmware.modal.updateFirmwareInfo') }}
@@ -21,6 +24,22 @@
{{ $t('pageFirmware.modal.updateFirmwareInfo3') }}
</p>
</template>
+ <template
+ v-else-if="
+ uploadFirmwareType == 'BIOS' ||
+ uploadFirmwareType == 'CPLD' ||
+ uploadFirmwareType == 'FPGA' ||
+ uploadFirmwareType == 'HSBP' ||
+ uploadFirmwareType == 'FCB'
+ "
+ >
+ {{ $t('pageFirmware.modal.updateFirmwareInfoOem') }}
+ </template>
+ <template
+ v-else-if="uploadFirmwareType && uploadFirmwareType.indexOf('BMC') !== -1"
+ >
+ {{ $t('pageFirmware.modal.updateFirmwareInfoOemBmc') }}
+ </template>
<template v-else>
{{ $t('pageFirmware.modal.updateFirmwareInfoDefault') }}
</template>
@@ -29,6 +48,16 @@
<script>
export default {
+ props: {
+ uploadFirmwareType: {
+ type: String,
+ default: '',
+ },
+ firmwareLength: {
+ type: Number,
+ default: 0,
+ },
+ },
computed: {
runningBmc() {
return this.$store.getters['firmware/activeBmcFirmware'];
diff --git a/src/views/Operations/Firmware/FirmwareUpdateStatus.vue b/src/views/Operations/Firmware/FirmwareUpdateStatus.vue
new file mode 100755
index 0000000..e2fd859
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareUpdateStatus.vue
@@ -0,0 +1,822 @@
+<template>
+ <b-overlay
+ z-index="2000"
+ class="firmware-overlay"
+ :show="showOverlay"
+ :opacity="opacity"
+ spinner-type="none"
+ >
+ <b-container
+ class="firmware-update-container"
+ fluid="xl"
+ style="max-width: 1440px"
+ >
+ <b-row>
+ <b-col xl="10" style="margin-bottom: 30px">
+ <div class="d-flex justify-content-between align-items-center">
+ <upload-file
+ v-model="file"
+ required
+ :disabled="isPageDisabled"
+ :validted-file="validtedFileType"
+ @submit="uploadImage"
+ ></upload-file>
+ <b-button
+ v-if="!hasUploadedBmc"
+ :disabled="
+ runningOnSecondaryContainBMC ||
+ isPageDisabled ||
+ !allFirmwareStatus.some((it) => it.UpdateStatus === 'Uploaded')
+ "
+ variant="primary"
+ size="sm"
+ class="btn-file"
+ style="flex-shrink: 0; margin-left: 50px; height: 35px"
+ @click="updateAll"
+ >{{ $t('pageFirmware.form.updateFirmware.updateAll') }}</b-button
+ >
+ </div>
+ </b-col>
+ <b-col xl="10">
+ <b-table
+ show-empty
+ :empty-text="$t('pageFirmware.table.emptyMessage')"
+ :fields="fields"
+ striped
+ hover
+ :items="allFirmwareStatus"
+ >
+ <template #row-details="row">
+ <div v-if="row.item.name === 'BMC'">
+ <b-form-group
+ :label="$t('pageFirmware.form.updateFirmware.options')"
+ >
+ <div class="d-flex">
+ <b-form-radio-group
+ v-model="bmcConfig"
+ style="margin-right: 40px"
+ :disabled="bmcConfigDisabled"
+ :options="getUpdateOptions('BMC')"
+ stacked
+ ></b-form-radio-group>
+ <b-form-radio-group
+ v-model="bmcConfig2"
+ :disabled="isPageDisabled"
+ :options="bmcConfigOptions"
+ stacked
+ ></b-form-radio-group>
+ </div>
+ </b-form-group>
+ </div>
+ <div v-if="row.item.name === 'BIOS'">
+ <b-form-group
+ :label="$t('pageFirmware.form.updateFirmware.options')"
+ :disabled="isPageDisabled"
+ >
+ <b-form-radio-group
+ v-model="biosConfig2"
+ :disabled="isPageDisabled"
+ :options="biosConfigOptions"
+ stacked
+ ></b-form-radio-group>
+ </b-form-group>
+ </div>
+ <div v-if="row.item.name === 'Retimer'">
+ <b-form-group
+ :label="$t('pageFirmware.form.updateFirmware.options')"
+ :disabled="isPageDisabled"
+ style="width: 200px"
+ >
+ <b-form-select
+ v-model="retimerConfig"
+ :options="retimerOptions"
+ ></b-form-select>
+ </b-form-group>
+ </div>
+ </template>
+ <template #cell(status)="{ item }">
+ <div
+ v-if="item.UpdateStatus === 'Updating'"
+ class="d-flex align-items-center"
+ >
+ <div style="width: 140px">
+ <b-progress style="border-radius: 20px" :max="100">
+ <b-progress-bar
+ :value="item.UpdateProgress"
+ ></b-progress-bar>
+ </b-progress>
+ </div>
+ <div>
+ {{ updatingBmcPartitionName(item) }}
+ {{ item.UpdateStatus }}-{{ item.UpdateProgress }}%
+ </div>
+ </div>
+ <div
+ v-else-if="item.UpdateStatus === 'Updated'"
+ class="d-flex align-items-center"
+ >
+ {{ updatingBmcPartitionName(item) }}
+ {{
+ updatingBmcPartitionName(item) !== 'Updating'
+ ? ' Updated'
+ : ''
+ }}
+ </div>
+ <div v-else class="d-flex align-items-center">
+ <div
+ v-if="item.UpdateStatus === 'Conflicted'"
+ class="mr-1"
+ :title="
+ $t('pageFirmware.toast.errorUpdateInPeerNodeUpdating')
+ "
+ >
+ <status-icon status="danger" />
+ </div>
+ {{ item.UpdateStatus }}
+ </div>
+ </template>
+ <template #cell(version)="{ item }">{{
+ item.VersionStatus
+ }}</template>
+ <template #cell(Action)="{ item }">
+ <link-button
+ :disabled="isPageDisabled || item.UpdateStatus !== 'Uploaded'"
+ @click="handleUpdate(item)"
+ >{{ $t('pageFirmware.table.update') }}</link-button
+ >
+ <link-button
+ :disabled="
+ isPageDisabled ||
+ ['Updated', 'Updating'].includes(item.UpdateStatus)
+ "
+ danger
+ @click="handleDelete(item)"
+ >{{ $t('pageFirmware.table.delete') }}</link-button
+ >
+ </template>
+ </b-table></b-col
+ ></b-row
+ >
+ <modal-update-firmware
+ :upload-firmware-type="updatingFirmware && updatingFirmware.name"
+ :firmware-length="allFirmwareStatus && allFirmwareStatus.length"
+ @ok="handleUpdateFirmware"
+ />
+ </b-container>
+ </b-overlay>
+</template>
+
+<script>
+// import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+// import IconChevron from '@carbon/icons-vue/es/chevron--down/20';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+import LinkButton from '@/components/Global/LinkButton';
+import UploadFile from '@/components/Global/UploadFile';
+import ModalUpdateFirmware from './FirmwareModalUpdateFirmware';
+import { BOverlay } from 'bootstrap-vue';
+import { bus } from '@/utilities/bus';
+// import TableRowExpandMixin, {
+// expandRowLabel,
+// } from '@/components/Mixins/TableRowExpandMixin';
+export default {
+ components: {
+ // IconTrashcan,
+ LinkButton,
+ UploadFile,
+ ModalUpdateFirmware,
+ BOverlay,
+ // IconChevron,
+ },
+ mixins: [BVToastMixin, LoadingBarMixin],
+ props: {
+ isPageDisabled: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ console,
+ file: null,
+ fileErrorTips: '',
+ bmcConfig: 'Keep',
+ bmcConfig2: 'BMC',
+ biosConfig: 'Keep',
+ biosConfig2: 'BIOS',
+ retimerConfig: 'Retimer0',
+ isKeepConfigs: true,
+ updatingFirmware: null,
+ getStatustimerId: undefined,
+ isNeedReboot: false,
+ showOverlay: false,
+ opacity: 0,
+ // expandRowLabel,
+ fields: [
+ {
+ key: 'expandRow',
+ label: '',
+ tdClass: 'table-row-expand',
+ },
+ {
+ key: 'name',
+ label: this.$t('pageFirmware.table.firmwareName'),
+ },
+ {
+ key: 'status',
+ label: this.$t('pageFirmware.table.firmwareStatus'),
+ },
+ {
+ key: 'version',
+ label: this.$t('pageFirmware.table.firmwareVersion'),
+ },
+ {
+ key: 'Action',
+ label: '',
+ },
+ ],
+ retimerOptions: [
+ 'Retimer0',
+ 'Retimer1',
+ 'Retimer2',
+ 'Retimer3',
+ 'Retimer4',
+ 'Retimer5',
+ 'Retimer6',
+ 'Retimer7',
+ 'All',
+ ],
+ };
+ },
+ computed: {
+ allFirmwareStatus() {
+ const allFirmware = this.$store.getters['firmware/allFirmwareStatus'];
+
+ return (
+ allFirmware?.reduce((res, item) => {
+ const [temp] = Object.entries(item);
+ res.push({
+ name: temp[0],
+ ...temp[1],
+ _showDetails:
+ temp[1].UpdateStatus === 'Uploaded' &&
+ (temp[0] === 'BMC' ||
+ temp[0] === 'BIOS' ||
+ temp[0] === 'Retimer'),
+ });
+ return res;
+ }, []) || []
+ );
+ },
+ priorityFirmware() {
+ const pri = this.$store.getters['firmware/priorityFirmware'];
+ return pri?.Priority ?? null;
+ },
+ bmcConfigDisabled() {
+ return (
+ this.isPageDisabled ||
+ this.flagForBmcConfig ||
+ this.flagForPrimaryBmcConfig
+ );
+ },
+ flagForBmcConfig() {
+ return this.priorityFirmware === 1 && this.bmcConfig2 === 'BMC';
+ },
+ flagForPrimaryBmcConfig() {
+ return this.priorityFirmware === 0 && this.bmcConfig2 === 'BMC_Secondary';
+ },
+ // Upgrade the primary from the primary or upgrade the backup from the backup.
+ isPrimaryToPrimaryOrSecondaryToSecondary() {
+ return (
+ (this.priorityFirmware === 0 && this.bmcConfig2 === 'BMC') ||
+ (this.priorityFirmware === 1 && this.bmcConfig2 === 'BMC_Secondary')
+ );
+ },
+ // bmcIsUpdatingfCurrentPartition
+ bmcIsUpdatingfCurrentPartition() {
+ return this.$store.getters['firmware/bmcIsUpdatingfCurrentPartition'];
+ },
+ runningOnSecondaryContainBMC() {
+ if (
+ this.allFirmwareStatus.find(
+ (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BMC'
+ )
+ ) {
+ if (this.bmcConfig2 === 'BMC' || this.bmcConfig2 === 'BMC_Secondary') {
+ return this.priorityFirmware === 1;
+ }
+ return false;
+ }
+ return false;
+ },
+ bmcConfigOptions() {
+ const opts = [
+ {
+ text: this.$t('pageFirmware.primaryImage'),
+ value: 'BMC',
+ },
+ {
+ text: this.$t('pageFirmware.secondaryImage'),
+ value: 'BMC_Secondary',
+ },
+ {
+ text: this.$t('pageFirmware.both'),
+ value: 'Both',
+ },
+ ];
+ const firmwareStatus =
+ this.$store.getters['firmware/allFirmwareStatus'] || [];
+ const bothEnabled = firmwareStatus.some(
+ (it) => it.BMC?.BothUpdateEnable === true
+ );
+ if (!bothEnabled) {
+ opts.splice(2, 1);
+ }
+ return this.priorityFirmware === 1 ? opts.slice(0, 1) : opts;
+ },
+ biosConfigOptions() {
+ const opts = [
+ {
+ text: this.$t('pageFirmware.primaryImage'),
+ value: 'BIOS',
+ },
+ {
+ text: this.$t('pageFirmware.secondaryImage'),
+ value: 'BIOS_Secondary',
+ },
+ {
+ text: this.$t('pageFirmware.both'),
+ value: 'Both',
+ },
+ ];
+ const firmwareStatus =
+ this.$store.getters['firmware/allFirmwareStatus'] || [];
+ const bothEnabled = firmwareStatus.some(
+ (it) => it.BIOS?.BothUpdateEnable === true
+ );
+ if (!bothEnabled) {
+ opts.splice(2, 1);
+ }
+ return opts;
+ },
+ hasUploadedBmc() {
+ return this.allFirmwareStatus.find(
+ (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BMC'
+ );
+ },
+ },
+ watch: {
+ flagForBmcConfig: {
+ immediate: true,
+ handler(newVal) {
+ if (newVal) this.bmcConfig = 'FactoryReset';
+ },
+ },
+ flagForPrimaryBmcConfig: {
+ immediate: true,
+ handler(newVal) {
+ if (newVal) this.bmcConfig = 'Keep';
+ },
+ },
+ },
+ created() {
+ // this.$store.dispatch('firmware/getAllUpdateStatus');
+ this.getFirmwareStatus();
+ setTimeout(() => {
+ this.getFirmwareStatusTimer();
+ }, 5000);
+ },
+ beforeDestroy() {
+ clearTimeout(this.getStatustimerId);
+ this.getStatustimerId = null;
+ },
+ methods: {
+ updatingBmcPartitionName(item) {
+ if (item.name !== 'BMC' || !this.$store.state.firmware.bmcIsUpdating)
+ return '';
+ // When upgrading BMC and BMC's Update progress is null and Update Status is Updated, Updating is displayed
+ if (
+ item.UpdateStatus === 'Updated' &&
+ this.$store.state.firmware.bmcIsUpdating &&
+ item.UpdateProgress === null
+ ) {
+ return 'Updating';
+ }
+ const currentPartition =
+ this.priorityFirmware === 0 ? 'Primary' : 'Secondary';
+ const map = {
+ Primary: 'Secondary',
+ Secondary: 'Primary',
+ };
+ if (this.bmcIsUpdatingfCurrentPartition) {
+ return currentPartition;
+ } else {
+ return map[currentPartition];
+ }
+ },
+ // get FirmwareStatus
+ getFirmwareStatus() {
+ this.loadingVisible(true);
+ this.$store.dispatch('firmware/getAllUpdateStatus').finally(() => {
+ this.loadingVisible(false);
+ });
+ },
+ // timer
+ async getFirmwareStatusTimer() {
+ try {
+ await this.$store.dispatch('firmware/getAllUpdateStatus');
+ } finally {
+ if (this.getStatustimerId !== null) {
+ this.getStatustimerId = setTimeout(() => {
+ this.getFirmwareStatusTimer();
+ }, 5000);
+ }
+ }
+ },
+ loadingVisible(bool) {
+ bool ? this.startLoader() : this.endLoader();
+ this.$root.$emit('set-upload-event', bool);
+ },
+ getUpdateOptions() {
+ const all = [
+ {
+ text: this.$t('pageFirmware.form.updateOptions.keep'),
+ value: 'Keep',
+ },
+ {
+ text: this.$t('pageFirmware.form.updateOptions.clear'),
+ value: 'Clear',
+ },
+ {
+ text: this.$t('pageFirmware.form.updateOptions.partialKeep'),
+ value: 'PartialKeep',
+ },
+ {
+ text: this.$t('pageFirmware.form.updateOptions.factoryReset'),
+ value: 'FactoryReset',
+ },
+ ];
+ // return type === 'BMC' ? all : all.slice(0, 2);
+ return all.slice(0, 2);
+ },
+ uploadImage() {
+ this.loadingVisible(true);
+ const timerId = setTimeout(() => {
+ this.loadingVisible(false);
+ this.errorToast(this.$t('pageFirmware.toast.timeoutUploadMessage'), {
+ title: this.$t('pageFirmware.toast.timeoutUpload'),
+ });
+ }, 1000000);
+ const toastId = 'upload-started-toast' + Date.now();
+ this.infoToast(this.$t('pageFirmware.toast.uploadStartedMessage'), {
+ title: this.$t('pageFirmware.toast.uploadStarted'),
+ timestamp: true,
+ id: toastId,
+ });
+ this.dispatchUpload(timerId, toastId);
+ },
+ dispatchUpload(timerId, toastId) {
+ console.log('file', this.file);
+ this.$store
+ .dispatch('firmware/uploadFirmwareOEM', this.file)
+ .then((res) => {
+ setTimeout(() => {
+ this.$bvToast.hide(toastId);
+ }, 10000);
+ this.file = null;
+ if (!res) return;
+ const oemData = res?.data?.Oem?.Luxshare?.[0];
+ const firmwareKey = oemData ? Object.keys(oemData)[0] : '';
+ const versionStatus = oemData
+ ? oemData[firmwareKey]?.VersionStatus
+ : '';
+ const uploadStatus = oemData
+ ? oemData[firmwareKey]?.UpdateStatus
+ : '';
+ if (uploadStatus === 'Failed') {
+ let errorToast = this.$t('pageFirmware.toast.errorUploadFirmware');
+ if (versionStatus === 'Invalid') {
+ errorToast = this.$t(
+ 'pageFirmware.toast.validateFirmwareFailed',
+ {
+ firmware: firmwareKey,
+ }
+ );
+ } else if (versionStatus === 'Mismatch') {
+ errorToast = this.$t('pageFirmware.toast.firmwareIdMismatch', {
+ firmware: firmwareKey,
+ });
+ }
+ this.errorToast(errorToast);
+ } else {
+ this.successToast(
+ this.$t('pageFirmware.toast.uploadSuccessMessage'),
+ {
+ title: this.$t('pageFirmware.toast.uploadSuccess'),
+ timestamp: true,
+ }
+ );
+ }
+ this.getFirmwareStatus();
+ })
+ .catch(({ message }) => {
+ this.errorToast(message);
+ })
+ .finally(() => {
+ this.loadingVisible(false);
+ clearTimeout(timerId);
+ });
+ },
+ validtedFileType(file) {
+ return new Promise((resolve, reject) => {
+ const fileExtension = file.name.split('.').pop().toLowerCase();
+ if (fileExtension === 'tar') {
+ resolve('');
+ return;
+ }
+ if (fileExtension === 'gz' || fileExtension === 'tgz') {
+ resolve('');
+ return;
+ }
+
+ const temporaryFileReader = new FileReader();
+ temporaryFileReader.onerror = () => {
+ temporaryFileReader.abort();
+ reject(this.$t('pageFirmware.form.invalidFiletype'));
+ };
+ temporaryFileReader.onload = (e) => {
+ const arr = new Uint8Array(e.target.result).subarray(0, 4);
+ let header = '';
+ for (let i = 0; i < arr.length; i++) {
+ header += arr[i].toString(16);
+ }
+ let type = null;
+ switch (header) {
+ case '504b34':
+ type = 'application/x-zip-compressed';
+ break;
+ case '1f8b88':
+ type = 'application/x-gzip';
+ break;
+ default:
+ type = 'unknown';
+ break;
+ }
+ const validateFiletype = [
+ 'application/x-gzip',
+ 'application/x-zip-compressed',
+ 'application/x-tar',
+ ];
+ if (validateFiletype.includes(type)) {
+ resolve('');
+ } else {
+ reject(this.$t('pageFirmware.form.invalidFiletype'));
+ }
+ };
+ temporaryFileReader.readAsArrayBuffer(file);
+ });
+ },
+ formatStatus(item) {
+ if (item.UpdateStatus === 'Updating') {
+ // return <b-progress variant="primary"></b-progress>;
+ return 'Updating - ' + item.UpdateProgress + '%';
+ } else return item.UpdateStatus;
+ },
+ handleUpdate(item) {
+ this.updatingFirmware = item;
+ this.$bvModal.show('modal-update-firmware');
+ },
+ getAllUpdateAction() {
+ if (
+ this.allFirmwareStatus.find(
+ (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BMC'
+ )
+ ) {
+ return this.bmcConfig;
+ } else if (
+ this.allFirmwareStatus.find(
+ (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BIOS'
+ )
+ ) {
+ return this.biosConfig;
+ } else {
+ return undefined;
+ }
+ },
+ getTarget() {
+ if (this.updatingFirmware.name === 'BMC') {
+ return this.bmcConfig2;
+ } else if (this.updatingFirmware.name === 'BIOS') {
+ return this.biosConfig2;
+ } else if (this.updatingFirmware.name === 'Retimer') {
+ return this.retimerConfig;
+ } else {
+ return this.updatingFirmware.name;
+ }
+ },
+ checkIsNeedReboot(isAll) {
+ // Single update: it's BMC and it upgrades the primary from the primary or the backup from the backup.
+ // it's BMC and the Both is selected.
+ return (
+ !isAll &&
+ this.updatingFirmware?.name === 'BMC' &&
+ (this.bmcConfig2 === 'Both' ||
+ this.isPrimaryToPrimaryOrSecondaryToSecondary)
+ );
+ },
+ handleUpdateFirmware(isAll) {
+ this.isNeedReboot = this.checkIsNeedReboot(isAll);
+ if (
+ !isAll &&
+ this.updatingFirmware.name === 'BMC' &&
+ this.bmcConfig2 === 'Both'
+ ) {
+ isAll = true;
+ }
+ if (
+ !isAll &&
+ this.updatingFirmware.name === 'BIOS' &&
+ this.biosConfig2 === 'Both'
+ ) {
+ isAll = true;
+ }
+ this.loadingVisible(true);
+ const timerId = setTimeout(() => {
+ const message = this.$t(
+ 'pageFirmware.form.updateFirmware.updateTimeout'
+ );
+ this.updateInfoFail(timerId, message);
+ }, 1500000);
+ this.infoToast(this.$t('pageFirmware.toast.updateStartedMessage'), {
+ title: this.$t('pageFirmware.toast.updateStarted'),
+ timestamp: true,
+ });
+ let updateConfigs;
+ if (isAll) {
+ updateConfigs = {
+ Target: 'All',
+ Action: this.getAllUpdateAction(),
+ };
+ } else {
+ updateConfigs = {
+ Target: this.getTarget(),
+ Action:
+ this.updatingFirmware.name.search(/BIOS|BMC/) !== -1
+ ? this[`${this.updatingFirmware.name.toLowerCase()}Config`]
+ : undefined,
+ };
+ }
+
+ this.$store
+ .dispatch('firmware/startUpdate', updateConfigs)
+ .then((res) => {
+ if (!res) return;
+ if (this.isNeedReboot) {
+ this.$store.commit('firmware/setBmcIsUpdating', true);
+ this.warningToast(
+ this.$t('pageFirmware.toast.bmcUpdatingTips'),
+ {
+ toaster: 'b-toaster-top-center',
+ id: 'bmcUpdatingTips',
+ },
+ true
+ );
+ bus.$on('bmcUpdateComplete', () => {
+ this.$bvToast.hide();
+ this.successToast(
+ this.$t('pageFirmware.toast.bmcUpdateCompleteTips'),
+ {
+ toaster: 'b-toaster-top-center',
+ },
+ true
+ );
+ this.opacity = 0.1;
+ });
+ let flag = false;
+ bus.$on('updateCurrentPartition', () => {
+ if (flag) return;
+ flag = true;
+ clearTimeout(this.getStatustimerId);
+ this.getStatustimerId = null;
+ this.$store.commit('firmware/setBmcUpdateProgress', 0);
+ this.$store.commit(
+ 'firmware/setBmcIsUpdatingfCurrentPartition',
+ true
+ );
+ this.$store.dispatch('firmware/getBmcUpdateStatus');
+ });
+ this.showOverlay = true;
+ }
+ this.updateInfoSuccess(timerId, isAll);
+ })
+ .catch((e) => {
+ const errorCode = e?.response?.status;
+ let errMsg;
+ if (errorCode === 405 && isAll) {
+ errMsg = this.$t('pageFirmware.toast.errorUpdateAsyncAndSync');
+ } else if (errorCode === 503) {
+ errMsg = this.$t('pageFirmware.toast.errorUpdateInPost');
+ } else if (errorCode === 409) {
+ errMsg = this.$t(
+ 'pageFirmware.toast.errorUpdateInPeerNodeUpdating'
+ );
+ } else {
+ errMsg = this.$t('pageFirmware.toast.errorUpdateFirmware');
+ }
+ this.updateInfoFail(undefined, errMsg);
+ })
+ .finally(() => {
+ clearTimeout(timerId);
+ this.loadingVisible(false);
+ });
+ },
+ async updateAll() {
+ const isBMCUploaded = this.allFirmwareStatus.some(
+ (it) => it.name === 'BMC' && it.UpdateStatus === 'Uploaded'
+ );
+ const tips =
+ isBMCUploaded && this.allFirmwareStatus.length > 1
+ ? this.$t('pageFirmware.toast.bmcUpdateTips')
+ : null;
+ const confirmTitle = this.$t('pageFirmware.modal.updateAllConfirm');
+
+ try {
+ const value = tips
+ ? await this.$bvModal.msgBoxConfirm(tips, { title: confirmTitle })
+ : await this.$bvModal.msgBoxConfirm(confirmTitle);
+
+ if (value) {
+ this.handleUpdateFirmware(true);
+ }
+ } catch (error) {
+ console.error('Update all error', error);
+ }
+ },
+ updateInfoSuccess(isAll) {
+ this.uploadResultMessage = !isAll
+ ? this.$t('pageFirmware.form.updateFirmware.updateFinished', {
+ type: this.updatingFirmware.name,
+ version: this.updatingFirmware.VersionStatus,
+ })
+ : this.$t('pageFirmware.table.updateComplete');
+ this.infoToast(this.$t('pageFirmware.toast.verifyUpdateMessage'), {
+ title: this.$t('pageFirmware.toast.verifyUpdate'),
+ refreshAction: true,
+ });
+ },
+ updateInfoFail(timerId, message) {
+ this.errorToast(message);
+ timerId && this.loadingVisible(false);
+ timerId && clearTimeout(timerId);
+ },
+ handleDelete(item) {
+ this.$bvModal
+ .msgBoxConfirm(
+ this.$t('pageFirmware.modal.deleteConfirm', {
+ firmware: item.name,
+ })
+ )
+ .then((value) => {
+ if (value) {
+ this.loadingVisible(true);
+ this.$store
+ .dispatch('firmware/deleteFirmware', item.name)
+ .then(() => {
+ this.successToast(
+ this.$t('pageFirmware.toast.firmwareDeleted', {
+ firmware: item.name,
+ }),
+ {
+ title: this.$t('pageFirmware.toast.deleteSuccess'),
+ }
+ );
+ clearTimeout(this.getStatustimerId);
+ this.getStatustimerId = null;
+ this.getFirmwareStatusTimer().finally(() => {
+ this.loadingVisible(false);
+ });
+ })
+ .catch(() => {
+ this.loadingVisible(false);
+ this.errorToast(
+ this.$t('pageFirmware.toast.deleteFailed', {
+ firmware: item.name,
+ })
+ );
+ });
+ }
+ });
+ },
+ },
+};
+</script>
+<style lang="scss" scoped>
+$btn-height: 33px;
+.btn-file {
+ height: $btn-height;
+ white-space: nowrap;
+}
+.firmware-overlay {
+ height: 100vh;
+}
+</style>
--
2.25.1