Update the default firmware page

- Minor updates made to the general layout and styles
  - Changes to some page copy
  - Moves update firmware form to bottom of page
- Adds dynamic TFTP upload option
- Adds dynamic card layout for BMC and host firmwre
  - 2 cards for combined
  - 4 cards for separate
- Removes FirmwareSingleImage components that were used for IBM builds

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: Ib5465ecc30dd1505824bf41c82d33b7655d5e598
diff --git a/src/store/modules/Configuration/FirmwareStore.js b/src/store/modules/Configuration/FirmwareStore.js
index 59a421d..3b4bd85 100644
--- a/src/store/modules/Configuration/FirmwareStore.js
+++ b/src/store/modules/Configuration/FirmwareStore.js
@@ -1,156 +1,123 @@
 import api from '@/store/api';
 import i18n from '@/i18n';
 
-/**
- * Get backup firmware image from SoftwareImages
- * The backup is whichever image is not the current
- * or "ActiveSoftwareImage"
- * @param {Array} list
- * @param {String} currentLocation
- */
-function getBackupFirmwareLocation(list, currentLocation) {
-  return list
-    .map((item) => item['@odata.id'])
-    .find((location) => {
-      const id = location.split('/').pop();
-      const currentId = currentLocation.split('/').pop();
-      return id !== currentId;
-    });
-}
-
 const FirmwareStore = {
   namespaced: true,
   state: {
-    bmcFirmware: {
-      currentVersion: null,
-      currentState: null,
-      currentLocation: null,
-      backupVersion: null,
-      backupState: null,
-      backupLocation: null,
-    },
-    hostFirmware: {
-      currentVersion: null,
-      currentState: null,
-      currentLocation: null,
-      backupVersion: null,
-      backupState: null,
-      backupLocation: null,
-    },
+    bmcFirmware: [],
+    hostFirmware: [],
+    bmcActiveFirmwareId: null,
+    hostActiveFirmwareId: null,
     applyTime: null,
+    tftpAvailable: false,
   },
   getters: {
-    bmcFirmwareCurrentVersion: (state) => state.bmcFirmware.currentVersion,
-    bmcFirmwareCurrentState: (state) => state.bmcFirmware.currentState,
-    bmcFirmwareBackupVersion: (state) => state.bmcFirmware.backupVersion,
-    bmcFirmwareBackupState: (state) => state.bmcFirmware.backupState,
-    hostFirmwareCurrentVersion: (state) => state.hostFirmware.currentVersion,
-    hostFirmwareCurrentState: (state) => state.hostFirmware.currentState,
-    hostFirmwareBackupVersion: (state) => state.hostFirmware.backupVersion,
-    hostFirmwareBackupState: (state) => state.hostFirmware.backupState,
+    isTftpUploadAvailable: (state) => state.tftpAvailable,
+    isSingleFileUploadEnabled: (state) => state.hostFirmware.length === 0,
+    activeBmcFirmware: (state) => {
+      return state.bmcFirmware.find(
+        (firmware) => firmware.id === state.bmcActiveFirmwareId
+      );
+    },
+    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
+      );
+    },
   },
   mutations: {
-    setBmcFirmwareCurrent: (state, { version, location, status }) => {
-      state.bmcFirmware.currentVersion = version;
-      state.bmcFirmware.currentState = status;
-      state.bmcFirmware.currentLocation = location;
-    },
-    setBmcFirmwareBackup: (state, { version, location, status }) => {
-      state.bmcFirmware.backupVersion = version;
-      state.bmcFirmware.backupState = status;
-      state.bmcFirmware.backupLocation = location;
-    },
-    setHostFirmwareCurrent: (state, { version, location, status }) => {
-      state.hostFirmware.currentVersion = version;
-      state.hostFirmware.currentState = status;
-      state.hostFirmware.currentLocation = location;
-    },
-    setHostFirmwareBackup: (state, { version, location, status }) => {
-      state.hostFirmware.backupVersion = version;
-      state.hostFirmware.backupState = status;
-      state.hostFirmware.backupLocation = location;
-    },
+    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({ dispatch }) {
-      return await api.all([
-        dispatch('getBmcFirmware'),
-        dispatch('getHostFirmware'),
-      ]);
+      dispatch('getActiveHostFirmware');
+      dispatch('getActiveBmcFirmware');
+      return await dispatch('getFirmwareInventory');
     },
-    async getBmcFirmware({ commit }) {
-      return await api
+    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 = getBackupFirmwareLocation(
-            Links.SoftwareImages,
-            currentLocation
-          );
-          return { currentLocation, backupLocation };
-        })
-        .then(async ({ currentLocation, backupLocation }) => {
-          const currentData = await api.get(currentLocation);
-          let backupData = {};
-
-          if (backupLocation) {
-            backupData = await api.get(backupLocation);
-          }
-
-          commit('setBmcFirmwareCurrent', {
-            version: currentData?.data?.Version,
-            location: currentData?.data?.['@odata.id'],
-            status: currentData?.data?.Status?.State,
-          });
-          commit('setBmcFirmwareBackup', {
-            version: backupData.data?.Version,
-            location: backupData.data?.['@odata.id'],
-            status: backupData.data?.Status?.State,
-          });
+          const id = Links?.ActiveSoftwareImage['@odata.id'].split('/').pop();
+          commit('setActiveBmcFirmwareId', id);
         })
         .catch((error) => console.log(error));
     },
-    async getHostFirmware({ commit }) {
-      return await api
+    getActiveHostFirmware({ commit }) {
+      return api
         .get('/redfish/v1/Systems/system/Bios')
         .then(({ data: { Links } }) => {
-          const currentLocation = Links.ActiveSoftwareImage['@odata.id'];
-          const backupLocation = getBackupFirmwareLocation(
-            Links.SoftwareImages,
-            currentLocation
-          );
-          return { currentLocation, backupLocation };
-        })
-        .then(async ({ currentLocation, backupLocation }) => {
-          const currentData = await api.get(currentLocation);
-          let backupData = {};
-
-          if (backupLocation) {
-            backupData = await api.get(backupLocation);
-          }
-
-          commit('setHostFirmwareCurrent', {
-            version: currentData?.data?.Version,
-            location: currentData?.data?.['@odata.id'],
-            status: currentData?.data?.Status?.State,
-          });
-          commit('setHostFirmwareBackup', {
-            version: backupData.data?.Version,
-            location: backupData.data?.['@odata.id'],
-            status: backupData.data?.Status?.State,
-          });
+          const id = Links?.ActiveSoftwareImage['@odata.id'].split('/').pop();
+          commit('setActiveHostFirmwareId', id);
         })
         .catch((error) => console.log(error));
     },
-    getUpdateServiceApplyTime({ commit }) {
+    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')
         .then(({ data }) => {
           const applyTime =
             data.HttpPushUriOptions.HttpPushUriApplyTime.ApplyTime;
+          const allowableActions =
+            data?.Actions?.['#UpdateService.SimpleUpdate']?.[
+              'TransferProtocol@Redfish.AllowableValues'
+            ];
+
           commit('setApplyTime', applyTime);
+          if (allowableActions.includes('TFTP')) {
+            commit('setTftpUploadAvailable', true);
+          }
         })
         .catch((error) => console.log(error));
     },
@@ -177,17 +144,15 @@
         .post('/redfish/v1/UpdateService', image, {
           headers: { 'Content-Type': 'application/octet-stream' },
         })
-        .then(() => dispatch('getSystemFirwareVersion'))
-        .then(() => i18n.t('pageFirmware.toast.successUploadMessage'))
         .catch((error) => {
           console.log(error);
-          throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot'));
+          throw new Error(i18n.t('pageFirmware.toast.errorUpdateFirmware'));
         });
     },
-    async uploadFirmwareTFTP({ state, dispatch }, { address, filename }) {
+    async uploadFirmwareTFTP({ state, dispatch }, fileAddress) {
       const data = {
         TransferProtocol: 'TFTP',
-        ImageURI: `${address}/${filename}`,
+        ImageURI: fileAddress,
       };
       if (state.applyTime !== 'Immediate') {
         // ApplyTime must be set to Immediate before making
@@ -199,15 +164,13 @@
           '/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate',
           data
         )
-        .then(() => dispatch('getSystemFirwareVersion'))
-        .then(() => i18n.t('pageFirmware.toast.successUploadMessage'))
         .catch((error) => {
           console.log(error);
-          throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot'));
+          throw new Error(i18n.t('pageFirmware.toast.errorUpdateFirmware'));
         });
     },
-    async switchBmcFirmware({ state }) {
-      const backupLocation = state.bmcFirmware.backupLocation;
+    async switchBmcFirmwareAndReboot({ getters }) {
+      const backupLocation = getters.backupBmcFirmware.location;
       const data = {
         Links: {
           ActiveSoftwareImage: {
@@ -217,10 +180,9 @@
       };
       return await api
         .patch('/redfish/v1/Managers/bmc', data)
-        .then(() => i18n.t('pageFirmware.toast.successRebootFromBackup'))
         .catch((error) => {
           console.log(error);
-          throw new Error(i18n.t('pageFirmware.toast.errorRebootFromBackup'));
+          throw new Error(i18n.t('pageFirmware.toast.errorSwitchImages'));
         });
     },
   },