From 401c2c09d179969ff24c253062303d6f0eeed370 Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:56:25 -0700 Subject: [PATCH] fix(service): only discover devices and add the devices if there are no devices in data store (#62) * fix(service): fix device discovery error handling during service starup (#59) * refactor(cli): command usage and example string generation refactor and update (#57) * refactor(cli): command usage and example string generation refactor and update * doc(cli): add docker-specific note about service-related cli options * feat(webui): device discovery in cfm webui (#58) * feat: set the nodes width based on the device id dimensions * feat: add ipaddress to the existed devices in dashboard * feat: add new button to discover new devices and the related interfaces * fix: fix the bug in addDiscoveredHosts interface * fix: fix bug in addDiscoveredBlades interface * feat: add function addDiscoveredDevices with output popup and waiting popup * style: separate lines for discovered blades and hosts * feat: add waiting progress for device discovery * feat: update the content after adding new discovered devices * feat: distinguish node status by border color * feat: prevent the user from manually adding blades to the CMA_Discovered_Blades appliance * style: remove unnecessary stype setup * fix(docker): update the docker running command to make D-bus available using privileged mode (#60) * feat: update the docker running command to make D-bus available ay priviledged mode * fix: update the docker running command to make D-bus available using privileged mode * feat: only discover devices and add the devices if there are no devices in data store * Update cmd/cfm-service/main.go Co-authored-by: Scott Howe Signed-off-by: Mengling Ding <71745861+Meng-20@users.noreply.github.com> * feat: check if there are any devices(blades or hosts) in the data store * refactor: change the customId for adding a discovered device * Fix: Simplify checking for existing blades * docs: update comments for the device name formatting * fix(webui): linter suggested fixes * fix: remove the redundant code --------- Signed-off-by: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Co-authored-by: Scott Howe --- cmd/cfm-service/main.go | 26 +- pkg/common/common.go | 4 +- services/discovery.go | 17 +- webui/src/components/Appliance/Memory.vue | 4 +- webui/src/components/Dashboard/Dashboard.vue | 6 +- webui/src/components/Stores/ApplianceStore.ts | 506 +++++++++-------- webui/src/components/Stores/HostStore.ts | 507 ++++++++++-------- 7 files changed, 588 insertions(+), 482 deletions(-) diff --git a/cmd/cfm-service/main.go b/cmd/cfm-service/main.go index 7a3502a..2ef87d4 100644 --- a/cmd/cfm-service/main.go +++ b/cmd/cfm-service/main.go @@ -68,17 +68,27 @@ func main() { defaultRedfishController := redfishapi.NewDefaultAPIController(defaultRedfishService) api.AddRedfishRouter(ctx, router, defaultRedfishController) - // Discover devices before loading datastore - bladeDevices, errBlade := services.DiscoverDevices(ctx, defaultApiService, "blade") - hostDevices, errHost := services.DiscoverDevices(ctx, defaultApiService, "cxl-host") - // Add the discovered devices into datastore - if errBlade == nil && errHost == nil { - services.AddDiscoveredDevices(ctx, defaultApiService, bladeDevices, hostDevices) - } - // Load datastore datastore.DStore().Restore() data := datastore.DStore().GetDataStore() + + // Check if there are any devices in the data store + bladeExist := len(data.ApplianceData) != 0 + hostExist := len(data.HostData) != 0 + + // If there are no devices in the data store, do discovery, otherwise skip + if !bladeExist && !hostExist { + // Discover devices before loading datastore + bladeDevices, errBlade := services.DiscoverDevices(ctx, defaultApiService, "blade") + hostDevices, errHost := services.DiscoverDevices(ctx, defaultApiService, "cxl-host") + // Add the discovered devices into datastore + if errBlade == nil && errHost == nil { + services.AddDiscoveredDevices(ctx, defaultApiService, bladeDevices, hostDevices) + } + // Update data + data = datastore.DStore().GetDataStore() + } + datastore.ReloadDataStore(ctx, defaultApiService, data) // Set up CORS middleware (for webui) diff --git a/pkg/common/common.go b/pkg/common/common.go index 98218ed..eaf3231 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -28,7 +28,7 @@ var DefaultBladeCredentials = &openapi.Credentials{ Port: 443, Insecure: true, Protocol: "https", - CustomId: "Discoverd_Blade_", + CustomId: "", } var DefaultHostCredentials = &openapi.Credentials{ @@ -38,5 +38,5 @@ var DefaultHostCredentials = &openapi.Credentials{ Port: 8082, Insecure: true, Protocol: "http", - CustomId: "Discoverd_Host_", + CustomId: "", } diff --git a/services/discovery.go b/services/discovery.go index 9a71542..442fef3 100644 --- a/services/discovery.go +++ b/services/discovery.go @@ -3,6 +3,7 @@ package services import ( "fmt" "log" + "strings" "golang.org/x/net/context" @@ -11,7 +12,7 @@ import ( "cfm/pkg/openapi" ) -// discoverDevices function to call the DiscoverDevices API +// DiscoverDevices function to call the DiscoverDevices API func DiscoverDevices(ctx context.Context, apiService openapi.DefaultAPIServicer, deviceType string) (openapi.ImplResponse, error) { resp, _ := apiService.DiscoverDevices(ctx, deviceType) if resp.Code >= 300 { @@ -35,7 +36,6 @@ func AddDiscoveredDevices(ctx context.Context, apiService openapi.DefaultAPIServ } // Add blades - // Convert data type bladeBodyBytes, ok := blades.Body.([]*openapi.DiscoveredDevice) if !ok { log.Fatalf("Response body is not []byte") @@ -46,8 +46,10 @@ func AddDiscoveredDevices(ctx context.Context, apiService openapi.DefaultAPIServ if !exist { newCredentials := *common.DefaultBladeCredentials newCredentials.IpAddress = bladeDevice.Address - //Assign the actual ipAddress to customId to ensure its uniqueness - newCredentials.CustomId = newCredentials.CustomId + bladeDevice.Address + + // Remove the .local suffix (e.g. blade device name: granite00.local) from the device name by splitting it with . and assign it to the customId + deviceName := strings.SplitN(bladeDevice.Name, ".", 2)[0] + newCredentials.CustomId = deviceName applianceDatum.AddBladeDatum(&newCredentials) datastore.DStore().Store() @@ -55,7 +57,6 @@ func AddDiscoveredDevices(ctx context.Context, apiService openapi.DefaultAPIServ } // Add cxl-hosts - // Convert data type hostBodyBytes, ok := hosts.Body.([]*openapi.DiscoveredDevice) if !ok { log.Fatalf("Response body is not []byte") @@ -65,8 +66,10 @@ func AddDiscoveredDevices(ctx context.Context, apiService openapi.DefaultAPIServ if !exist { newCredentials := *common.DefaultHostCredentials newCredentials.IpAddress = hostDevice.Address - //Assign the actual ipAddress to customId to ensure its uniqueness - newCredentials.CustomId = newCredentials.CustomId + hostDevice.Address + + // Remove the .local suffix (e.g. host device name: host00.local) from the device name by splitting it with . and assign it to the customId + deviceName := strings.SplitN(hostDevice.Name, ".", 2)[0] + newCredentials.CustomId = deviceName datastore.DStore().GetDataStore().AddHostDatum(&newCredentials) datastore.DStore().Store() diff --git a/webui/src/components/Appliance/Memory.vue b/webui/src/components/Appliance/Memory.vue index 62d7c32..bff8bc8 100644 --- a/webui/src/components/Appliance/Memory.vue +++ b/webui/src/components/Appliance/Memory.vue @@ -316,7 +316,7 @@ \ No newline at end of file + diff --git a/webui/src/components/Dashboard/Dashboard.vue b/webui/src/components/Dashboard/Dashboard.vue index 07aaad5..2f890b8 100644 --- a/webui/src/components/Dashboard/Dashboard.vue +++ b/webui/src/components/Dashboard/Dashboard.vue @@ -26,7 +26,7 @@ style="margin-bottom: 40px" variant="tonal" > - Click to discover new devices @@ -323,7 +323,7 @@ export default { if (this.selectedBlades.length === 0) { console.log("No blades selected."); } else { - for (var i = 0; i < this.selectedBlades.length; i++) { + for (let i = 0; i < this.selectedBlades.length; i++) { try { const newAddedBlade = await applianceStore.addDiscoveredBlades( this.selectedBlades[i] @@ -341,7 +341,7 @@ export default { if (this.selectedHosts.length === 0) { console.log("No hosts selected."); } else { - for (var i = 0; i < this.selectedHosts.length; i++) { + for (let i = 0; i < this.selectedHosts.length; i++) { try { const newAddedHost = await hostStore.addDiscoveredHosts( this.selectedHosts[i] diff --git a/webui/src/components/Stores/ApplianceStore.ts b/webui/src/components/Stores/ApplianceStore.ts index b1ad0d3..84c4e3b 100644 --- a/webui/src/components/Stores/ApplianceStore.ts +++ b/webui/src/components/Stores/ApplianceStore.ts @@ -1,237 +1,295 @@ // Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates -import { defineStore } from 'pinia' -import { Appliance, Credentials, DefaultApi, DiscoveredDevice } from "@/axios/api"; +import { defineStore } from "pinia"; +import { + Appliance, + Credentials, + DefaultApi, + DiscoveredDevice, +} from "@/axios/api"; import { BASE_PATH } from "@/axios/base"; -import axios from 'axios'; +import axios from "axios"; // Use API_BASE_PATH to overwrite the BASE_PATH in the generated client code const API_BASE_PATH = process.env.BASE_PATH || BASE_PATH; -export const useApplianceStore = defineStore('appliance', { - state: () => ({ - appliances: [] as Appliance[], - selectedApplianceId: null as unknown as string, - addApplianceError: null as unknown, - deleteApplianceError: null as unknown, - renameApplianceError: null as unknown, - applianceIds: [] as { id: string, blades: { id: string, ipAddress: string, status: string | undefined }[] }[], - discoveredBlades: [] as DiscoveredDevice[], - - prefixBladeId: "Discoverd_Blade_", - newBladeCredentials: { - username: "root", - password: "0penBmc", - ipAddress: "127.0.0.1", - port: 443, - insecure: true, - protocol: "https", - customId: "", - }, - - defaultApplianceId: "CMA_Discovered_Blades", - newApplianceCredentials: { - username: "root", - password: "0penBmc", - ipAddress: "127.0.0.1", - port: 8443, - insecure: true, - protocol: "https", - customId: "", - }, - }), - - actions: { - async renameAppliance(applianceId: string, newApplianceId: string) { - this.renameApplianceError = ""; - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.appliancesUpdateById(applianceId, newApplianceId); - - // Update the appliances array - if (response) { - this.appliances = this.appliances.filter( - (appliance) => appliance.id !== applianceId - ); - this.appliances.push(response.data); - } - - return response.data - } catch (error) { - if (axios.isAxiosError(error)) { - this.renameApplianceError = error.message; - - if (error.response) { - this.renameApplianceError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.renameApplianceError = error; - } - console.error("Error:", error); - } - }, - - async fetchAppliances() { - this.appliances = []; - this.applianceIds = []; - try { - // Get all appliances from OpenBMC - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const responseOfAppliances = await defaultApi.appliancesGet(); - const applianceCount = responseOfAppliances.data.memberCount; - - for (let i = 0; i < applianceCount; i++) { - // Extract the id for each appliance - const uri = responseOfAppliances.data.members[i]; - const applianceId: string = JSON.stringify(uri).split("/").pop()?.slice(0, -2) as string; - // Get appliance by id - const detailsResponseOfAppliance = await defaultApi.appliancesGetById( - applianceId - ); - - // Store appliance in appliances - if (detailsResponseOfAppliance) { - this.appliances.push(detailsResponseOfAppliance.data); - - const responseOfBlades = await defaultApi.bladesGet(applianceId); - const bladeCount = responseOfBlades.data.memberCount; - const associatedBlades = []; - - for (let i = 0; i < bladeCount; i++) { - // Extract the id for each blade - const uri = responseOfBlades.data.members[i]; - const bladeId: string = JSON.stringify(uri).split("/").pop()?.slice(0, -2) as string; - // Store blade in blades - if (bladeId) { - const responseOfBlade = await defaultApi.bladesGetById(applianceId, bladeId); - const response = { id: responseOfBlade.data.id, ipAddress: responseOfBlade.data.ipAddress, status: responseOfBlade.data.status } - associatedBlades.push(response); - } - } - this.applianceIds.push({ id: detailsResponseOfAppliance.data.id, blades: associatedBlades }); - } - } - } catch (error) { - console.error("Error fetching appliances:", error); - } - }, - - async discoverBlades() { - try { - // Get all the existed blades - const existedBladeIpAddress: (string | undefined)[] = [] - for (var i = 0; i < this.applianceIds.length; i++) { - for (var j = 0; j < this.applianceIds[i].blades.length; j++) { - existedBladeIpAddress.push(this.applianceIds[i].blades[j].ipAddress) - } - } - - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - this.discoveredBlades = []; - const responseOfBlade = await defaultApi.discoverDevices("blade"); - this.discoveredBlades = responseOfBlade.data; - - // Remove the existed blades from the discovered blades - for (var k = 0; k < this.discoveredBlades.length; k++) { - for (var m = 0; m < existedBladeIpAddress.length; m++) { - this.discoveredBlades = this.discoveredBlades.filter( - (discoveredBlade) => discoveredBlade.address !== existedBladeIpAddress[m] - ); - } - } - - return this.discoveredBlades - } catch (error) { - console.error("Error discovering new devices:", error); - } - }, - - async addDiscoveredBlades(blade: DiscoveredDevice) { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const responseOfApplianceExist = await defaultApi.appliancesGetById(this.defaultApplianceId) - - // If there is no default appliance, add one - if (!responseOfApplianceExist) { - this.newApplianceCredentials.customId = this.defaultApplianceId; - const responseOfAppliance = await defaultApi.appliancesPost(this.newApplianceCredentials); - - // Add the new appliance to the appliances and applianceIds array - if (responseOfAppliance) { - this.appliances.push(responseOfAppliance.data); - const newAppliance = { id: responseOfAppliance.data.id, blades: [] } - this.applianceIds.push(newAppliance) - } - } +export const useApplianceStore = defineStore("appliance", { + state: () => ({ + appliances: [] as Appliance[], + selectedApplianceId: null as unknown as string, + addApplianceError: null as unknown, + deleteApplianceError: null as unknown, + renameApplianceError: null as unknown, + applianceIds: [] as { + id: string; + blades: { id: string; ipAddress: string; status: string | undefined }[]; + }[], + discoveredBlades: [] as DiscoveredDevice[], - // Add the new discovered blade to the default appliance - let appliance = this.applianceIds.find(appliance => appliance.id === this.defaultApplianceId); + newBladeCredentials: { + username: "root", + password: "0penBmc", + ipAddress: "127.0.0.1", + port: 443, + insecure: true, + protocol: "https", + customId: "", + }, - this.newBladeCredentials.customId = this.prefixBladeId + blade.address; - this.newBladeCredentials.ipAddress = blade.address + ""; + defaultApplianceId: "CMA_Discovered_Blades", + newApplianceCredentials: { + username: "root", + password: "0penBmc", + ipAddress: "127.0.0.1", + port: 8443, + insecure: true, + protocol: "https", + customId: "", + }, + }), - const responseOfBlade = await defaultApi.bladesPost(this.defaultApplianceId, this.newBladeCredentials); + actions: { + async renameAppliance(applianceId: string, newApplianceId: string) { + this.renameApplianceError = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.appliancesUpdateById( + applianceId, + newApplianceId + ); - if (responseOfBlade) { - const response = { id: responseOfBlade.data.id, ipAddress: responseOfBlade.data.ipAddress, status: responseOfBlade.data.status }; - appliance!.blades.push(response); - } + // Update the appliances array + if (response) { + this.appliances = this.appliances.filter( + (appliance) => appliance.id !== applianceId + ); + this.appliances.push(response.data); + } - return responseOfBlade.data; - }, - - async addNewAppliance(newAppliance: Credentials) { - this.addApplianceError = ""; - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.appliancesPost(newAppliance); - const addedAppliance = response.data; - // Add the new appliance to the appliances array - this.appliances.push(addedAppliance); - return addedAppliance; - } catch (error) { - if (axios.isAxiosError(error)) { - this.addApplianceError = error.message; - if (error.response) { - this.addApplianceError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.addApplianceError = error; - } - console.error("Error:", error); - } - }, - - async deleteAppliance(applianceId: string) { - this.deleteApplianceError = ""; - let deletedAppliance = ""; - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.appliancesDeleteById(applianceId); - deletedAppliance = response.data.id; - // Remove the deleted appliance from the appliances array - if (response) { - this.appliances = this.appliances.filter( - (appliance) => appliance.id !== applianceId - ); - } - return deletedAppliance; - } catch (error) { - if (axios.isAxiosError(error)) { - this.deleteApplianceError = error.message; - if (error.response) { - this.deleteApplianceError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.deleteApplianceError = error; - } - console.error("Error:", error); + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + this.renameApplianceError = error.message; + + if (error.response) { + this.renameApplianceError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.renameApplianceError = error; + } + console.error("Error:", error); + } + }, + + async fetchAppliances() { + this.appliances = []; + this.applianceIds = []; + try { + // Get all appliances from OpenBMC + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const responseOfAppliances = await defaultApi.appliancesGet(); + const applianceCount = responseOfAppliances.data.memberCount; + + for (let i = 0; i < applianceCount; i++) { + // Extract the id for each appliance + const uri = responseOfAppliances.data.members[i]; + const applianceId: string = JSON.stringify(uri) + .split("/") + .pop() + ?.slice(0, -2) as string; + // Get appliance by id + const detailsResponseOfAppliance = await defaultApi.appliancesGetById( + applianceId + ); + + // Store appliance in appliances + if (detailsResponseOfAppliance) { + this.appliances.push(detailsResponseOfAppliance.data); + + const responseOfBlades = await defaultApi.bladesGet(applianceId); + const bladeCount = responseOfBlades.data.memberCount; + const associatedBlades = []; + + for (let i = 0; i < bladeCount; i++) { + // Extract the id for each blade + const uri = responseOfBlades.data.members[i]; + const bladeId: string = JSON.stringify(uri) + .split("/") + .pop() + ?.slice(0, -2) as string; + // Store blade in blades + if (bladeId) { + const responseOfBlade = await defaultApi.bladesGetById( + applianceId, + bladeId + ); + const response = { + id: responseOfBlade.data.id, + ipAddress: responseOfBlade.data.ipAddress, + status: responseOfBlade.data.status, + }; + associatedBlades.push(response); + } } - }, + this.applianceIds.push({ + id: detailsResponseOfAppliance.data.id, + blades: associatedBlades, + }); + } + } + } catch (error) { + console.error("Error fetching appliances:", error); + } + }, + + async discoverBlades() { + try { + // Get all the existed blades + const existedBladeIpAddress: (string | undefined)[] = []; + for (let i = 0; i < this.applianceIds.length; i++) { + for (let j = 0; j < this.applianceIds[i].blades.length; j++) { + existedBladeIpAddress.push( + this.applianceIds[i].blades[j].ipAddress + ); + } + } + + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + this.discoveredBlades = []; + const responseOfBlade = await defaultApi.discoverDevices("blade"); + this.discoveredBlades = responseOfBlade.data; + + // Remove the existed blades from the discovered blades + for (let k = 0; k < this.discoveredBlades.length; k++) { + for (let m = 0; m < existedBladeIpAddress.length; m++) { + this.discoveredBlades = this.discoveredBlades.filter( + (discoveredBlade) => + discoveredBlade.address !== existedBladeIpAddress[m] + ); + } + } + + // Format the device name, remove the .local suffix (e.g. blade device name: granite00.local) from the device name by splitting it with . + for (let n = 0; n < this.discoveredBlades.length; n++) { + this.discoveredBlades[n].name = + this.discoveredBlades[n].name!.split(".")[0]; + } + + return this.discoveredBlades; + } catch (error) { + console.error("Error discovering new devices:", error); + } + }, + + async addDiscoveredBlades(blade: DiscoveredDevice) { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const responseOfApplianceExist = await defaultApi.appliancesGetById( + this.defaultApplianceId + ); + + // If there is no default appliance, add one + if (!responseOfApplianceExist) { + this.newApplianceCredentials.customId = this.defaultApplianceId; + const responseOfAppliance = await defaultApi.appliancesPost( + this.newApplianceCredentials + ); + + // Add the new appliance to the appliances and applianceIds array + if (responseOfAppliance) { + this.appliances.push(responseOfAppliance.data); + const newAppliance = { id: responseOfAppliance.data.id, blades: [] }; + this.applianceIds.push(newAppliance); + } + } + + const appliance = this.applianceIds.find( + (appliance) => appliance.id === this.defaultApplianceId + ); + // Remove the .local suffix (e.g. blade device name: granite00.local) from the device name by splitting it with . and assign it to the customId + const deviceName = blade.name!.split(".")[0]; + this.newBladeCredentials.customId = deviceName; + this.newBladeCredentials.ipAddress = blade.address + ""; + + // Add the new discovered blade to the default appliance + const responseOfBlade = await defaultApi.bladesPost( + this.defaultApplianceId, + this.newBladeCredentials + ); + + if (responseOfBlade) { + const response = { + id: responseOfBlade.data.id, + ipAddress: responseOfBlade.data.ipAddress, + status: responseOfBlade.data.status, + }; + appliance!.blades.push(response); + } + + return responseOfBlade.data; + }, + + async addNewAppliance(newAppliance: Credentials) { + this.addApplianceError = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.appliancesPost(newAppliance); + const addedAppliance = response.data; + // Add the new appliance to the appliances array + this.appliances.push(addedAppliance); + return addedAppliance; + } catch (error) { + if (axios.isAxiosError(error)) { + this.addApplianceError = error.message; + if (error.response) { + this.addApplianceError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.addApplianceError = error; + } + console.error("Error:", error); + } + }, + + async deleteAppliance(applianceId: string) { + this.deleteApplianceError = ""; + let deletedAppliance = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.appliancesDeleteById(applianceId); + deletedAppliance = response.data.id; + // Remove the deleted appliance from the appliances array + if (response) { + this.appliances = this.appliances.filter( + (appliance) => appliance.id !== applianceId + ); + } + return deletedAppliance; + } catch (error) { + if (axios.isAxiosError(error)) { + this.deleteApplianceError = error.message; + if (error.response) { + this.deleteApplianceError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.deleteApplianceError = error; + } + console.error("Error:", error); + } + }, - selectAppliance(applianceId: string) { - this.selectedApplianceId = applianceId; - }, - } -}) \ No newline at end of file + selectAppliance(applianceId: string) { + this.selectedApplianceId = applianceId; + }, + }, +}); diff --git a/webui/src/components/Stores/HostStore.ts b/webui/src/components/Stores/HostStore.ts index ae3e44a..059aed1 100644 --- a/webui/src/components/Stores/HostStore.ts +++ b/webui/src/components/Stores/HostStore.ts @@ -1,244 +1,279 @@ // Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates -import { defineStore } from 'pinia' +import { defineStore } from "pinia"; import { Host, Credentials, DefaultApi, DiscoveredDevice } from "@/axios/api"; import { BASE_PATH } from "@/axios/base"; -import axios from 'axios'; +import axios from "axios"; // Use API_BASE_PATH to overwrite the BASE_PATH in the generated client code const API_BASE_PATH = process.env.BASE_PATH || BASE_PATH; -export const useHostStore = defineStore('host', { - state: () => ({ - hosts: [] as Host[], - selectedHostId: null as unknown as string, - selectedHostIp: null as unknown as string, - selectedHostPortNum: null as unknown as number, - selectedHostLocalMemory: null as unknown as number | undefined, - selectedHostStatus: null as unknown as string | undefined, - addHostError: null as unknown, - deleteHostError: null as unknown, - resyncHostError: null as unknown, - renameHostError: null as unknown, - hostIds: [] as { id: string, ipAddress: string, status: string | undefined }[], - discoveredHosts: [] as DiscoveredDevice[], - - prefixHostId: "Discoverd_Host_", - newHostCredentials: { - username: "admin", - password: "admin12345", - ipAddress: "127.0.0.1", - port: 8082, - insecure: true, - protocol: "http", - customId: "", - }, - }), - - actions: { - async fetchHosts() { - this.hosts = []; - this.hostIds = []; - try { - // Get all hosts from OpenBMC - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const responseOfHosts = await defaultApi.hostsGet(); - const hostCount = responseOfHosts.data.memberCount; - - for (let i = 0; i < hostCount; i++) { - // Extract the id for each host - const uri = responseOfHosts.data.members[i]; - const hostId: string = JSON.stringify(uri).split("/").pop()?.slice(0, -2) as string; - // Get host by id - const detailsResponseOfHost = await defaultApi.hostsGetById( - hostId - ); - - // Store host in hosts - if (detailsResponseOfHost) { - this.hosts.push(detailsResponseOfHost.data); - const host = { id: detailsResponseOfHost.data.id, ipAddress: detailsResponseOfHost.data.ipAddress, status: detailsResponseOfHost.data.status } - this.hostIds.push(host) - } - } - } catch (error) { - console.error("Error fetching hosts:", error); - } - }, - - async fetchHostById(hostId: string) { - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const detailsResponseOfHost = await defaultApi.hostsGetById( - hostId - ); - - const host = detailsResponseOfHost.data; - this.updateSelectHostStatus(host.status) - - return host; - } catch (error) { - console.error("Error fetching host by id:", error); - } - }, - - async discoverHosts() { - try { - // Get all the existed hosts - const existedHostIpAddress: (string | undefined)[] = [] - for (var i = 0; i < this.hostIds.length; i++) { - existedHostIpAddress.push(this.hostIds[i].ipAddress) - } - - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - this.discoveredHosts = []; - const responseOfHost = await defaultApi.discoverDevices("cxl-host"); - this.discoveredHosts = responseOfHost.data; - - // Remove the existed hosts from the discovered hosts - for (var k = 0; k < this.discoveredHosts.length; k++) { - for (var m = 0; m < existedHostIpAddress.length; m++) { - this.discoveredHosts = this.discoveredHosts.filter( - (discoveredHost) => discoveredHost.address !== existedHostIpAddress[m] - ); - } - } - - return this.discoveredHosts - } catch (error) { - console.error("Error discovering new devices:", error); - } - }, - - async addDiscoveredHosts(host: DiscoveredDevice) { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - // Add all the new didcovered hosts - this.newHostCredentials.customId = this.prefixHostId + host.address; - this.newHostCredentials.ipAddress = host.address + ""; - - const responseOfHost = await defaultApi.hostsPost(this.newHostCredentials); - - // Update the applianceIds and appliances - if (responseOfHost) { - const response = { id: responseOfHost.data.id, ipAddress: responseOfHost.data.ipAddress, status: responseOfHost.data.status } - this.hosts.push(responseOfHost.data); - this.hostIds.push(response) - } - return responseOfHost.data; - }, - - async addNewHost(newHost: Credentials) { - this.addHostError = ""; - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.hostsPost(newHost); - const addedHost = response.data; - // Add the new host to the hosts array - this.hosts.push(addedHost); - return addedHost; - } catch (error) { - if (axios.isAxiosError(error)) { - this.addHostError = error.message; - if (error.response) { - this.addHostError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.addHostError = error; - } - console.error("Error:", error); - } - }, - - async deleteHost(hostId: string) { - this.deleteHostError = ""; - let deletedHost = ""; - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.hostsDeleteById(hostId); - deletedHost = response.data.id; - // Remove the deleted host from the hosts array - if (response) { - this.hosts = this.hosts.filter( - (host) => host.id !== hostId - ); - } - return deletedHost; - } catch (error) { - if (axios.isAxiosError(error)) { - this.deleteHostError = error.message; - if (error.response) { - this.deleteHostError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.deleteHostError = error; - } - console.error("Error:", error); - } - }, - - async renameHost(hostId: string, newHostId: string) { - this.renameHostError = ""; - - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.hostsUpdateById(hostId, newHostId); - - // Update the hosts array - if (response) { - this.hosts = this.hosts.filter( - (host) => host.id !== hostId - ); - this.hosts.push(response.data); - } - - return response.data - } catch (error) { - if (axios.isAxiosError(error)) { - this.renameHostError = error.message; - if (error.response) { - this.renameHostError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.renameHostError = error; - } - console.error("Error:", error); - } - }, - - - async resyncHost(hostId: string) { - this.resyncHostError = ""; - try { - const defaultApi = new DefaultApi(undefined, API_BASE_PATH); - const response = await defaultApi.hostsResyncById(hostId); - - const resyncedHost = response.data; - return resyncedHost; - } catch (error) { - if (axios.isAxiosError(error)) { - this.resyncHostError = error.message; - if (error.response) { - this.resyncHostError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; - } - } - else { - this.resyncHostError = error; - } - console.error("Error:", error); - } - }, - - selectHost(selectedHostId: string, selectedHostIp: string, selectedHostPortNum: number, selectedHostLocalMemory: number | undefined, status: string | undefined) { - this.selectedHostId = selectedHostId; - this.selectedHostIp = selectedHostIp; - this.selectedHostPortNum = selectedHostPortNum; - this.selectedHostLocalMemory = selectedHostLocalMemory; - this.selectedHostStatus = status - }, - updateSelectHostStatus(status: string | undefined) { - this.selectedHostStatus = status +export const useHostStore = defineStore("host", { + state: () => ({ + hosts: [] as Host[], + selectedHostId: null as unknown as string, + selectedHostIp: null as unknown as string, + selectedHostPortNum: null as unknown as number, + selectedHostLocalMemory: null as unknown as number | undefined, + selectedHostStatus: null as unknown as string | undefined, + addHostError: null as unknown, + deleteHostError: null as unknown, + resyncHostError: null as unknown, + renameHostError: null as unknown, + hostIds: [] as { + id: string; + ipAddress: string; + status: string | undefined; + }[], + discoveredHosts: [] as DiscoveredDevice[], + + newHostCredentials: { + username: "admin", + password: "admin12345", + ipAddress: "127.0.0.1", + port: 8082, + insecure: true, + protocol: "http", + customId: "", + }, + }), + + actions: { + async fetchHosts() { + this.hosts = []; + this.hostIds = []; + try { + // Get all hosts from OpenBMC + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const responseOfHosts = await defaultApi.hostsGet(); + const hostCount = responseOfHosts.data.memberCount; + + for (let i = 0; i < hostCount; i++) { + // Extract the id for each host + const uri = responseOfHosts.data.members[i]; + const hostId: string = JSON.stringify(uri) + .split("/") + .pop() + ?.slice(0, -2) as string; + // Get host by id + const detailsResponseOfHost = await defaultApi.hostsGetById(hostId); + + // Store host in hosts + if (detailsResponseOfHost) { + this.hosts.push(detailsResponseOfHost.data); + const host = { + id: detailsResponseOfHost.data.id, + ipAddress: detailsResponseOfHost.data.ipAddress, + status: detailsResponseOfHost.data.status, + }; + this.hostIds.push(host); + } + } + } catch (error) { + console.error("Error fetching hosts:", error); + } + }, + + async fetchHostById(hostId: string) { + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const detailsResponseOfHost = await defaultApi.hostsGetById(hostId); + + const host = detailsResponseOfHost.data; + this.updateSelectHostStatus(host.status); + + return host; + } catch (error) { + console.error("Error fetching host by id:", error); + } + }, + + async discoverHosts() { + try { + // Get all the existed hosts + const existedHostIpAddress: (string | undefined)[] = []; + for (let i = 0; i < this.hostIds.length; i++) { + existedHostIpAddress.push(this.hostIds[i].ipAddress); + } + + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + this.discoveredHosts = []; + const responseOfHost = await defaultApi.discoverDevices("cxl-host"); + this.discoveredHosts = responseOfHost.data; + + // Remove the existed hosts from the discovered hosts + for (let k = 0; k < this.discoveredHosts.length; k++) { + for (let m = 0; m < existedHostIpAddress.length; m++) { + this.discoveredHosts = this.discoveredHosts.filter( + (discoveredHost) => + discoveredHost.address !== existedHostIpAddress[m] + ); + } + } + + // Format the device name, remove the .local suffix (e.g. host device name: host00.local) from the device name by splitting it with . + for (let n = 0; n < this.discoveredHosts.length; n++) { + this.discoveredHosts[n].name = + this.discoveredHosts[n].name!.split(".")[0]; + } + + return this.discoveredHosts; + } catch (error) { + console.error("Error discovering new devices:", error); + } + }, + + async addDiscoveredHosts(host: DiscoveredDevice) { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + + // Remove the .local suffix (e.g. host device name: host00.local) from the device name by splitting it with . and assign it to the customId + const deviceName = host.name!.split(".")[0]; + this.newHostCredentials.customId = deviceName; + this.newHostCredentials.ipAddress = host.address + ""; + + // Add the new didcovered host + const responseOfHost = await defaultApi.hostsPost( + this.newHostCredentials + ); + + // Update the hostIds and hosts + if (responseOfHost) { + const response = { + id: responseOfHost.data.id, + ipAddress: responseOfHost.data.ipAddress, + status: responseOfHost.data.status, + }; + this.hosts.push(responseOfHost.data); + this.hostIds.push(response); + } + return responseOfHost.data; + }, + + async addNewHost(newHost: Credentials) { + this.addHostError = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.hostsPost(newHost); + const addedHost = response.data; + // Add the new host to the hosts array + this.hosts.push(addedHost); + return addedHost; + } catch (error) { + if (axios.isAxiosError(error)) { + this.addHostError = error.message; + if (error.response) { + this.addHostError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.addHostError = error; } - } -}) \ No newline at end of file + console.error("Error:", error); + } + }, + + async deleteHost(hostId: string) { + this.deleteHostError = ""; + let deletedHost = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.hostsDeleteById(hostId); + deletedHost = response.data.id; + // Remove the deleted host from the hosts array + if (response) { + this.hosts = this.hosts.filter((host) => host.id !== hostId); + } + return deletedHost; + } catch (error) { + if (axios.isAxiosError(error)) { + this.deleteHostError = error.message; + if (error.response) { + this.deleteHostError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.deleteHostError = error; + } + console.error("Error:", error); + } + }, + + async renameHost(hostId: string, newHostId: string) { + this.renameHostError = ""; + + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.hostsUpdateById(hostId, newHostId); + + // Update the hosts array + if (response) { + this.hosts = this.hosts.filter((host) => host.id !== hostId); + this.hosts.push(response.data); + } + + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + this.renameHostError = error.message; + if (error.response) { + this.renameHostError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.renameHostError = error; + } + console.error("Error:", error); + } + }, + + async resyncHost(hostId: string) { + this.resyncHostError = ""; + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.hostsResyncById(hostId); + + const resyncedHost = response.data; + return resyncedHost; + } catch (error) { + if (axios.isAxiosError(error)) { + this.resyncHostError = error.message; + if (error.response) { + this.resyncHostError = + error.response?.data.status.message + + " (" + + error.response?.request.status + + ")"; + } + } else { + this.resyncHostError = error; + } + console.error("Error:", error); + } + }, + + selectHost( + selectedHostId: string, + selectedHostIp: string, + selectedHostPortNum: number, + selectedHostLocalMemory: number | undefined, + status: string | undefined + ) { + this.selectedHostId = selectedHostId; + this.selectedHostIp = selectedHostIp; + this.selectedHostPortNum = selectedHostPortNum; + this.selectedHostLocalMemory = selectedHostLocalMemory; + this.selectedHostStatus = status; + }, + updateSelectHostStatus(status: string | undefined) { + this.selectedHostStatus = status; + }, + }, +});