From e466ae8a6a00097f33aaa0eae7cbe9e47604258a Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 5 Aug 2026 10:45:19 +0200 Subject: [PATCH 1/2] Fix listing virtual machines with vnfnics --- cloudstack/AffinityGroupService.go | 2 +- cloudstack/BackupService.go | 2 +- cloudstack/ISOService.go | 4 +- cloudstack/NicService.go | 2 +- cloudstack/SSHService.go | 2 +- cloudstack/SnapshotService.go | 2 +- cloudstack/VirtualMachineService.go | 42 ++++++++++---------- cloudstack/VirtualNetworkFunctionsService.go | 14 ++++++- generate/generate.go | 17 ++++++++ 9 files changed, 57 insertions(+), 30 deletions(-) diff --git a/cloudstack/AffinityGroupService.go b/cloudstack/AffinityGroupService.go index afd1767..c635a39 100644 --- a/cloudstack/AffinityGroupService.go +++ b/cloudstack/AffinityGroupService.go @@ -1232,7 +1232,7 @@ type UpdateVMAffinityGroupResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/BackupService.go b/cloudstack/BackupService.go index 92033d8..9891473 100644 --- a/cloudstack/BackupService.go +++ b/cloudstack/BackupService.go @@ -2301,7 +2301,7 @@ type CreateVMFromBackupResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/ISOService.go b/cloudstack/ISOService.go index d98f004..eb8f483 100644 --- a/cloudstack/ISOService.go +++ b/cloudstack/ISOService.go @@ -290,7 +290,7 @@ type AttachIsoResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -924,7 +924,7 @@ type DetachIsoResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/NicService.go b/cloudstack/NicService.go index 3fe8f33..d4f316f 100644 --- a/cloudstack/NicService.go +++ b/cloudstack/NicService.go @@ -695,7 +695,7 @@ type UpdateVmNicIpResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/SSHService.go b/cloudstack/SSHService.go index 30038d4..a250859 100644 --- a/cloudstack/SSHService.go +++ b/cloudstack/SSHService.go @@ -1245,7 +1245,7 @@ type ResetSSHKeyForVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/SnapshotService.go b/cloudstack/SnapshotService.go index ab11f36..902a927 100644 --- a/cloudstack/SnapshotService.go +++ b/cloudstack/SnapshotService.go @@ -3849,7 +3849,7 @@ type RevertToVMSnapshotResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/VirtualMachineService.go b/cloudstack/VirtualMachineService.go index 675b5c8..f10e928 100644 --- a/cloudstack/VirtualMachineService.go +++ b/cloudstack/VirtualMachineService.go @@ -397,7 +397,7 @@ type AddNicToVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -760,7 +760,7 @@ type AssignVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -1153,7 +1153,7 @@ type ChangeServiceForVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -2937,7 +2937,7 @@ type DeployVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -3248,7 +3248,7 @@ type DestroyVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -4800,7 +4800,7 @@ type VirtualMachine struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -6211,7 +6211,7 @@ type VirtualMachinesMetric struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -6817,7 +6817,7 @@ type MigrateVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -7170,7 +7170,7 @@ type MigrateVirtualMachineWithVolumeResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -7481,7 +7481,7 @@ type RebootVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -7722,7 +7722,7 @@ type RecoverVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -8008,7 +8008,7 @@ type RemoveNicFromVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -8293,7 +8293,7 @@ type ResetPasswordForVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -8681,7 +8681,7 @@ type ResetUserDataForVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -9067,7 +9067,7 @@ type RestoreVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -9708,7 +9708,7 @@ type StartVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -9994,7 +9994,7 @@ type StopVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -10280,7 +10280,7 @@ type UpdateDefaultNicForVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -11058,7 +11058,7 @@ type UpdateVirtualMachineResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -12581,7 +12581,7 @@ type ImportVmResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -13532,7 +13532,7 @@ type ImportUnmanagedInstanceResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/cloudstack/VirtualNetworkFunctionsService.go b/cloudstack/VirtualNetworkFunctionsService.go index 90f7dee..6890a48 100644 --- a/cloudstack/VirtualNetworkFunctionsService.go +++ b/cloudstack/VirtualNetworkFunctionsService.go @@ -1810,7 +1810,7 @@ type DeployVnfApplianceResponse struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } @@ -3009,6 +3009,16 @@ func (s *VirtualNetworkFunctionsService) ListVnfAppliances(p *ListVnfAppliancesP return &r, nil } +type VnfNic struct { + Deviceid int64 `json:"deviceid"` + Description string `json:"description"` + Management bool `json:"management"` + Name string `json:"name"` + Networkid string `json:"networkid"` + Networkname string `json:"networkname"` + Required bool `json:"required"` +} + type ListVnfAppliancesResponse struct { Count int `json:"count"` VnfAppliances []*VnfAppliance `json:"vnfappliance"` @@ -3118,7 +3128,7 @@ type VnfAppliance struct { Videoram int64 `json:"videoram"` Vmtype string `json:"vmtype"` Vnfdetails map[string]string `json:"vnfdetails"` - Vnfnics []string `json:"vnfnics"` + Vnfnics []*VnfNic `json:"vnfnics"` Zoneid string `json:"zoneid"` Zonename string `json:"zonename"` } diff --git a/generate/generate.go b/generate/generate.go index c967967..23647fb 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -2009,6 +2009,20 @@ func (s *service) generateResponseType(a *API) { pn("") return } + if a.Name == "listVnfAppliances" { + // The API docs do not describe the shape of the "vnfnics" field, so this + // type is hand maintained to mirror org.apache.cloudstack.api.response.VnfNicResponse. + pn("type VnfNic struct {") + pn(" Deviceid int64 `json:\"deviceid\"`") + pn(" Description string `json:\"description\"`") + pn(" Management bool `json:\"management\"`") + pn(" Name string `json:\"name\"`") + pn(" Networkid string `json:\"networkid\"`") + pn(" Networkname string `json:\"networkname\"`") + pn(" Required bool `json:\"required\"`") + pn("}") + pn("") + } ln := capitalize(strings.TrimPrefix(a.Name, "list")) @@ -2403,6 +2417,9 @@ func mapType(aName string, pName string, pType string) string { if pName == "scaledownpolicies" || pName == "scaleuppolicies" { return "[]*AutoScalePolicy" } + if pName == "vnfnics" { + return "[]*VnfNic" + } return "[]string" case "map": if mapRequireList[aName] != nil && mapRequireList[aName][pName] { From 4ce5f9f5e63b2dec39f575b1b4938c098650128c Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Mon, 10 Aug 2026 19:56:18 +0530 Subject: [PATCH 2/2] Fix response key for ListVnfTemplates and ListVnfAppliances Both list structs took their json tag from the API name, so they looked for "vnftemplate" and "vnfappliance". Neither key exists in the server response: ListVnfTemplatesCmd is an empty subclass of ListTemplatesCmd and returns its items under "template", and ListVnfAppliancesCmd inherits execute() from ListVMsCmd and returns its items under "virtualmachine". Both calls therefore returned a correct Count with an always-empty slice, which also made GetVnfTemplateByName/ByID and GetVnfApplianceByName/ByID always report that the resource was not found. Adds both to the existing switch of APIs whose response key differs from the API name, alongside cases like listVirtualMachinesUsageHistory, and regenerates. Verified against a 4.22.1.0 server response that returns one VNF template: it decodes to one element instead of zero. --- cloudstack/VirtualNetworkFunctionsService.go | 4 ++-- generate/generate.go | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cloudstack/VirtualNetworkFunctionsService.go b/cloudstack/VirtualNetworkFunctionsService.go index 6890a48..a50af3e 100644 --- a/cloudstack/VirtualNetworkFunctionsService.go +++ b/cloudstack/VirtualNetworkFunctionsService.go @@ -3021,7 +3021,7 @@ type VnfNic struct { type ListVnfAppliancesResponse struct { Count int `json:"count"` - VnfAppliances []*VnfAppliance `json:"vnfappliance"` + VnfAppliances []*VnfAppliance `json:"virtualmachine"` } type VnfAppliance struct { @@ -3992,7 +3992,7 @@ func (s *VirtualNetworkFunctionsService) ListVnfTemplates(p *ListVnfTemplatesPar type ListVnfTemplatesResponse struct { Count int `json:"count"` - VnfTemplates []*VnfTemplate `json:"vnftemplate"` + VnfTemplates []*VnfTemplate `json:"template"` } type VnfTemplate struct { diff --git a/generate/generate.go b/generate/generate.go index 23647fb..3cc8b0f 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -2122,6 +2122,16 @@ func (s *service) generateResponseType(a *API) { case "listLBStickinessPolicies": pn(" Count int `json:\"count\"`") pn(" %s []*%s `json:\"%s\"`", ln, parseSingular(ln), "stickinesspolicies") + case "listVnfTemplates": + // ListVnfTemplatesCmd is an empty subclass of ListTemplatesCmd, so the + // server returns the items under "template", not "vnftemplate". + pn(" Count int `json:\"count\"`") + pn(" %s []*%s `json:\"%s\"`", ln, parseSingular(ln), "template") + case "listVnfAppliances": + // ListVnfAppliancesCmd inherits execute() from ListVMsCmd, so the server + // returns the items under "virtualmachine", not "vnfappliance". + pn(" Count int `json:\"count\"`") + pn(" %s []*%s `json:\"%s\"`", ln, parseSingular(ln), "virtualmachine") default: pn(" Count int `json:\"count\"`") pn(" %s []*%s `json:\"%s\"`", ln, parseSingular(ln), strings.ToLower(parseSingular(ln)))