From 671cd0373b92751b8837f31d0f88150e0263527b Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:32:11 -0700 Subject: [PATCH] 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 --- webui/src/components/Appliance/Appliances.vue | 14 +- webui/src/components/Dashboard/Dashboard.vue | 362 +++++++++++++++++- .../Dashboard/initial-control-elements.ts | 59 +-- .../Dashboard/initial-data-elements.ts | 61 +-- webui/src/components/Dashboard/useLayout.ts | 53 ++- webui/src/components/Stores/ApplianceStore.ts | 99 ++++- webui/src/components/Stores/HostStore.ts | 64 +++- 7 files changed, 625 insertions(+), 87 deletions(-) diff --git a/webui/src/components/Appliance/Appliances.vue b/webui/src/components/Appliance/Appliances.vue index 3651a4e7..07fe5029 100644 --- a/webui/src/components/Appliance/Appliances.vue +++ b/webui/src/components/Appliance/Appliances.vue @@ -93,6 +93,7 @@ variant="text" id="addBlade" @click="addNewBladeWindowButton" + :disabled="isAddBladeButtonDisabled" > mdi-plus-thick BLADE @@ -1524,6 +1525,12 @@ export default { ComposeMemoryButton, }, + computed: { + isAddBladeButtonDisabled() { + return this.selectedApplianceId === "CMA_Discovered_Blades"; + }, + }, + methods: { /* Open the add appliance popup */ addNewApplianceWindowButton() { @@ -1985,7 +1992,10 @@ export default { applianceStore.selectedApplianceId, newBladeId ), - bladeStore.fetchBladeById(applianceStore.selectedApplianceId, newBladeId), + bladeStore.fetchBladeById( + applianceStore.selectedApplianceId, + newBladeId + ), ]); // Update the URL with the new blade ID updateUrlWithBladeId(applianceStore.selectedApplianceId, newBladeId); @@ -2063,4 +2073,4 @@ export default { .highlighted-tab { font-weight: bold; } - \ No newline at end of file + diff --git a/webui/src/components/Dashboard/Dashboard.vue b/webui/src/components/Dashboard/Dashboard.vue index c0aa88c2..07aaad58 100644 --- a/webui/src/components/Dashboard/Dashboard.vue +++ b/webui/src/components/Dashboard/Dashboard.vue @@ -3,7 +3,7 @@ - {{ buttonLabel }} - + + + + {{ buttonLabel }} + + + + + Click to discover new devices + +
+ + + + + mdi-magnify + + + + + + Devices + + + mdi-rectangle + CMA + + + + + mdi-rectangle + Blade + + + + + mdi-rectangle + Host + + +
Status + + + mdi-rectangle-outline + online + + + + + mdi-rectangle-outline + offline + + + + + mdi-rectangle-outline + unavailable + + +
+
+
- - - - mdi-magnify - - - + + + + + + New Discovered Blades: + +
+ New Discovered Hosts: + +
+ + + + Cancel + Add + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

Congrats! New devices were added

+

+ New blades: +

    +
  • + {{ blade.id }} +
  • +

New hosts:
+
    +
  • + {{ host.id }} +
  • +
+

+ +
+ + Done + +
+
+
@@ -68,6 +265,110 @@ import { ControlButton, Controls } from "@vue-flow/controls"; export default { components: { VueFlow, ControlButton, Controls }, + data() { + return { + addNewDiscoveredDevicesProgressText: + "Adding the selected devices, please wait...", + discoverDevicesProgressText:"Discovering devices, please wait...", + + dialogNewDiscoveredDevices: false, + dialogAddNewDiscoveredDevicesWait: false, + dialogAddNewDiscoveredDevicesOutput: false, + dialogDiscoverDevicesWait: false, + + discoveredBlades: [], + discoveredHosts: [], + + selectedBlades: [], + selectedHosts: [], + + newBlades: [], + newHosts: [], + }; + }, + + methods: { + async discoverDevices() { + this.dialogDiscoverDevicesWait = true; + const applianceStore = useApplianceStore(); + const hostStore = useHostStore(); + + try { + const responseOfBlades = await applianceStore.discoverBlades(); + const responseOfHosts = await hostStore.discoverHosts(); + + const response = (responseOfBlades || []).concat(responseOfHosts || []); + this.discoveredBlades = responseOfBlades || []; + this.discoveredHosts = responseOfHosts || []; + + this.dialogNewDiscoveredDevices = true; + this.dialogDiscoverDevicesWait = false; + + return response.length ? response : []; + } catch (error) { + this.dialogDiscoverDevicesWait = false; + console.error("Error fetching data:", error); + return []; + } + }, + + async addDiscoveredDevices() { + this.dialogNewDiscoveredDevices = false; + this.dialogAddNewDiscoveredDevicesWait = true; + + const applianceStore = useApplianceStore(); + const hostStore = useHostStore(); + const bladePortStore = useBladePortStore(); + + if (this.selectedBlades.length === 0) { + console.log("No blades selected."); + } else { + for (var i = 0; i < this.selectedBlades.length; i++) { + try { + const newAddedBlade = await applianceStore.addDiscoveredBlades( + this.selectedBlades[i] + ); + + if (newAddedBlade) { + this.newBlades.push(newAddedBlade); + } + } catch (error) { + console.error("Error adding new discovered blade:", error); + } + } + } + + if (this.selectedHosts.length === 0) { + console.log("No hosts selected."); + } else { + for (var i = 0; i < this.selectedHosts.length; i++) { + try { + const newAddedHost = await hostStore.addDiscoveredHosts( + this.selectedHosts[i] + ); + if (newAddedHost) { + this.newHosts.push(newAddedHost); + } + } catch (error) { + console.error("Error adding new discovered host:", error); + } + } + } + + // Update the graph content + await applianceStore.fetchAppliances(); + await hostStore.fetchHosts(); + for (const appliance of applianceStore.applianceIds) { + for (const blade of appliance.blades) { + await bladePortStore.fetchBladePorts(appliance.id, blade.id); + } + } + + this.dialogAddNewDiscoveredDevicesWait = false; + this.dialogAddNewDiscoveredDevicesOutput = true; + }, + }, + setup() { const applianceStore = useApplianceStore(); const hostStore = useHostStore(); @@ -178,8 +479,8 @@ export default { await hostStore.fetchHosts(); // Ensure blade ports are fetched after appliances, this action will create the edges for dataPlane for (const appliance of applianceStore.applianceIds) { - for (const bladeId of appliance.bladeIds) { - await bladePortStore.fetchBladePorts(appliance.id, bladeId); + for (const blade of appliance.blades) { + await bladePortStore.fetchBladePorts(appliance.id, blade.id); } } }); @@ -200,3 +501,22 @@ export default { }, }; + + diff --git a/webui/src/components/Dashboard/initial-control-elements.ts b/webui/src/components/Dashboard/initial-control-elements.ts index 5ba362a0..0d979005 100644 --- a/webui/src/components/Dashboard/initial-control-elements.ts +++ b/webui/src/components/Dashboard/initial-control-elements.ts @@ -23,7 +23,7 @@ export const useControlData = () => { id: "cfm-service", data: { label: "CFM Service", }, position: position, - style: { backgroundColor: "#6ebe4a", color: "#000" }, + style: { backgroundColor: useLayout().Colors.serviceColor, border: "none" }, type: serviceNodeType, }, ] @@ -35,26 +35,33 @@ export const useControlData = () => { id: `appliance-${appliance.id}`, data: { label: appliance.id, url: `/appliances/${appliance.id}` }, position: position, - style: { backgroundColor: "#f2ae72", color: "#000" }, + style: { backgroundColor: useLayout().Colors.applianceColor, border: "none" }, type: applianceNodeType, }, - ...appliance.bladeIds.map((bladeId, bladeIndex) => ({ - id: `blade-${bladeId}`, - data: { label: bladeId, url: `/appliances/${appliance.id}/blades/${bladeId}`, associatedAppliance: appliance.id }, - position: position, - style: { backgroundColor: "#f2e394", color: "#000" }, - type: bladeNodeType, - })), + ...appliance.blades.map((blade, bladeIndex) => { + const borderColor = useLayout().borderColorChange(blade.status); + return { + id: `blade-${blade.id}`, + data: { label: blade.id, url: `/appliances/${appliance.id}/blades/${blade.id}`, associatedAppliance: appliance.id }, + position: position, + style: { backgroundColor: useLayout().Colors.baldeColor, border: `3px solid ${borderColor}` }, + type: bladeNodeType, + } + }), ] ); - const hostNodes = hostStore.hostIds.map((host, index) => ({ - id: `host-${host}`, - data: { label: host, url: `/hosts/${host}` }, - position: position, - style: { backgroundColor: "#d9ecd0", color: "#000" }, - type: hostNodeType, - })); + const hostNodes = hostStore.hostIds.map((host, index) => { + const borderColor = useLayout().borderColorChange(host.status); + + return { + id: `host-${host.id}`, + data: { label: host.id, url: `/hosts/${host.id}` }, + position: position, + style: { backgroundColor: useLayout().Colors.hostColor, border: `3px solid ${borderColor}` }, + type: hostNodeType, + } + }); const allNodes = [...coreNode, ...applianceNodes, ...hostNodes]; @@ -67,18 +74,18 @@ export const useControlData = () => { source: "cfm-service", target: `appliance-${appliance.id}`, }, - ...appliance.bladeIds.map((bladeId) => ({ - id: `appliance-blade-${appliance.id}-${bladeId}`, + ...appliance.blades.map((blade) => ({ + id: `appliance-blade-${appliance.id}-${blade.id}`, source: `appliance-${appliance.id}`, - target: `blade-${bladeId}`, + target: `blade-${blade.id}`, })), ]) : []; const hostEdges = hostStore.hostIds.map((host) => ({ - id: `cfm-${host}`, + id: `cfm-${host.id}`, source: "cfm-service", - target: `host-${host}`, + target: `host-${host.id}`, })); return [...coreEdges, ...hostEdges]; @@ -97,19 +104,19 @@ export const useControlData = () => { target: `appliance-${appliance.id}`, animated: true, }, - ...appliance.bladeIds.map((bladeId) => ({ - id: `appliance-blade-${appliance.id}-${bladeId}`, + ...appliance.blades.map((blade) => ({ + id: `appliance-blade-${appliance.id}-${blade.id}`, source: `appliance-${appliance.id}`, - target: `blade-${bladeId}`, + target: `blade-${blade.id}`, animated: true, })), ]) : []; const hostEdges = hostStore.hostIds.map((host) => ({ - id: `cfm-${host}`, + id: `cfm-${host.id}`, source: "cfm-service", - target: `host-${host}`, + target: `host-${host.id}`, animated: true, })); diff --git a/webui/src/components/Dashboard/initial-data-elements.ts b/webui/src/components/Dashboard/initial-data-elements.ts index 28e4538b..c4ec05e5 100644 --- a/webui/src/components/Dashboard/initial-data-elements.ts +++ b/webui/src/components/Dashboard/initial-data-elements.ts @@ -3,6 +3,7 @@ import { computed } from "vue"; import { useApplianceStore } from "../Stores/ApplianceStore"; import { useHostStore } from "../Stores/HostStore"; import { useBladePortStore } from "../Stores/BladePortStore"; +import { useLayout } from "./useLayout"; export const useData = () => { const applianceStore = useApplianceStore(); @@ -17,7 +18,7 @@ export const useData = () => { let currentYPosition = 0; const applianceNodes = applianceStore.applianceIds.flatMap( (appliance, index) => { - const bladeCount = appliance.bladeIds.length; + const bladeCount = appliance.blades.length; const applianceHeight = 50 + bladeCount * 50; // Adjust height based on number of blades const applianceWidth = 270; // Width of the appliance node const bladeWidth = 250; // Width of the blade node @@ -27,24 +28,27 @@ export const useData = () => { id: `appliance-${appliance.id}`, data: { label: appliance.id, url: `/appliances/${appliance.id}` }, position: { x: 100, y: currentYPosition }, - style: { backgroundColor: "rgba(242, 174, 114, 0.5)", color: "#000", height: `${applianceHeight}px`, width: `${applianceWidth}px` }, + style: { backgroundColor: useLayout().Colors.applianceColor, height: `${applianceHeight}px`, width: `${applianceWidth}px`, border: "none" }, type: applianceNodeType, sourcePosition: 'right', targetPosition: 'left', }; - const bladeNodes = appliance.bladeIds.map((bladeId, bladeIndex) => ({ - id: `blade-${bladeId}`, - data: { label: bladeId, url: `/appliances/${appliance.id}/blades/${bladeId}`, associatedAppliance: appliance.id }, - position: { x: bladeXPosition, y: 50 + bladeIndex * 50 }, // Center blades within the appliance node - style: { backgroundColor: "#f2e394", color: "#000", width: `${bladeWidth}px` }, - type: bladeNodeType, - parentNode: `appliance-${appliance.id}`, - extent: 'parent', - expandParent: true, - sourcePosition: 'right', - targetPosition: 'left', - })); + const bladeNodes = appliance.blades.map((blade, bladeIndex) => { + const borderColor = useLayout().borderColorChange(blade.status); + return { + id: `blade-${blade.id}`, + data: { label: blade.id, url: `/appliances/${appliance.id}/blades/${blade.id}`, associatedAppliance: appliance.id }, + position: { x: bladeXPosition, y: 50 + bladeIndex * 50 }, // Center blades within the appliance node + style: { backgroundColor: useLayout().Colors.baldeColor, width: `${bladeWidth}px`, border: `3px solid ${borderColor}` }, + type: bladeNodeType, + parentNode: `appliance-${appliance.id}`, + extent: 'parent', + expandParent: true, + sourcePosition: 'right', + targetPosition: 'left', + } + }); currentYPosition += applianceHeight + 20; // Add some space between nodes @@ -52,15 +56,26 @@ export const useData = () => { } ); - const hostNodes = hostStore.hostIds.map((host, index) => ({ - id: `host-${host}`, - data: { label: host, url: `/hosts/${host}` }, - position: { x: 500, y: index * 200 }, - style: { backgroundColor: "#d9ecd0", color: "#000" }, - type: hostNodeType, - sourcePosition: 'right', - targetPosition: 'left', - })); + const hostNodes = hostStore.hostIds.map((host, index) => { + const { width, height } = useLayout().measureText(host.id); + const borderColor = useLayout().borderColorChange(host.status); + + return { + id: `host-${host.id}`, + data: { label: host.id, url: `/hosts/${host.id}` }, + position: { x: 500, y: index * 200 }, + style: { + backgroundColor: useLayout().Colors.hostColor, + color: "#000", + width: `${width + 20}px`, // Adding some padding + height: `${height + 20}px`, // Adding some padding + border: `3px solid ${borderColor}` + }, + type: hostNodeType, + sourcePosition: 'right', + targetPosition: 'left', + }; + }); return [...applianceNodes, ...hostNodes]; }); diff --git a/webui/src/components/Dashboard/useLayout.ts b/webui/src/components/Dashboard/useLayout.ts index acf35066..e6b6a4e5 100644 --- a/webui/src/components/Dashboard/useLayout.ts +++ b/webui/src/components/Dashboard/useLayout.ts @@ -7,6 +7,39 @@ import { ref } from 'vue'; * Composable to run the layout algorithm on the graph. * It uses the `dagre` library to calculate the layout of the nodes and edges. */ + +export function measureText(text: string, font = '16px Arial') { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + context!.font = font; + const metrics = context!.measureText(text); + const width = Math.max(metrics.width, 220); + return { + width: width, + height: parseInt(font, 10) // Assuming height is roughly the font size + }; +}; + +export const Colors = { + applianceColor: '#f2ae72', + baldeColor: '#f2e394', + hostColor: '#d9ecd0', + serviceColor: '#6ebe4a', +}; + +export function borderColorChange(status: string | undefined) { + switch (status) { + case "online": + return "#6ebe4a"; // green + case "offline": + return "#b00020"; // Red + case "unavailable": + return "#ff9f40"; // Orange + default: + return "#ffffff"; // White + } +}; + export function useLayout() { const { findNode } = useVueFlow(); @@ -28,10 +61,13 @@ export function useLayout() { previousDirection.value = direction; for (const node of nodes) { - // Use the dimensions property of the internal node (`GraphNode` type) - const graphNode = findNode(node.id); + // Measure the text dimensions for dynamic sizing + const { width, height } = measureText(node.data.label); - dagreGraph.setNode(node.id, { width: graphNode?.dimensions.width || 150, height: graphNode?.dimensions.height || 50 }); + dagreGraph.setNode(node.id, { + width: width + 20, // Adding some padding + height: height + 20 // Adding some padding + }); } for (const edge of edges) { @@ -41,7 +77,7 @@ export function useLayout() { dagre.layout(dagreGraph); // Set nodes with updated positions - return nodes.map((node: { id: string | dagre.Label; }) => { + return nodes.map((node: { id: string | dagre.Label; style: any; }) => { const nodeWithPosition = dagreGraph.node(node.id); return { @@ -49,9 +85,14 @@ export function useLayout() { targetPosition: isHorizontal ? Position.Left : Position.Top, sourcePosition: isHorizontal ? Position.Right : Position.Bottom, position: { x: nodeWithPosition.x, y: nodeWithPosition.y }, + style: { + ...node.style, + width: `${nodeWithPosition.width}px`, + height: `${nodeWithPosition.height}px` + } }; }); } - return { graph, layout, previousDirection }; -} + return { graph, layout, previousDirection, measureText, borderColorChange, Colors }; +} \ No newline at end of file diff --git a/webui/src/components/Stores/ApplianceStore.ts b/webui/src/components/Stores/ApplianceStore.ts index 661f7779..b1ad0d3f 100644 --- a/webui/src/components/Stores/ApplianceStore.ts +++ b/webui/src/components/Stores/ApplianceStore.ts @@ -1,6 +1,6 @@ // Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates import { defineStore } from 'pinia' -import { Appliance, Credentials, DefaultApi } from "@/axios/api"; +import { Appliance, Credentials, DefaultApi, DiscoveredDevice } from "@/axios/api"; import { BASE_PATH } from "@/axios/base"; import axios from 'axios'; @@ -14,7 +14,30 @@ export const useApplianceStore = defineStore('appliance', { addApplianceError: null as unknown, deleteApplianceError: null as unknown, renameApplianceError: null as unknown, - applianceIds: [] as { id: string, bladeIds: string[] }[], + 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: { @@ -72,7 +95,7 @@ export const useApplianceStore = defineStore('appliance', { const responseOfBlades = await defaultApi.bladesGet(applianceId); const bladeCount = responseOfBlades.data.memberCount; - const bladeIds = []; + const associatedBlades = []; for (let i = 0; i < bladeCount; i++) { // Extract the id for each blade @@ -80,10 +103,12 @@ export const useApplianceStore = defineStore('appliance', { const bladeId: string = JSON.stringify(uri).split("/").pop()?.slice(0, -2) as string; // Store blade in blades if (bladeId) { - bladeIds.push(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, bladeIds }); + this.applianceIds.push({ id: detailsResponseOfAppliance.data.id, blades: associatedBlades }); } } } catch (error) { @@ -91,13 +116,75 @@ export const useApplianceStore = defineStore('appliance', { } }, + 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) + } + } + + // Add the new discovered blade to the default appliance + let appliance = this.applianceIds.find(appliance => appliance.id === this.defaultApplianceId); + + this.newBladeCredentials.customId = this.prefixBladeId + blade.address; + this.newBladeCredentials.ipAddress = blade.address + ""; + + 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; - console.log("added appliance", addedAppliance) // Add the new appliance to the appliances array this.appliances.push(addedAppliance); return addedAppliance; diff --git a/webui/src/components/Stores/HostStore.ts b/webui/src/components/Stores/HostStore.ts index 5051c548..ae3e44ae 100644 --- a/webui/src/components/Stores/HostStore.ts +++ b/webui/src/components/Stores/HostStore.ts @@ -1,6 +1,6 @@ // Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates import { defineStore } from 'pinia' -import { Host, Credentials, DefaultApi } from "@/axios/api"; +import { Host, Credentials, DefaultApi, DiscoveredDevice } from "@/axios/api"; import { BASE_PATH } from "@/axios/base"; import axios from 'axios'; @@ -19,7 +19,19 @@ export const useHostStore = defineStore('host', { deleteHostError: null as unknown, resyncHostError: null as unknown, renameHostError: null as unknown, - hostIds: [] as string[], + 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: { @@ -44,7 +56,8 @@ export const useHostStore = defineStore('host', { // Store host in hosts if (detailsResponseOfHost) { this.hosts.push(detailsResponseOfHost.data); - this.hostIds.push(detailsResponseOfHost.data.id) + const host = { id: detailsResponseOfHost.data.id, ipAddress: detailsResponseOfHost.data.ipAddress, status: detailsResponseOfHost.data.status } + this.hostIds.push(host) } } } catch (error) { @@ -68,6 +81,51 @@ export const useHostStore = defineStore('host', { } }, + 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 {