From 64626ebac7529a4cc471781ed45f0782d1b0c79f Mon Sep 17 00:00:00 2001 From: roly 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 @@ + + + + + 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 @@ + + + + + + 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 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -34,17 +56,29 @@ + + + + + + + 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 @@

+
{{ priorityFirmware }}
+
{{ $t('pageFirmware.cardBodyVersion') }}
{{ runningVersion }}
@@ -28,6 +30,8 @@

+
{{ backupFirmware }}
+
{{ $t('pageFirmware.cardBodyVersion') }}
@@ -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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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')" > +

+ {{ $t('pageFirmware.toast.bmcUpdateTips') }} +

+ + @@ -29,6 +48,16 @@ + -- 2.25.1