@cryptotaxi247 / netdata / commits / 43f27e1bb

fix(ap): handle SSIDs containing whitespace (#22472)

* Initial plan * fix(ap): handle SSIDs with whitespace in iw dev output parsing Agent-Logs-Url: https://github.com/netdata/netdata/sessions/dda66a5c-6ba0-4c7c-87fd-f7d4fab3a147 Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> * style(ap): gofmt collector_test.go --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> Co-authored-by: ilyam8 <ilya@netdata.cloud>

Copilot committed May 12, 2026 at 20:17 UTC 43f27e1bbad4944e48b0ebfc6eea1e630e3bbdaa
4 files changed +53 -12
src/go/plugin/go.d/collector/ap/charts.go
+1 -1
@@ -123,7 +123,7 @@ func (c *Collector) addInterfaceCharts(dev *iwInterface) {
123 {Key: "ssid", Value: dev.ssid},
124 }
125 for _, dim := range chart.Dims {
126 - dim.ID = fmt.Sprintf(dim.ID, dev.name, dev.ssid)
126 + dim.ID = fmt.Sprintf(dim.ID, dev.name, cleanSSID(dev.ssid))
127 }
128 }
129
src/go/plugin/go.d/collector/ap/collect.go
+4 -4
@@ -70,7 +70,7 @@ func (c *Collector) collect() (map[string]int64, error) {
70 c.addInterfaceCharts(iface)
71 }
72
73 - px := fmt.Sprintf("ap_%s_%s_", iface.name, iface.ssid)
73 + px := fmt.Sprintf("ap_%s_%s_", iface.name, cleanSSID(iface.ssid))
74
75 if stats.clients != nil {
76 mx[px+"clients"] = *stats.clients
@@ -139,11 +139,11 @@ func parseIwDevices(resp []byte) ([]*iwInterface, error) {
139 ifaces[name] = iface
140 }
141 case strings.HasPrefix(line, "ssid") && iface != nil:
142 - parts := strings.Fields(line)
143 - if len(parts) != 2 {
142 + ssid := strings.TrimSpace(strings.TrimPrefix(line, "ssid"))
143 + if ssid == "" {
144 return nil, fmt.Errorf("invalid ssid line: '%s'", line)
145 }
146 - iface.ssid = parts[1]
146 + iface.ssid = ssid
147 case strings.HasPrefix(line, "type") && iface != nil:
148 parts := strings.Fields(line)
149 if len(parts) != 2 {
src/go/plugin/go.d/collector/ap/collector_test.go
+36 -7
@@ -22,17 +22,19 @@ var (
22
23 dataIwDevManaged, _ = os.ReadFile("testdata/iw_dev_managed.txt")
24
25 - dataIwDevAP, _ = os.ReadFile("testdata/iw_dev_ap.txt")
26 - dataIwStationDump, _ = os.ReadFile("testdata/station_dump.txt")
25 + dataIwDevAP, _ = os.ReadFile("testdata/iw_dev_ap.txt")
26 + dataIwDevAPSsidWithSpaces, _ = os.ReadFile("testdata/iw_dev_ap_ssid_with_spaces.txt")
27 + dataIwStationDump, _ = os.ReadFile("testdata/station_dump.txt")
28 )
29
30 func Test_testDataIsValid(t *testing.T) {
31 for name, data := range map[string][]byte{
31 - "dataConfigJSON": dataConfigJSON,
32 - "dataConfigYAML": dataConfigYAML,
33 - "dataIwDevManaged": dataIwDevManaged,
34 - "dataIwDevAP": dataIwDevAP,
35 - "dataIwStationDump": dataIwStationDump,
32 + "dataConfigJSON": dataConfigJSON,
33 + "dataConfigYAML": dataConfigYAML,
34 + "dataIwDevManaged": dataIwDevManaged,
35 + "dataIwDevAP": dataIwDevAP,
36 + "dataIwDevAPSsidWithSpaces": dataIwDevAPSsidWithSpaces,
37 + "dataIwStationDump": dataIwStationDump,
38 } {
39 require.NotNil(t, data, name)
40 }
@@ -124,6 +126,10 @@ func TestCollector_Check(t *testing.T) {
126 wantFail: false,
127 prepareMock: prepareMockOk,
128 },
129 + "success case ssid with spaces": {
130 + wantFail: false,
131 + prepareMock: prepareMockOkSsidWithSpaces,
132 + },
133 "no ap devices": {
134 wantFail: true,
135 prepareMock: prepareMockNoAPDevices,
@@ -188,6 +194,22 @@ func TestCollector_Collect(t *testing.T) {
194 "ap_wlp1s1_testing_packets_sent": 51,
195 },
196 },
197 + "success case ssid with spaces": {
198 + prepareMock: prepareMockOkSsidWithSpaces,
199 + wantCharts: len(apChartsTmpl) * 1,
200 + wantMetrics: map[string]int64{
201 + "ap_wlp1s0_My_AP_1_average_signal": -50666,
202 + "ap_wlp1s0_My_AP_1_bitrate_receive": 49400,
203 + "ap_wlp1s0_My_AP_1_bitrate_transmit": 43333,
204 + "ap_wlp1s0_My_AP_1_bw_received": 101822,
205 + "ap_wlp1s0_My_AP_1_bw_sent": 9284,
206 + "ap_wlp1s0_My_AP_1_clients": 3,
207 + "ap_wlp1s0_My_AP_1_issues_failures": 1,
208 + "ap_wlp1s0_My_AP_1_issues_retries": 1,
209 + "ap_wlp1s0_My_AP_1_packets_received": 2670,
210 + "ap_wlp1s0_My_AP_1_packets_sent": 51,
211 + },
212 + },
213 "no ap devices": {
214 prepareMock: prepareMockNoAPDevices,
215 wantMetrics: nil,
@@ -229,6 +251,13 @@ func prepareMockOk() *mockIwExec {
251 }
252 }
253
254 +func prepareMockOkSsidWithSpaces() *mockIwExec {
255 + return &mockIwExec{
256 + devicesData: dataIwDevAPSsidWithSpaces,
257 + stationStatsData: dataIwStationDump,
258 + }
259 +}
260 +
261 func prepareMockNoAPDevices() *mockIwExec {
262 return &mockIwExec{
263 devicesData: dataIwDevManaged,
src/go/plugin/go.d/collector/ap/testdata/iw_dev_ap_ssid_with_spaces.txt new
+12
@@ -0,0 +1,12 @@
1 +phy#0
2 + Interface wlp1s0
3 + ifindex 2
4 + wdev 0x1
5 + addr 28:cd:c4:b8:63:cb
6 + ssid My AP 1
7 + type AP
8 + channel 1 (2412 MHz), width: 20 MHz, center1: 2412 MHz
9 + txpower 20.00 dBm
10 + multicast TXQ:
11 + qsz-byt qsz-pkt flows drops marks overlmt hashcol tx-bytes tx-packets
12 + 0 0 2 0 0 0 0 16447 226