Merge lp:~smoser/maas/kernel-cmdline-cleanup into lp:maas/trunk

Proposed by Scott Moser on 2012-09-13
Status: Merged
Approved by: Scott Moser on 2012-09-14
Approved revision: 980
Merged at revision: 1002
Proposed branch: lp:~smoser/maas/kernel-cmdline-cleanup
Merge into: lp:maas/trunk
Diff against target: 283 lines (+79/-60)
4 files modified
src/provisioningserver/kernel_opts.py (+28/-33)
src/provisioningserver/pxe/config.commissioning.template (+2/-0)
src/provisioningserver/pxe/config.template (+1/-0)
src/provisioningserver/tests/test_kernel_opts.py (+48/-27)
To merge this branch: bzr merge lp:~smoser/maas/kernel-cmdline-cleanup
Reviewer Review Type Date Requested Status
Raphaël Badin (community) 2012-09-13 Approve on 2012-09-14
Review via email: mp+124226@code.launchpad.net

Commit Message

cleanup of kernel command line arguments, add iscsi_inititator param

Move some options to purpose specific, drop some, change some.

ephemeral/commissioning:
 * add iscsi_initiator: this was always supposed to be required, but 12.04
   initramfs seem to work without it, so we didn't have it. 12.10 fail
   to configure the iscsi target in the initramfs without it. (LP: #1050523)
 * change url= to cloud-config-url=<preseed_url>
   This was really just re-named to cloud-config-url. cloud-init supports
   both 'url=' and 'cloud-config-url', but cloud-config-url is preferable
   as it is obviously more explicit in purpose.
 * change ip=dhcp to ip=::::<hostname>
   LP: #1046405 has more information, but this is how you would specify
   that the initramfs's dhcp client should specify hostname
 * add overlayroot=tmpfs
   cloud images for quantal now use the overlayroot package to accomplish
   read-only root. 12.04 images used a un-packaged earlier version that
   was hard-coded in the images to on. 12.10 and later need a kernel param
   to enable it.

move to install specific:
 netcfg/choose_interface, hostname, domain, text, priority, auto, locale

common:
 * add to intel: 'console=tty1 console=ttyS0'.
   This should log kernel messages to both serial console and graphical
   console if it is present. Note, the kernel assigns /dev/console to
       the last valid argument.
 * add 'nomodeset' to not switch video mode
 * remove 'suite'. Nothing that I am aware of reads this.

Other:
 * add 'SAY' of the kernel cmdline. At least in testing with kvm
   and -curses, this is very helpful as it goes to wherever the bios
   console is, and you can see it even if 'console=ttyS0' is given
   and the kernel's log of that information goes elsewhere.

Description of the Change

cleanup of kernel command line arguments, add iscsi_inititator param

This generally cleans up the parameters that we pass on the kernel command
line.

ephemeral/commissioning:
 * add iscsi_initiator: this was always supposed to be required, but 12.04
   initramfs seem to work without it, so we didn't have it. 12.10 fail
   to configure the iscsi target in the initramfs without it. (LP: #1050523)
 * change url= to cloud-config-url=<preseed_url>
   This was really just re-named to cloud-config-url. cloud-init supports
   both 'url=' and 'cloud-config-url', but cloud-config-url is preferable
   as it is obviously more explicit in purpose.
 * change ip=dhcp to ip=::::<hostname>
   LP: #1046405 has more information, but this is how you would specify
   that the initramfs's dhcp client should specify hostname
 * add overlayroot=tmpfs
   cloud images for quantal now use the overlayroot package to accomplish
   read-only root. 12.04 images used a un-packaged earlier version that
   was hard-coded in the images to on. 12.10 and later need a kernel param
   to enable it.

move to install specific:
 netcfg/choose_interface, hostname, domain, text, priority, auto, locale

common:
 * add to intel: 'console=tty1 console=ttyS0'.
   This should log kernel messages to both serial console and graphical
   console if it is present. Note, the kernel assigns /dev/console to
       the last valid argument.
 * add 'nomodeset' to not switch video mode
 * remove 'suite'. Nothing that I am aware of reads this.

Other:
 * add 'SAY' of the kernel cmdline. At least in testing with kvm
   and -curses, this is very helpful as it goes to wherever the bios
   console is, and you can see it even if 'console=ttyS0' is given
   and the kernel's log of that information goes elsewhere.

To post a comment you must log in.
Raphaël Badin (rvb) wrote :

Looks good, thanks for the comments in the code. I suppose you've field-tested this. A few remarks:

[0]

162 +def make_kernel_parameters(content=None):

Looks like 'content' could be renamed 'extra_parameters' or something…?

[1]

Some things are untested, I think it's worth adding a few tests or changing existing tests:

48 + "ip=::::%s" % params.hostname,

65 + "text priority=%s" % "critical",

79 + return ["console=tty1 console=ttyS0"]

97 + options = ["nomodeset"]

[2]

244 + self.assertIn("root=LABEL=cloudimg-rootfs", cmdline)
245 + self.assertIn("iscsi_initiator=", cmdline)
246 + self.assertIn("overlayroot=", cmdline)

If something goes wrong, this would be more easy to fix it it was written as a one line check:

Something along the lines of
        self.assertThat(
            cmdline,
            ContainsAll([
                "root=LABEL=cloudimg-rootfs",
                "iscsi_initiator=",
                "overlayroot=tmpfs",
                ]))

Even if I admit this is slightly less readable, the reasoning is that, if none of the strings that should be present in the cmdline are present, you'll have that information in one go as opposed to learning that "root=LABEL=cloudimg-rootfs" is not there, fix the problem, run the tests again, see that "iscsi_initiator=" is not there, fix the problem, etc.

[3]

88 - compose_initrd_opt(
89 - params.arch, params.subarch,
90 - params.release, params.purpose),

The method 'compose_initrd_opt' isn't called from anywhere now but is still in the code…?

fwiw, I spotted that in the coverage information:
$ ./bin/maas test --with-coverage --cover-package=provisioningserver.kernel_opts src/provisioningserver/tests/test_
kernel_opts.py -s
[...]
Name Stmts Miss Cover Missing
--------------------------------------------------------------
provisioningserver.kernel_opts 51 2 96% 51-52
----------------------------------------------------------------------
Ran 13 tests in 0.183s

[4]

$ make lint
src/provisioningserver/tests/test_kernel_opts.py:32: 'compose_image_path' imported but unused

review: Approve
980. By Scott Moser on 2012-09-14

remove unused compose_initrd_opt, improve test coverage

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/provisioningserver/kernel_opts.py'
2--- src/provisioningserver/kernel_opts.py 2012-09-03 11:31:04 +0000
3+++ src/provisioningserver/kernel_opts.py 2012-09-14 18:55:21 +0000
4@@ -47,11 +47,6 @@
5 __call__ = KernelParametersBase._replace
6
7
8-def compose_initrd_opt(arch, subarch, release, purpose):
9- path = "%s/initrd.gz" % compose_image_path(arch, subarch, release, purpose)
10- return "initrd=%s" % path
11-
12-
13 def compose_preseed_opt(preseed_url):
14 """Compose a kernel option for preseed URL.
15
16@@ -60,18 +55,6 @@
17 return "auto url=%s" % preseed_url
18
19
20-def compose_suite_opt(release):
21- return "suite=%s" % release
22-
23-
24-def compose_hostname_opt(hostname):
25- return "hostname=%s" % hostname
26-
27-
28-def compose_domain_opt(domain):
29- return "domain=%s" % domain
30-
31-
32 def compose_locale_opt():
33 locale = 'en_US'
34 return "locale=%s" % locale
35@@ -81,7 +64,6 @@
36 return [
37 'log_host=%s' % log_host,
38 'log_port=%d' % 514,
39- 'text priority=%s' % 'critical',
40 ]
41
42
43@@ -127,19 +109,37 @@
44 def compose_purpose_opts(params):
45 """Return the list of the purpose-specific kernel options."""
46 if params.purpose == "commissioning":
47+ # these are kernel parameters read by ephemeral
48+ # read by open-iscsi initramfs code
49 return [
50+ # read by open-iscsi initramfs code
51 "iscsi_target_name=%s:%s" % (
52 ISCSI_TARGET_NAME_PREFIX,
53 get_ephemeral_name(params.release, params.arch)),
54- "ip=dhcp",
55- "ro root=LABEL=cloudimg-rootfs",
56 "iscsi_target_ip=%s" % params.fs_host,
57 "iscsi_target_port=3260",
58+ "iscsi_initiator=%s" % params.hostname,
59+ # read by klibc 'ipconfig' in initramfs
60+ "ip=::::%s" % params.hostname,
61+ # cloud-images have this filesystem label
62+ "ro root=LABEL=cloudimg-rootfs",
63+ # read by overlayroot package
64+ "overlayroot=tmpfs",
65+ # read by cloud-init
66+ "cloud-config-url=%s" % params.preseed_url,
67 ]
68 else:
69+ # these are options used by the debian installer
70 return [
71- "netcfg/choose_interface=auto"
72- ]
73+ # read by debian installer
74+ "netcfg/choose_interface=auto",
75+ "hostname=%s" % params.hostname,
76+ "domain=%s" % params.domain,
77+ "text priority=%s" % "critical",
78+ "auto",
79+ "url=%s" % params.preseed_url,
80+ compose_locale_opt(),
81+ ]
82
83
84 def compose_arch_opts(params):
85@@ -147,7 +147,8 @@
86 if (params.arch, params.subarch) == ("armhf", "highbank"):
87 return ["console=ttyAMA0"]
88 else:
89- return []
90+ # on intel, send kernel output to both console and ttyS0
91+ return ["console=tty1 console=ttyS0"]
92
93
94 def compose_kernel_command_line_new(params):
95@@ -155,17 +156,11 @@
96
97 :type params: `KernelParameters`.
98 """
99- options = [
100- compose_initrd_opt(
101- params.arch, params.subarch,
102- params.release, params.purpose),
103- compose_preseed_opt(params.preseed_url),
104- compose_suite_opt(params.release),
105- compose_hostname_opt(params.hostname),
106- compose_domain_opt(params.domain),
107- compose_locale_opt(),
108- ]
109+ options = ["nomodeset"]
110 options += compose_purpose_opts(params)
111+ # Note: logging opts are not respected by ephemeral images, so
112+ # these are actually "purpose_opts" but were left generic
113+ # as it would be nice to have.
114 options += compose_logging_opts(params.log_host)
115 options += compose_arch_opts(params)
116 return ' '.join(options)
117
118=== modified file 'src/provisioningserver/pxe/config.commissioning.template'
119--- src/provisioningserver/pxe/config.commissioning.template 2012-08-30 10:42:56 +0000
120+++ src/provisioningserver/pxe/config.commissioning.template 2012-09-14 18:55:21 +0000
121@@ -6,6 +6,7 @@
122
123 LABEL amd64
124 SAY Booting (amd64) under MAAS direction...
125+ SAY {{kernel_params(arch="amd64") | kernel_command}}
126 KERNEL {{kernel_params(arch="amd64") | kernel_path }}
127 INITRD {{kernel_params(arch="amd64") | initrd_path }}
128 APPEND {{kernel_params(arch="amd64") | kernel_command}}
129@@ -13,6 +14,7 @@
130
131 LABEL i386
132 SAY Booting (i386) under MAAS direction...
133+ SAY {{kernel_params(arch="i386") | kernel_command}}
134 KERNEL {{kernel_params(arch="i386") | kernel_path }}
135 INITRD {{kernel_params(arch="i386") | initrd_path }}
136 APPEND {{kernel_params(arch="i386") | kernel_command}}
137
138=== modified file 'src/provisioningserver/pxe/config.template'
139--- src/provisioningserver/pxe/config.template 2012-08-30 10:42:56 +0000
140+++ src/provisioningserver/pxe/config.template 2012-09-14 18:55:21 +0000
141@@ -2,6 +2,7 @@
142
143 LABEL execute
144 SAY Booting under MAAS direction...
145+ SAY {{kernel_params | kernel_command}}
146 KERNEL {{kernel_params | kernel_path }}
147 INITRD {{kernel_params | initrd_path }}
148 APPEND {{kernel_params | kernel_command}}
149
150=== modified file 'src/provisioningserver/tests/test_kernel_opts.py'
151--- src/provisioningserver/tests/test_kernel_opts.py 2012-08-31 11:04:29 +0000
152+++ src/provisioningserver/tests/test_kernel_opts.py 2012-09-14 18:55:21 +0000
153@@ -19,6 +19,7 @@
154 from maastesting.factory import factory
155 from maastesting.matchers import ContainsAll
156 from maastesting.testcase import TestCase
157+from provisioningserver import kernel_opts
158 from provisioningserver.kernel_opts import (
159 compose_kernel_command_line_new,
160 compose_preseed_opt,
161@@ -27,6 +28,7 @@
162 ISCSI_TARGET_NAME_PREFIX,
163 KernelParameters,
164 )
165+
166 from provisioningserver.pxe.tftppath import compose_image_path
167 from provisioningserver.testing.config import ConfigFixture
168 from testtools.matchers import (
169@@ -35,12 +37,15 @@
170 )
171
172
173-def make_kernel_parameters():
174+def make_kernel_parameters(extra_parameters=None):
175 """Make a randomly populated `KernelParameters` instance."""
176- return KernelParameters(**{
177+ parms = {
178 field: factory.make_name(field)
179 for field in KernelParameters._fields
180- })
181+ }
182+ if extra_parameters is not None:
183+ parms.update(extra_parameters)
184+ return KernelParameters(**parms)
185
186
187 class TestUtilitiesKernelOpts(TestCase):
188@@ -70,24 +75,8 @@
189 "auto url=%s" % params.preseed_url,
190 compose_kernel_command_line_new(params))
191
192- def test_compose_kernel_command_line_includes_initrd(self):
193- params = make_kernel_parameters()
194- initrd_path = compose_image_path(
195- params.arch, params.subarch, params.release,
196- purpose=params.purpose)
197- self.assertIn(
198- "initrd=%s" % initrd_path,
199- compose_kernel_command_line_new(params))
200-
201- def test_compose_kernel_command_line_includes_suite(self):
202- # At the moment, the OS release we use is hard-coded to "precise."
203- params = make_kernel_parameters()
204- self.assertIn(
205- "suite=%s" % params.release,
206- compose_kernel_command_line_new(params))
207-
208- def test_compose_kernel_command_line_includes_hostname_and_domain(self):
209- params = make_kernel_parameters()
210+ def test_install_compose_kernel_command_line_includes_name_domain(self):
211+ params = make_kernel_parameters({"purpose": "install"})
212 self.assertThat(
213 compose_kernel_command_line_new(params),
214 ContainsAll([
215@@ -95,15 +84,15 @@
216 "domain=%s" % params.domain,
217 ]))
218
219- def test_compose_kernel_command_line_includes_locale(self):
220- params = make_kernel_parameters()
221+ def test_install_compose_kernel_command_line_includes_locale(self):
222+ params = make_kernel_parameters({"purpose": "install"})
223 locale = "en_US"
224 self.assertIn(
225 "locale=%s" % locale,
226 compose_kernel_command_line_new(params))
227
228- def test_compose_kernel_command_line_includes_log_settings(self):
229- params = make_kernel_parameters()
230+ def test_install_compose_kernel_command_line_includes_log_settings(self):
231+ params = make_kernel_parameters({"purpose": "install"})
232 # Port 514 (UDP) is syslog.
233 log_port = "514"
234 text_priority = "critical"
235@@ -115,14 +104,46 @@
236 "text priority=%s" % text_priority,
237 ]))
238
239- def test_compose_kernel_command_line_inc_purpose_opts(self):
240+ def test_install_compose_kernel_command_line_inc_purpose_opts(self):
241 # The result of compose_kernel_command_line includes the purpose
242 # options for a non "commissioning" node.
243- params = make_kernel_parameters()
244+ params = make_kernel_parameters({"purpose": "install"})
245 self.assertIn(
246 "netcfg/choose_interface=auto",
247 compose_kernel_command_line_new(params))
248
249+ def test_commissioning_compose_kernel_command_line_inc_purpose_opts(self):
250+ # The result of compose_kernel_command_line includes the purpose
251+ # options for a non "commissioning" node.
252+ self.patch(kernel_opts,
253+ "get_ephemeral_name").return_value = "RELEASE-ARCH"
254+ params = make_kernel_parameters({"purpose": "commissioning"})
255+ cmdline = compose_kernel_command_line_new(params)
256+ self.assertThat(
257+ cmdline,
258+ ContainsAll([
259+ "root=LABEL=cloudimg-rootfs",
260+ "iscsi_initiator=",
261+ "overlayroot=tmpfs",
262+ "ip=::::%s" % params.hostname]))
263+
264+ def test_compose_kernel_command_line_inc_common_opts(self):
265+ # Test that some kernel arguments appear on both commissioning
266+ # and install command lines.
267+ self.patch(kernel_opts,
268+ "get_ephemeral_name").return_value = "RELEASE-ARCH"
269+ expected = ["console=tty1", "console=ttyS0", "nomodeset"]
270+
271+ params = make_kernel_parameters({
272+ "purpose": "commissioning", "arch": "i386" })
273+ cmdline = compose_kernel_command_line_new(params)
274+ self.assertThat(cmdline, ContainsAll(expected))
275+
276+ params = make_kernel_parameters({
277+ "purpose": "install", "arch": "i386" })
278+ cmdline = compose_kernel_command_line_new(params)
279+ self.assertThat(cmdline, ContainsAll(expected))
280+
281 def create_ephemeral_info(self, name, arch, release):
282 """Create a pseudo-real ephemeral info file."""
283 ephemeral_info = """