Merge ~danilogondolfo/netplan:mantic_lp1809994 into ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu-mantic

Proposed by Danilo Egea Gondolfo
Status: Merged
Merged at revision: fb6294c66694e31bcb7b8aa29e9e4ec71203eee8
Proposed branch: ~danilogondolfo/netplan:mantic_lp1809994
Merge into: ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu-mantic
Diff against target: 319 lines (+291/-0)
4 files modified
debian/changelog (+13/-0)
debian/patches/lp1809994/0010-parse-improve-the-parsing-of-access-points-LP-180999.patch (+197/-0)
debian/patches/lp1809994/0011-wifi-replace-the-previously-defined-AP-with-the-new-.patch (+79/-0)
debian/patches/series (+2/-0)
Reviewer Review Type Date Requested Status
Lukas Märdian Approve
Review via email: mp+455887@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Lukas Märdian (slyon) wrote :

Tests are looking good and this matches the fix that we have in Noble.

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/debian/changelog b/debian/changelog
2index cc632ba..6774ec3 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,3 +1,16 @@
6+netplan.io (0.107-5ubuntu0.2) mantic; urgency=medium
7+
8+ * lp1809994/0010-parse-improve-the-parsing-of-access-points-LP-180999.patch:
9+ Improve access-points parsing. Properly handle access-points if a second
10+ pass if required in the parser and not report them as duplicates
11+ (LP: #1809994).
12+ * lp1809994/0011-wifi-replace-the-previously-defined-AP-with-the-new-.patch:
13+ Overwrite the access-point object if another access-point with the same
14+ name is found instead of ignoring the new one. This behavior is closer
15+ to the merging process Netplan does.
16+
17+ -- Danilo Egea Gondolfo <danilo.egea.gondolfo@canonical.com> Wed, 15 Nov 2023 10:58:49 +0000
18+
19 netplan.io (0.107-5ubuntu0.1) mantic; urgency=medium
20
21 * d/p/lp2039821/0008-wireguard-ignore-empty-endpoints.patch (LP: #2039821)
22diff --git a/debian/patches/lp1809994/0010-parse-improve-the-parsing-of-access-points-LP-180999.patch b/debian/patches/lp1809994/0010-parse-improve-the-parsing-of-access-points-LP-180999.patch
23new file mode 100644
24index 0000000..85392c7
25--- /dev/null
26+++ b/debian/patches/lp1809994/0010-parse-improve-the-parsing-of-access-points-LP-180999.patch
27@@ -0,0 +1,197 @@
28+From: Danilo Egea Gondolfo <danilo.egea.gondolfo@canonical.com>
29+Date: Fri, 6 Oct 2023 11:42:36 +0100
30+Subject: parse: improve the parsing of access-points (LP: #1809994)
31+
32+When the parser requires a second pass, it will report access-points it
33+already parsed as duplicates and error out.
34+
35+With this change, access-points will be stored in a local hash table,
36+which will be used to find duplicates in the same netdef, and moved to
37+the netdef->access_points hash table in the end. Access-points already
38+present in the netdef hash table will be considered already parsed and
39+skipped.
40+
41+Add a test to check the problem is fixed and another one to test if
42+merging the access-points when the same interface is present in multiple
43+files is working properly.
44+
45+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/netplan.io/+bug/1809994
46+Origin: https://github.com/canonical/netplan/pull/413
47+---
48+ src/parse.c | 45 +++++++++++++++++++---------
49+ src/types-internal.h | 3 ++
50+ src/types.c | 2 +-
51+ tests/generator/test_common.py | 54 ++++++++++++++++++++++++++++++++++
52+ 4 files changed, 89 insertions(+), 15 deletions(-)
53+
54+--- a/src/parse.c
55++++ b/src/parse.c
56+@@ -59,6 +59,9 @@
57+
58+ NetplanParser global_parser = {0};
59+
60++static gboolean
61++insert_kv_into_hash(void *key, void *value, void *hash);
62++
63+ /**
64+ * Load YAML file into a yaml_document_t.
65+ *
66+@@ -1404,11 +1407,13 @@
67+ static gboolean
68+ handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, __unused const void* data, GError** error)
69+ {
70++ GHashTable* access_points = g_hash_table_new(g_str_hash, g_str_equal);
71++
72+ for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) {
73+ NetplanWifiAccessPoint *access_point = NULL;
74+ g_autofree char* full_key = NULL;
75+ yaml_node_t* key, *value;
76+- gboolean ret = TRUE;
77++ const gchar* ssid;
78+
79+ key = yaml_document_get_node(&npp->doc, entry->key);
80+ assert_type(npp, key, YAML_SCALAR_NODE);
81+@@ -1421,31 +1426,43 @@
82+ continue;
83+ }
84+
85++ ssid = scalar(key);
86++
87++ /* Skip access-points that already exist in the netdef */
88++ if (npp->current.netdef->access_points && g_hash_table_contains(npp->current.netdef->access_points, ssid))
89++ continue;
90++
91++ /* Check if there's already an SSID with that name in the list of APs we are parsing */
92++ if (g_hash_table_contains(access_points, ssid)) {
93++ g_hash_table_foreach(access_points, free_access_point, NULL);
94++ g_hash_table_destroy(access_points);
95++ return yaml_error(npp, key, error, "%s: Duplicate access point SSID '%s'", npp->current.netdef->id, ssid);
96++ }
97++
98+ g_assert(access_point == NULL);
99+ access_point = g_new0(NetplanWifiAccessPoint, 1);
100+- access_point->ssid = g_strdup(scalar(key));
101++ access_point->ssid = g_strdup(ssid);
102+ g_debug("%s: adding wifi AP '%s'", npp->current.netdef->id, access_point->ssid);
103+
104+- /* Check if there's already an SSID with that name */
105+- // FIXME: This check fails on multi-pass parsing, e.g. when defined in
106+- // the same YAML file with a set of virtual-ethernets peers.
107+- if (npp->current.netdef->access_points &&
108+- g_hash_table_lookup(npp->current.netdef->access_points, access_point->ssid)) {
109+- ret = yaml_error(npp, key, error, "%s: Duplicate access point SSID '%s'", npp->current.netdef->id, access_point->ssid);
110+- }
111+-
112+ npp->current.access_point = access_point;
113+- if (!ret || !process_mapping(npp, value, full_key, wifi_access_point_handlers, NULL, error)) {
114++ if (!process_mapping(npp, value, full_key, wifi_access_point_handlers, NULL, error)) {
115+ access_point_clear(&npp->current.access_point, npp->current.backend);
116++ g_hash_table_foreach(access_points, free_access_point, NULL);
117++ g_hash_table_destroy(access_points);
118+ return FALSE;
119+ }
120+
121++ g_hash_table_insert(access_points, access_point->ssid, access_point);
122++ npp->current.access_point = NULL;
123++ }
124++
125++ if (g_hash_table_size(access_points) > 0) {
126+ if (!npp->current.netdef->access_points)
127+ npp->current.netdef->access_points = g_hash_table_new(g_str_hash, g_str_equal);
128+- g_hash_table_insert(npp->current.netdef->access_points, access_point->ssid, access_point);
129+- npp->current.access_point = NULL;
130++ g_hash_table_foreach_steal(access_points, insert_kv_into_hash, npp->current.netdef->access_points);
131++ mark_data_as_dirty(npp, &npp->current.netdef->access_points);
132+ }
133+- mark_data_as_dirty(npp, &npp->current.netdef->access_points);
134++ g_hash_table_destroy(access_points);
135+ return TRUE;
136+ }
137+
138+--- a/src/types-internal.h
139++++ b/src/types-internal.h
140+@@ -313,3 +313,6 @@
141+
142+ void
143+ free_address_options(void* ptr);
144++
145++void
146++free_access_point(void* key, void* value, void* data);
147+--- a/src/types.c
148++++ b/src/types.c
149+@@ -200,7 +200,7 @@
150+ * @data: pointer to a NetplanBackend value representing the renderer context in which
151+ * to interpret the processed object, especially regarding the backend settings
152+ */
153+-static void
154++void
155+ free_access_point(__unused void* key, void* value, __unused void* data)
156+ {
157+ NetplanWifiAccessPoint* ap = value;
158+--- a/tests/generator/test_common.py
159++++ b/tests/generator/test_common.py
160+@@ -1557,6 +1557,28 @@
161+ ''',
162+ 'eth0.network': ND_EMPTY % ('eth0', 'no') + 'Bond=aggi\n'})
163+
164++ def test_check_parser_second_pass_will_not_lead_to_duplicate_access_point(self):
165++ '''
166++ When the parser needs more than one pass we shouldn't
167++ try to load the same access-point again from wifi devices.
168++ Testcase for LP: #1809994'''
169++
170++ self.generate('''network:
171++ bridges:
172++ br0:
173++ interfaces:
174++ - eth0
175++ ethernets:
176++ eth0:
177++ dhcp4: false
178++ wifis:
179++ wlan0:
180++ dhcp4: true
181++ access-points:
182++ "mywifi":
183++ password: "aaaaaaaa"
184++''')
185++
186+
187+ class TestMerging(TestBase):
188+ '''multiple *.yaml merging'''
189+@@ -1740,3 +1762,35 @@
190+ ''',
191+ 'enyellow.network': ND_DHCP4 % 'enyellow',
192+ 'enblue.network': ND_DHCP4 % 'enblue'})
193++
194++ def test_wifi_access_points_merging(self):
195++ self.generate('''network:
196++ version: 2
197++ wifis:
198++ wlan0:
199++ dhcp4: true
200++ access-points:
201++ "mywifi":
202++ password: "aaaaaaaa"''',
203++ confs={'newwifi': '''network:
204++ version: 2
205++ wifis:
206++ wlan0:
207++ dhcp4: true
208++ access-points:
209++ "mynewwifi":
210++ password: "aaaaaaaa"'''})
211++
212++ self.assert_wpa_supplicant("wlan0", """ctrl_interface=/run/wpa_supplicant
213++
214++network={
215++ ssid="mynewwifi"
216++ key_mgmt=WPA-PSK
217++ psk="aaaaaaaa"
218++}
219++network={
220++ ssid="mywifi"
221++ key_mgmt=WPA-PSK
222++ psk="aaaaaaaa"
223++}
224++""")
225diff --git a/debian/patches/lp1809994/0011-wifi-replace-the-previously-defined-AP-with-the-new-.patch b/debian/patches/lp1809994/0011-wifi-replace-the-previously-defined-AP-with-the-new-.patch
226new file mode 100644
227index 0000000..c03521e
228--- /dev/null
229+++ b/debian/patches/lp1809994/0011-wifi-replace-the-previously-defined-AP-with-the-new-.patch
230@@ -0,0 +1,79 @@
231+From: Danilo Egea Gondolfo <danilo.egea.gondolfo@canonical.com>
232+Date: Thu, 12 Oct 2023 18:07:31 +0100
233+Subject: wifi: replace the previously defined AP with the new one
234+
235+If we find an AP with the same name in the same interface, we drop the
236+first one and use the new one. This is done to maintain the documented
237+behavior of respecting the order in which the files are parsed. We still
238+need to implement support for merging AP settings.
239+
240+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/netplan.io/+bug/1809994
241+Origin: https://github.com/canonical/netplan/pull/413
242+---
243+ src/parse.c | 17 +++++++++++++----
244+ tests/generator/test_common.py | 30 ++++++++++++++++++++++++++++++
245+ 2 files changed, 43 insertions(+), 4 deletions(-)
246+
247+--- a/src/parse.c
248++++ b/src/parse.c
249+@@ -1428,11 +1428,20 @@
250+
251+ ssid = scalar(key);
252+
253+- /* Skip access-points that already exist in the netdef */
254+- if (npp->current.netdef->access_points && g_hash_table_contains(npp->current.netdef->access_points, ssid))
255+- continue;
256++ /*
257++ * Delete the access-point if it already exists in the netdef and let the new
258++ * one be added. It has the side effect of reprocessing APs if the parser requires a
259++ * second pass.
260++ *
261++ * TODO: implement support for merging AP settings if they were previously defined
262++ */
263++ if (npp->current.netdef->access_points && g_hash_table_contains(npp->current.netdef->access_points, ssid)) {
264++ NetplanWifiAccessPoint *ap = g_hash_table_lookup(npp->current.netdef->access_points, ssid);
265++ g_hash_table_remove(npp->current.netdef->access_points, ssid);
266++ free_access_point(NULL, ap, NULL);
267++ }
268+
269+- /* Check if there's already an SSID with that name in the list of APs we are parsing */
270++ /* Check if the SSID was already defined in the same netdef in this YAML file we are parsing */
271+ if (g_hash_table_contains(access_points, ssid)) {
272+ g_hash_table_foreach(access_points, free_access_point, NULL);
273+ g_hash_table_destroy(access_points);
274+--- a/tests/generator/test_common.py
275++++ b/tests/generator/test_common.py
276+@@ -1794,3 +1794,33 @@
277+ psk="aaaaaaaa"
278+ }
279+ """)
280++
281++ def test_wifi_access_points_overwriting(self):
282++ ''' If we find an AP that is already defined we drop the first one.
283++ XXX: this test must be removed once we implement support for AP merging
284++ '''
285++ self.generate('''network:
286++ version: 2
287++ wifis:
288++ wlan0:
289++ dhcp4: true
290++ access-points:
291++ "mywifi":
292++ password: "aaaaaaaa"''',
293++ confs={'newwifi': '''network:
294++ version: 2
295++ wifis:
296++ wlan0:
297++ dhcp4: true
298++ access-points:
299++ "mywifi":
300++ password: "bbbbbbbb"'''})
301++
302++ self.assert_wpa_supplicant("wlan0", """ctrl_interface=/run/wpa_supplicant
303++
304++network={
305++ ssid="mywifi"
306++ key_mgmt=WPA-PSK
307++ psk="bbbbbbbb"
308++}
309++""")
310diff --git a/debian/patches/series b/debian/patches/series
311index 837291f..4d9d078 100644
312--- a/debian/patches/series
313+++ b/debian/patches/series
314@@ -7,3 +7,5 @@ lp2034595/0006-apply-bring-lo-back-up-if-it-s-managed-by-NM.patch
315 lp2034595/0007-apply-don-t-assume-the-NM-loopback-connection-is-cal.patch
316 lp2039821/0008-wireguard-ignore-empty-endpoints.patch
317 lp2039825/0009-auth-add-support-for-LEAP-and-EAP-PWD.patch
318+lp1809994/0010-parse-improve-the-parsing-of-access-points-LP-180999.patch
319+lp1809994/0011-wifi-replace-the-previously-defined-AP-with-the-new-.patch

Subscribers

People subscribed via source and target branches