mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
webui: fix more wrappers decoding keyed GETs as bare nodes
Three more RESTCONF wrappers assume the response to a keyed GET is the bare node, when the server nests it under the full parent path: the container resource-usage stats always read zero, the WireGuard listen port never populated, and resetting advertised link modes silently re-enabled auto-negotiation on links where it was forced off. Same class of bug as the firewall zone reset fix: the decode matches nothing and the zero value is used as if valid. Model the full nesting in all three wrappers, reusing the existing container list wrapper. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -1369,15 +1369,22 @@ func (h *ConfigureInterfacesHandler) ResetEthernetAdvertised(w http.ResponseWrit
|
|||||||
name := r.PathValue("name")
|
name := r.PathValue("name")
|
||||||
base := ifacePath(name) + "/ieee802-ethernet-interface:ethernet"
|
base := ifacePath(name) + "/ieee802-ethernet-interface:ethernet"
|
||||||
|
|
||||||
|
// Keyed GETs nest the requested node under its full parent path.
|
||||||
var resp struct {
|
var resp struct {
|
||||||
AN struct {
|
Interfaces struct {
|
||||||
Enable *bool `json:"enable"`
|
Interface []struct {
|
||||||
} `json:"ieee802-ethernet-interface:auto-negotiation"`
|
Ethernet struct {
|
||||||
|
AN struct {
|
||||||
|
Enable *bool `json:"enable"`
|
||||||
|
} `json:"auto-negotiation"`
|
||||||
|
} `json:"ieee802-ethernet-interface:ethernet"`
|
||||||
|
} `json:"interface"`
|
||||||
|
} `json:"ietf-interfaces:interfaces"`
|
||||||
}
|
}
|
||||||
enable := true // YANG default
|
enable := true // YANG default
|
||||||
if err := h.RC.Get(r.Context(), base+"/auto-negotiation", &resp); err == nil {
|
if err := h.RC.Get(r.Context(), base+"/auto-negotiation", &resp); err == nil {
|
||||||
if resp.AN.Enable != nil {
|
if ifs := resp.Interfaces.Interface; len(ifs) > 0 && ifs[0].Ethernet.AN.Enable != nil {
|
||||||
enable = *resp.AN.Enable
|
enable = *ifs[0].Ethernet.AN.Enable
|
||||||
}
|
}
|
||||||
} else if !restconf.IsNotFound(err) {
|
} else if !restconf.IsNotFound(err) {
|
||||||
log.Printf("configure interfaces %s reset advertised get: %v", name, err)
|
log.Printf("configure interfaces %s reset advertised get: %v", name, err)
|
||||||
|
|||||||
@@ -46,11 +46,6 @@ type containerListWrapper struct {
|
|||||||
} `json:"infix-containers:containers"`
|
} `json:"infix-containers:containers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerResourceUsageWrapper wraps the RESTCONF resource-usage response.
|
|
||||||
type containerResourceUsageWrapper struct {
|
|
||||||
ResourceUsage containerResourceUsageJSON `json:"infix-containers:resource-usage"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerEntry holds display-ready data for a single container row.
|
// ContainerEntry holds display-ready data for a single container row.
|
||||||
type ContainerEntry struct {
|
type ContainerEntry struct {
|
||||||
Name string
|
Name string
|
||||||
@@ -106,13 +101,16 @@ func (h *ContainersHandler) Overview(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
path := fmt.Sprintf("/data/infix-containers:containers/container=%s/resource-usage",
|
path := fmt.Sprintf("/data/infix-containers:containers/container=%s/resource-usage",
|
||||||
url.PathEscape(name))
|
url.PathEscape(name))
|
||||||
var w containerResourceUsageWrapper
|
var w containerListWrapper // keyed GETs nest under the full parent path
|
||||||
if err := h.RC.Get(ctx, path, &w); err != nil {
|
if err := h.RC.Get(ctx, path, &w); err != nil {
|
||||||
log.Printf("restconf resource-usage %s: %v", name, err)
|
log.Printf("restconf resource-usage %s: %v", name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(w.Containers.Container) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
usages[idx] = w.ResourceUsage
|
usages[idx] = w.Containers.Container[0].ResourceUsage
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
}(i, c.Name)
|
}(i, c.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,13 @@ type wgConfigJSON struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wgIfaceConfigWrapper is used to fetch per-interface WireGuard config.
|
// wgIfaceConfigWrapper is used to fetch per-interface WireGuard config.
|
||||||
|
// Keyed GETs nest the requested node under its full parent path.
|
||||||
type wgIfaceConfigWrapper struct {
|
type wgIfaceConfigWrapper struct {
|
||||||
WireGuard *wgConfigJSON `json:"infix-interfaces:wireguard"`
|
Interfaces struct {
|
||||||
|
Interface []struct {
|
||||||
|
WireGuard *wgConfigJSON `json:"infix-interfaces:wireguard"`
|
||||||
|
} `json:"interface"`
|
||||||
|
} `json:"ietf-interfaces:interfaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WGPeer holds display-ready data for a single WireGuard peer.
|
// WGPeer holds display-ready data for a single WireGuard peer.
|
||||||
@@ -128,8 +133,9 @@ func buildWGTunnel(ctx context.Context, rc *restconf.Client, iface ifaceJSON) WG
|
|||||||
// Fetch ListenPort from config endpoint (separate from oper-state).
|
// Fetch ListenPort from config endpoint (separate from oper-state).
|
||||||
var cfgWrap wgIfaceConfigWrapper
|
var cfgWrap wgIfaceConfigWrapper
|
||||||
path := fmt.Sprintf("/data/ietf-interfaces:interfaces/interface=%s/infix-interfaces:wireguard", iface.Name)
|
path := fmt.Sprintf("/data/ietf-interfaces:interfaces/interface=%s/infix-interfaces:wireguard", iface.Name)
|
||||||
if err := rc.Get(ctx, path, &cfgWrap); err == nil && cfgWrap.WireGuard != nil {
|
if err := rc.Get(ctx, path, &cfgWrap); err == nil &&
|
||||||
tunnel.ListenPort = cfgWrap.WireGuard.ListenPort
|
len(cfgWrap.Interfaces.Interface) > 0 && cfgWrap.Interfaces.Interface[0].WireGuard != nil {
|
||||||
|
tunnel.ListenPort = cfgWrap.Interfaces.Interface[0].WireGuard.ListenPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build peers from embedded peer-status.
|
// Build peers from embedded peer-status.
|
||||||
|
|||||||
Reference in New Issue
Block a user