Merge lp:~jtv/maas/move-udev-code into lp:~maas-committers/maas/trunk

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 3115
Proposed branch: lp:~jtv/maas/move-udev-code
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 285 lines (+169/-77)
4 files modified
src/maasserver/networking_preseed.py (+0/-62)
src/maasserver/tests/test_networking_preseed.py (+0/-15)
src/provisioningserver/tests/test_udev.py (+89/-0)
src/provisioningserver/udev.py (+80/-0)
To merge this branch: bzr merge lp:~jtv/maas/move-udev-code
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+236277@code.launchpad.net

Commit message

Move the code for generating udev rules server-side from maasserver to provisioningserver. Needed to allow the OS-specific code in the cluster to generate its own Curtin networking preseed.

Description of the change

The code just moves unchanged, but I extended the test coverage. On a sidenote, it's nice to have some more of the code (and especially tests) clearly independent from the database.

Jeroen

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/networking_preseed.py'
2--- src/maasserver/networking_preseed.py 2014-09-26 11:35:28 +0000
3+++ src/maasserver/networking_preseed.py 2014-09-29 03:38:08 +0000
4@@ -29,7 +29,6 @@
5 ]
6
7 from collections import defaultdict
8-from textwrap import dedent
9
10 from lxml import etree
11 from maasserver.dns.zonegenerator import get_dns_server_address
12@@ -381,64 +380,3 @@
13 compose_debian_network_interfaces_ipv6_stanza(
14 interface, static_ipv6, gateway))
15 return '%s\n' % '\n\n'.join(stanzas)
16-
17-
18-def compose_udev_equality(key, value):
19- """Return a udev comparison clause, like `ACTION=="add"`."""
20- assert key == key.upper()
21- return '%s=="%s"' % (key, value)
22-
23-
24-def compose_udev_attr_equality(attribute, value):
25- """Return a udev attribute comparison clause, like `ATTR{type}=="1"`."""
26- assert attribute == attribute.lower()
27- return 'ATTR{%s}=="%s"' % (attribute, value)
28-
29-
30-def compose_udev_setting(key, value):
31- """Return a udev assignment clause, like `NAME="eth0"`."""
32- assert key == key.upper()
33- return '%s="%s"' % (key, value)
34-
35-
36-def compose_udev_rule(interface, mac):
37- """Return a udev rule to set the name of network interface with `mac`.
38-
39- The rule ends up as a single line looking something like:
40-
41- SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*",
42- ATTR{address}="ff:ee:dd:cc:bb:aa", NAME="eth0"
43-
44- (Note the difference between `=` and `==`: they both occur.)
45- """
46- rule = ', '.join([
47- compose_udev_equality('SUBSYSTEM', 'net'),
48- compose_udev_equality('ACTION', 'add'),
49- compose_udev_equality('DRIVERS', '?*'),
50- compose_udev_attr_equality('address', mac),
51- compose_udev_setting('NAME', interface),
52- ])
53- return '%s\n' % rule
54-
55-
56-def compose_linux_udev_rules_file(interfaces):
57- """Return text for a udev persistent-net rules file.
58-
59- These rules assign fixed names to network interfaces. They ensure that
60- the same network interface cards come up with the same interface names on
61- every boot. Otherwise, the kernel may assign interface names in different
62- orders on every boot, and so network interfaces can "switch identities"
63- every other time the machine reboots.
64-
65- :param interfaces: List of tuples of interface name and MAC address.
66- :return: Text to write into a udev rules file.
67- """
68- rules = [
69- compose_udev_rule(interface, mac)
70- for interface, mac in interfaces
71- ]
72- return dedent("""\
73- # MAAS-assigned network interface names.
74- %s
75- # End of MAAS-assigned network interface names.
76- """) % '\n\n'.join(rules)
77
78=== modified file 'src/maasserver/tests/test_networking_preseed.py'
79--- src/maasserver/tests/test_networking_preseed.py 2014-09-26 11:35:28 +0000
80+++ src/maasserver/tests/test_networking_preseed.py 2014-09-29 03:38:08 +0000
81@@ -31,7 +31,6 @@
82 compose_debian_network_interfaces_file,
83 compose_debian_network_interfaces_ipv4_stanza,
84 compose_debian_network_interfaces_ipv6_stanza,
85- compose_linux_udev_rules_file,
86 extract_mac_string,
87 extract_network_interfaces,
88 generate_dns_server_entry,
89@@ -886,17 +885,3 @@
90
91 self.assertIn('auto %s' % interface, interfaces_file)
92 self.assertEqual(1, interfaces_file.count('auto %s' % interface))
93-
94-
95-class TestComposeLinuxUdevRulesFile(MAASServerTestCase):
96-
97- def test__generates_udev_rule(self):
98- interface = factory.make_name('eth')
99- mac = factory.make_mac_address()
100- expected_rule = (
101- '\nSUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
102- 'ATTR{address}=="%(mac)s", NAME="%(interface)s"\n'
103- ) % {'mac': mac, 'interface': interface}
104- self.assertIn(
105- expected_rule,
106- compose_linux_udev_rules_file([(interface, mac)]))
107
108=== added file 'src/provisioningserver/tests/test_udev.py'
109--- src/provisioningserver/tests/test_udev.py 1970-01-01 00:00:00 +0000
110+++ src/provisioningserver/tests/test_udev.py 2014-09-29 03:38:08 +0000
111@@ -0,0 +1,89 @@
112+# Copyright 2014 Canonical Ltd. This software is licensed under the
113+# GNU Affero General Public License version 3 (see the file LICENSE).
114+
115+"""Tests for udev rules generation code."""
116+
117+from __future__ import (
118+ absolute_import,
119+ print_function,
120+ unicode_literals,
121+ )
122+
123+str = None
124+
125+__metaclass__ = type
126+__all__ = []
127+
128+from maastesting.factory import factory
129+from maastesting.testcase import MAASTestCase
130+from provisioningserver.udev import (
131+ compose_network_interfaces_udev_rules,
132+ compose_udev_attr_equality,
133+ compose_udev_equality,
134+ compose_udev_rule,
135+ compose_udev_setting,
136+ )
137+from testtools.matchers import ContainsAll
138+
139+
140+class TestComposeUdevEquality(MAASTestCase):
141+
142+ def test__generates_comparison_with_double_equals_sign(self):
143+ self.assertEqual('KEY=="value"', compose_udev_equality('KEY', 'value'))
144+
145+ def test__rejects_lower_case_letters_in_key(self):
146+ self.assertRaises(
147+ AssertionError,
148+ compose_udev_equality, 'key', 'value')
149+
150+
151+class TestComposeUdevAttrEquality(MAASTestCase):
152+
153+ def test__generates_comparison_with_double_equals_sign(self):
154+ self.assertEqual(
155+ 'ATTR{key}=="value"',
156+ compose_udev_attr_equality('key', 'value'))
157+
158+ def test__rejects_upper_case_letters_in_key(self):
159+ self.assertRaises(
160+ AssertionError,
161+ compose_udev_attr_equality, 'KEY', 'value')
162+
163+
164+class TestComposeUdevSetting(MAASTestCase):
165+
166+ def test__generates_assignment_with_single_equals_sign(self):
167+ self.assertEqual('KEY="value"', compose_udev_setting('KEY', 'value'))
168+
169+ def test__rejects_lower_case_letters_in_key(self):
170+ self.assertRaises(
171+ AssertionError,
172+ compose_udev_setting, 'key', 'value')
173+
174+
175+class TestComposeUdevRule(MAASTestCase):
176+
177+ def test__generates_rule(self):
178+ interface = factory.make_name('eth')
179+ mac = factory.make_mac_address()
180+ expected_rule = (
181+ 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
182+ 'ATTR{address}=="%(mac)s", NAME="%(interface)s"\n'
183+ ) % {'mac': mac, 'interface': interface}
184+ self.assertEqual(expected_rule, compose_udev_rule(interface, mac))
185+
186+
187+class TestComposeNetworkInterfacesUdevRules(MAASTestCase):
188+
189+ def test__generates_udev_rules(self):
190+ interfaces = [
191+ (factory.make_name('eth'), factory.make_mac_address())
192+ for _ in range(2)
193+ ]
194+
195+ self.assertThat(
196+ compose_network_interfaces_udev_rules(interfaces),
197+ ContainsAll([
198+ 'ATTR{address}=="%s", NAME="%s"\n' % (interface, mac)
199+ for mac, interface in interfaces
200+ ]))
201
202=== added file 'src/provisioningserver/udev.py'
203--- src/provisioningserver/udev.py 1970-01-01 00:00:00 +0000
204+++ src/provisioningserver/udev.py 2014-09-29 03:38:08 +0000
205@@ -0,0 +1,80 @@
206+# Copyright 2014 Canonical Ltd. This software is licensed under the
207+# GNU Affero General Public License version 3 (see the file LICENSE).
208+
209+"""Code to generate `udev` rules."""
210+
211+from __future__ import (
212+ absolute_import,
213+ print_function,
214+ unicode_literals,
215+ )
216+
217+str = None
218+
219+__metaclass__ = type
220+__all__ = [
221+ 'compose_network_interfaces_udev_rules',
222+ ]
223+
224+from textwrap import dedent
225+
226+
227+def compose_udev_equality(key, value):
228+ """Return a udev comparison clause, like `ACTION=="add"`."""
229+ assert key == key.upper()
230+ return '%s=="%s"' % (key, value)
231+
232+
233+def compose_udev_attr_equality(attribute, value):
234+ """Return a udev attribute comparison clause, like `ATTR{type}=="1"`."""
235+ assert attribute == attribute.lower()
236+ return 'ATTR{%s}=="%s"' % (attribute, value)
237+
238+
239+def compose_udev_setting(key, value):
240+ """Return a udev assignment clause, like `NAME="eth0"`."""
241+ assert key == key.upper()
242+ return '%s="%s"' % (key, value)
243+
244+
245+def compose_udev_rule(interface, mac):
246+ """Return a udev rule to set the name of network interface with `mac`.
247+
248+ The rule ends up as a single line looking something like:
249+
250+ SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*",
251+ ATTR{address}="ff:ee:dd:cc:bb:aa", NAME="eth0"
252+
253+ (Note the difference between `=` and `==`: they both occur.)
254+ """
255+ rule = ', '.join([
256+ compose_udev_equality('SUBSYSTEM', 'net'),
257+ compose_udev_equality('ACTION', 'add'),
258+ compose_udev_equality('DRIVERS', '?*'),
259+ compose_udev_attr_equality('address', mac),
260+ compose_udev_setting('NAME', interface),
261+ ])
262+ return '%s\n' % rule
263+
264+
265+def compose_network_interfaces_udev_rules(interfaces):
266+ """Return text for a udev persistent-net rules file.
267+
268+ These rules assign fixed names to network interfaces. They ensure that
269+ the same network interface cards come up with the same interface names on
270+ every boot. Otherwise, the kernel may assign interface names in different
271+ orders on every boot, and so network interfaces can "switch identities"
272+ every other time the machine reboots.
273+
274+ :param interfaces: List of tuples of interface name and MAC address.
275+ :return: Text to write into a udev rules file.
276+ """
277+ rules = [
278+ compose_udev_rule(interface, mac)
279+ for interface, mac in interfaces
280+ ]
281+ return dedent("""\
282+ # MAAS-assigned network interface names.
283+ %s
284+ # End of MAAS-assigned network interface names.
285+ """) % '\n\n'.join(rules)