diff --git a/pkg/api/api_default_service.go b/pkg/api/api_default_service.go index 0019460..e295843 100644 --- a/pkg/api/api_default_service.go +++ b/pkg/api/api_default_service.go @@ -167,7 +167,19 @@ func (cfm *CfmApiService) AppliancesUpdateById(ctx context.Context, applianceId return formatErrorResp(ctx, err.(*common.RequestError)) } - return openapi.Response(http.StatusOK, newAppliance), nil + a := openapi.Appliance{ + Id: newAppliance.Id, + IpAddress: "", // Unused + Port: 0, // Unused + Status: "", // Unused + Blades: openapi.MemberItem{ + Uri: manager.GetCfmUriBlades(newAppliance.Id), + }, + TotalMemoryAvailableMiB: 0, + TotalMemoryAllocatedMiB: 0, + } + + return openapi.Response(http.StatusOK, a), nil } // AppliancesResync - @@ -554,7 +566,30 @@ func (cfm *CfmApiService) BladesUpdateById(ctx context.Context, applianceId stri return formatErrorResp(ctx, err.(*common.RequestError)) } - return openapi.Response(http.StatusOK, newBlade), nil + totals, err := newBlade.GetResourceTotals(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + b := openapi.Blade{ + Id: newBlade.Id, + IpAddress: newBlade.GetNetIp(), + Port: int32(newBlade.GetNetPort()), + Status: string(newBlade.Status), + Ports: openapi.MemberItem{ + Uri: manager.GetCfmUriBladePorts(appliance.Id, newBlade.Id), + }, + Resources: openapi.MemberItem{ + Uri: manager.GetCfmUriBladeResources(appliance.Id, newBlade.Id), + }, + Memory: openapi.MemberItem{ + Uri: manager.GetCfmUriBladeMemory(appliance.Id, newBlade.Id), + }, + TotalMemoryAvailableMiB: totals.TotalMemoryAvailableMiB, + TotalMemoryAllocatedMiB: totals.TotalMemoryAllocatedMiB, + } + + return openapi.Response(http.StatusOK, b), nil } // BladesGetResourceById - @@ -1019,7 +1054,30 @@ func (cfm *CfmApiService) HostsUpdateById(ctx context.Context, hostId string, ne return formatErrorResp(ctx, err.(*common.RequestError)) } - return openapi.Response(http.StatusOK, newHost), nil + totals, err := newHost.GetMemoryTotals(ctx) + if err != nil { + return formatErrorResp(ctx, err.(*common.RequestError)) + } + + h := openapi.Host{ + Id: newHost.Id, + IpAddress: newHost.GetNetIp(), + Port: int32(newHost.GetNetPort()), + Status: string(newHost.Status), + Ports: openapi.MemberItem{ + Uri: manager.GetCfmUriHostPorts(newHost.Id), + }, + Memory: openapi.MemberItem{ + Uri: manager.GetCfmUriHostMemory(newHost.Id), + }, + MemoryDevices: openapi.MemberItem{ + Uri: manager.GetCfmUriHostMemoryDevices(newHost.Id), + }, + LocalMemoryMiB: totals.LocalMemoryMib, + RemoteMemoryMiB: totals.RemoteMemoryMib, + } + + return openapi.Response(http.StatusOK, h), nil } // HostsResync - diff --git a/webui/src/components/Appliance/Appliances.vue b/webui/src/components/Appliance/Appliances.vue index 6ca6792..5565d52 100644 --- a/webui/src/components/Appliance/Appliances.vue +++ b/webui/src/components/Appliance/Appliances.vue @@ -45,18 +45,34 @@ {{ appliance.id }} - - mdi-close - Click here to delete this memory appliance - + + + + + + {{ item.text }} + + + @@ -105,18 +121,43 @@ {{ blade.id }} - - mdi-close - Click here to delete this blade - + + + + + + {{ item.text }} + + + @@ -152,19 +193,7 @@ > -

- Blade - - mdi-sync-circle - Click here to resynchronize this blade - -

+

Blade

A blade is associated with one appliance and it is Redfish service running on OpenBMC.
@@ -592,6 +621,295 @@ + + + + + Rename Appliance + + + + + + +
+ + +
+
+
+
+
+ + + + + Cancel + + + Rename + + +
+
+ + + + +

Rename an appliance succeeded!

+

+ New Appliance Id: +
{{ renamedApplianceId }} +

+ +
+ + Done + +
+
+
+ + + + +

Rename an appliance failed!

+

+ {{ renameApplianceError }} +

+ +
+ + Done + +
+
+
+ + + + + + + + + + + + + + + + Rename Blade + + + + + + +
+ + +
+
+
+
+
+ + + + + Cancel + + + Rename + + +
+
+ + + + +

Rename a Blade succeeded!

+

+ New Blade Id: +
{{ renamedBladeId }} +

+ +
+ + Done + +
+
+
+ + + + +

Rename a Blade failed!

+

+ {{ renameBladeError }} +

+ +
+ + Done + +
+
+
+ + + + + + + + + + + applianceStore.appliances); + if (appliances.value.length > 0) { + applianceStore.selectAppliance(newApplianceId); + } + + this.dialogRenameApplianceWait = false; + this.dialogRenameApplianceSuccess = true; + } else { + this.dialogRenameApplianceWait = false; + this.dialogRenameApplianceFailure = true; + } + + // Reset the credentials + this.renameApplianceCredentials = { + customId: "", + }; + }, + + renameBlade() { + this.dialogRenameBlade = true; + }, + + /* Triggle the API bladesUpdateById in blade store to rename a blade */ + async renameBladeConfirm( + applianceId: string, + bladeId: string, + newBladeId: string + ) { + // Make the rename blade popup disappear and waiting popup appear + this.dialogRenameBlade = false; + this.dialogRenameBladeWait = true; + + const bladeStore = useBladeStore(); + const newBlade = await bladeStore.renameBlade( + applianceId, + bladeId, + newBladeId + ); + + this.renameBladeError = bladeStore.renameBladeError as string; + + if (!this.renameBladeError) { + this.renamedBladeId = newBlade?.id; + + // Set the renamed Blade as the selected Blade + const Blades = computed(() => bladeStore.blades); + if (Blades.value.length > 0) { + const defaultBlade = newBlade; + bladeStore.selectBlade( + defaultBlade!.id, + defaultBlade!.ipAddress, + defaultBlade!.port, + Number(defaultBlade!.totalMemoryAvailableMiB), + Number(defaultBlade!.totalMemoryAllocatedMiB), + defaultBlade!.status + ); + } + + this.dialogRenameBladeWait = false; + this.dialogRenameBladeSuccess = true; + } else { + this.dialogRenameBladeWait = false; + this.dialogRenameBladeFailure = true; + } + + // Reset the credentials + this.renameBladeCredentials = { + customId: "", + }; + }, + /* Open the add blade popup */ addNewBladeWindowButton() { this.dialogNewBlade = true; diff --git a/webui/src/components/CXL-Hosts/CXL-Hosts.vue b/webui/src/components/CXL-Hosts/CXL-Hosts.vue index 2dc64a8..2f70f49 100644 --- a/webui/src/components/CXL-Hosts/CXL-Hosts.vue +++ b/webui/src/components/CXL-Hosts/CXL-Hosts.vue @@ -25,6 +25,14 @@ color="#6ebe4a" bg-color="rgba(110, 190, 74, 0.1)" > + + + mdi-plus-thick + Click here to add new cxl-host + + {{ host.id }} - - mdi-close - Click here to delete this cxl-host - + + + + + + {{ item.text }} + + + - - - mdi-plus-thick - Click here to add new cxl-host - - @@ -84,20 +108,7 @@ > -

- CXL-Host - - mdi-sync-circle - Click here to resynchronize this CXL-Host - device - -

+

CXL-Host

💻A CXL-Host device is a Redfish Service agent providing local memory composition.
@@ -626,6 +637,147 @@
+ + + + + + Rename Host + + + + + + +
+ + +
+
+
+
+
+ + + + + Cancel + + + Rename + + +
+
+ + + + +

Rename a Host succeeded!

+

+ New Host Id: +
{{ renamedHostId }} +

+ +
+ + Done + +
+
+
+ + + + +

Rename a Host failed!

+

+ {{ renameHostError }} +

+ +
+ + Done + +
+
+
+ + + + + + + + + + @@ -645,6 +797,8 @@ export default { return { loadProgressText: "Loading the page, please wait...", resyncHostProgressText: "Resynchronizing the CXL-Host, please wait...", + renameHostProgressText: "Renaming the CXL-Host, please wait...", + // The rules for the input fields when adding a new cxl-host rules: { required: (value: any) => !!value || "Field is required", @@ -682,6 +836,40 @@ export default { dialogResyncHostSuccess: false, dialogResyncHostFailure: false, resyncHostError: null as unknown, + + renameHostCredentials: { + customId: "", + }, + renamedHostId: null as unknown as string | undefined, // Be used on success popup + dialogRenameHost: false, + renameHostError: null as unknown, + dialogRenameHostSuccess: false, + dialogRenameHostFailure: false, + dialogRenameHostWait: false, + + hostDropItems: [ + { + text: "Delete", + icon: "mdi-delete", + function: this.deleteHostWindowButton, + id: "deleteHostWindow", + iconColor: "warning", + }, + { + text: "Rename", + icon: "mdi-rename-box", + function: this.renameHost, + id: "renameHostWindow", + iconColor: "primary", + }, + { + text: "Resync", + icon: "mdi-sync-circle", + function: this.resyncHostWindowButton, + id: "resyncHostWindow", + iconColor: "#6ebe4a", + }, + ], }; }, @@ -803,6 +991,48 @@ export default { } }, + renameHost() { + this.dialogRenameHost = true; + }, + + /* Triggle the API hostsUpdateById in host store to rename a host */ + async renameHostConfirm(hostId: string, newHostId: string) { + // Make the rename host popup disappear and waiting popup appear + this.dialogRenameHost = false; + this.dialogRenameHostWait = true; + + const hostStore = useHostStore(); + const newHost = await hostStore.renameHost(hostId, newHostId); + this.renameHostError = hostStore.renameHostError as string; + + if (!this.renameHostError) { + this.renamedHostId = newHost?.id; + + // Set the renamed host as the selected host + const Hosts = computed(() => hostStore.hosts); + if (Hosts.value.length > 0) { + hostStore.selectHost( + newHost?.id + "", + newHost?.ipAddress + "", + Number(newHost?.port), + newHost?.localMemoryMiB, + newHost?.status + ); + } + + this.dialogRenameHostWait = false; + this.dialogRenameHostSuccess = true; + } else { + this.dialogRenameHostWait = false; + this.dialogRenameHostFailure = true; + } + + // Reset the credentials + this.renameHostCredentials = { + customId: "", + }; + }, + // Method to manually update the content for the resync cxl-host async updateHostContent(hostId: string) { const hostPortStore = useHostPortStore(); diff --git a/webui/src/components/Stores/ApplianceStore.ts b/webui/src/components/Stores/ApplianceStore.ts index 530647e..661f777 100644 --- a/webui/src/components/Stores/ApplianceStore.ts +++ b/webui/src/components/Stores/ApplianceStore.ts @@ -13,10 +13,41 @@ export const useApplianceStore = defineStore('appliance', { selectedApplianceId: null as unknown as string, addApplianceError: null as unknown, deleteApplianceError: null as unknown, + renameApplianceError: null as unknown, applianceIds: [] as { id: string, bladeIds: string[] }[], }), 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 = []; @@ -66,6 +97,7 @@ export const useApplianceStore = defineStore('appliance', { 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/BladeStore.ts b/webui/src/components/Stores/BladeStore.ts index bddaee2..bdd30fe 100644 --- a/webui/src/components/Stores/BladeStore.ts +++ b/webui/src/components/Stores/BladeStore.ts @@ -18,6 +18,7 @@ export const useBladeStore = defineStore('blade', { addBladeError: null as unknown, deleteBladeError: null as unknown, resyncBladeError: null as unknown, + renameBladeError: null as unknown, }), actions: { async fetchBlades(applianceId: string) { @@ -68,6 +69,37 @@ export const useBladeStore = defineStore('blade', { } }, + async renameBlade(applianceId: string, bladeId: string, newBladeId: string) { + this.renameBladeError = ""; + + try { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const response = await defaultApi.bladesUpdateById(applianceId, bladeId, newBladeId); + + // Update the blades array + if (response) { + this.blades = this.blades.filter( + (blade) => blade.id !== bladeId + ); + this.blades.push(response.data); + } + + return response.data + } catch (error) { + if (axios.isAxiosError(error)) { + this.renameBladeError = error.message; + if (error.response) { + this.renameBladeError = error.response?.data.status.message + " (" + error.response?.request.status + ")"; + } + } + else { + this.renameBladeError = error; + } + console.error("Error:", error); + } + }, + + async resyncBlade(applianceId: string, bladeId: string) { this.resyncBladeError = ""; try { diff --git a/webui/src/components/Stores/HostStore.ts b/webui/src/components/Stores/HostStore.ts index b90e8f4..11f8607 100644 --- a/webui/src/components/Stores/HostStore.ts +++ b/webui/src/components/Stores/HostStore.ts @@ -18,6 +18,7 @@ export const useHostStore = defineStore('host', { addHostError: null as unknown, deleteHostError: null as unknown, resyncHostError: null as unknown, + renameHostError: null as unknown, hostIds: [] as string[], }), @@ -102,6 +103,37 @@ export const useHostStore = defineStore('host', { } }, + 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 {