Merge lp:~ltrager/maas/rootfs_over_http into lp:~maas-committers/maas/trunk

Proposed by Lee Trager
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 6106
Proposed branch: lp:~ltrager/maas/rootfs_over_http
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 479 lines (+98/-180)
6 files modified
src/maasserver/forms/settings.py (+9/-1)
src/maasserver/models/config.py (+3/-2)
src/maasserver/rpc/boot.py (+1/-0)
src/provisioningserver/kernel_opts.py (+44/-67)
src/provisioningserver/rpc/region.py (+1/-0)
src/provisioningserver/tests/test_kernel_opts.py (+40/-110)
To merge this branch: bzr merge lp:~ltrager/maas/rootfs_over_http
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Andres Rodriguez (community) Needs Fixing
Mike Pontillo (community) Approve
Review via email: mp+325655@code.launchpad.net

Commit message

When booting the ephemeral environment, fetch the rootfs over HTTP instead of using iSCSI.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

This looks like a nice improvement! A few questions below (mainly around IPv6 support; please add specific tests for that, too).

Somewhat related question: do we ever present a hostname in the fs_host, or is it always an IP address? (I assume it's just an IP address, and I think that's okay.)

We should also make sure we don't present an IPv6 link-local address as the fs_host (if we aren't doing that already).

review: Needs Information
Revision history for this message
Lee Trager (ltrager) wrote :

Thanks for the review. fs_host was being used by the ISCSI code so I assume it properly works with IPv6. I agree we need to do some IPv6 testing with this but I don't have access to an IPv6 enabled environment right now.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Thanks for the replies. In my view, only one thing needs to be done for this to land: add a separate IPv6 test case and ensure that IPv6 addresses have brackets surrounding them, while IPv4 addresses do not.

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

Thanks for the review. I've updated the branch to ensure that [] are wrapped around IPv6 addresses.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Thanks for the fixes. Ship it!

review: Approve
Revision history for this message
Andres Rodriguez (andreserl) wrote :

To follow on the discussion:

This needs to have a feature flag.

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

Booting over iSCSI is now the default. To enable booting over HTTP use the MAAS configuration option http_boot.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

This is okay as a global setting allow it to be changed over the API, which is nice. But this needs to be undocumented and removed before release. So this option should really be short lived.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/forms/settings.py'
--- src/maasserver/forms/settings.py 2017-06-15 14:27:01 +0000
+++ src/maasserver/forms/settings.py 2017-06-21 22:04:06 +0000
@@ -1,4 +1,4 @@
1# Copyright 2013-2016 Canonical Ltd. This software is licensed under the1# Copyright 2013-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Configuration items definition and utilities."""4"""Configuration items definition and utilities."""
@@ -538,6 +538,14 @@
538 'min_value': 1,538 'min_value': 1,
539 },539 },
540 },540 },
541 'http_boot': {
542 'default': False,
543 'form': forms.BooleanField,
544 'form_kwargs': {
545 'label': "When true all ephemeral environments boot over HTTP.",
546 'required': False
547 }
548 },
541}549}
542550
543551
544552
=== modified file 'src/maasserver/models/config.py'
--- src/maasserver/models/config.py 2017-05-29 13:44:04 +0000
+++ src/maasserver/models/config.py 2017-06-21 22:04:06 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012-2016 Canonical Ltd. This software is licensed under the1# Copyright 2012-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Configuration items."""4"""Configuration items."""
@@ -101,7 +101,8 @@
101 'max_node_testing_results': 10,101 'max_node_testing_results': 10,
102 'max_node_installation_results': 1,102 'max_node_installation_results': 1,
103 # Notifications.103 # Notifications.
104 'subnet_ip_exhaustion_threshold_count': 16104 'subnet_ip_exhaustion_threshold_count': 16,
105 'http_boot': False,
105 }106 }
106107
107108
108109
=== modified file 'src/maasserver/rpc/boot.py'
--- src/maasserver/rpc/boot.py 2017-06-01 06:16:00 +0000
+++ src/maasserver/rpc/boot.py 2017-06-21 22:04:06 +0000
@@ -326,6 +326,7 @@
326 "fs_host": local_ip,326 "fs_host": local_ip,
327 "log_host": server_host,327 "log_host": server_host,
328 "extra_opts": '' if extra_kernel_opts is None else extra_kernel_opts,328 "extra_opts": '' if extra_kernel_opts is None else extra_kernel_opts,
329 "http_boot": Config.objects.get_config('http_boot'),
329 }330 }
330 if machine is not None:331 if machine is not None:
331 params["system_id"] = machine.system_id332 params["system_id"] = machine.system_id
332333
=== modified file 'src/provisioningserver/kernel_opts.py'
--- src/provisioningserver/kernel_opts.py 2017-04-12 22:00:36 +0000
+++ src/provisioningserver/kernel_opts.py 2017-06-21 22:04:06 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012-2015 Canonical Ltd. This software is licensed under the1# Copyright 2012-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Generate kernel command-line options for inclusion in PXE configs."""4"""Generate kernel command-line options for inclusion in PXE configs."""
@@ -6,7 +6,6 @@
6__all__ = [6__all__ = [
7 'compose_kernel_command_line',7 'compose_kernel_command_line',
8 'KernelParameters',8 'KernelParameters',
9 'prefix_target_name',
10 ]9 ]
1110
12from collections import namedtuple11from collections import namedtuple
@@ -43,6 +42,7 @@
43 "fs_host", # Host/IP on which ephemeral filesystems are hosted.42 "fs_host", # Host/IP on which ephemeral filesystems are hosted.
44 "extra_opts", # String of extra options to supply, will be appended43 "extra_opts", # String of extra options to supply, will be appended
45 # verbatim to the kernel command line44 # verbatim to the kernel command line
45 "http_boot", # Whether or not to boot over HTTP.
46 ))46 ))
4747
4848
@@ -52,21 +52,6 @@
52 __call__ = KernelParametersBase._replace52 __call__ = KernelParametersBase._replace
5353
5454
55def compose_preseed_opt(preseed_url):
56 """Compose a kernel option for preseed URL.
57
58 :param preseed_url: The URL from which a preseed can be fetched.
59 """
60 # See https://help.ubuntu.com/12.04/installation-guide
61 # /i386/preseed-using.html#preseed-auto
62 return "auto url=%s" % preseed_url
63
64
65def compose_locale_opt():
66 locale = 'en_US'
67 return "locale=%s" % locale
68
69
70def compose_logging_opts(log_host):55def compose_logging_opts(log_host):
71 return [56 return [
72 'log_host=%s' % log_host,57 'log_host=%s' % log_host,
@@ -101,19 +86,6 @@
101 )86 )
10287
10388
104def compose_hostname_opts(params):
105 """Return list of hostname/domain options based on `params`.
106
107 The domain is omitted if `params` does not include it.
108 """
109 options = [
110 'hostname=%s' % params.hostname,
111 ]
112 if params.domain is not None:
113 options.append('domain=%s' % params.domain)
114 return options
115
116
117def prefix_target_name(name):89def prefix_target_name(name):
118 """Prefix an ISCSI target name with the standard target-name prefix."""90 """Prefix an ISCSI target name with the standard target-name prefix."""
119 return "%s:%s" % (ISCSI_TARGET_NAME_PREFIX, name)91 return "%s:%s" % (ISCSI_TARGET_NAME_PREFIX, name)
@@ -121,8 +93,18 @@
12193
122def compose_purpose_opts(params):94def compose_purpose_opts(params):
123 """Return the list of the purpose-specific kernel options."""95 """Return the list of the purpose-specific kernel options."""
124 if params.purpose in ["commissioning", "xinstall", "enlist"]:96 if params.http_boot:
125 # These are kernel parameters read by the ephemeral environment.97 kernel_params = [
98 "root=squash:http://%s:5248/images/%s/%s/%s/%s/%s/squashfs" % (
99 (
100 '[%s]' % params.fs_host
101 if IPAddress(params.fs_host).version == 6
102 else params.fs_host
103 ),
104 params.osystem, params.arch, params.subarch, params.release,
105 params.label),
106 ]
107 else:
126 tname = prefix_target_name(108 tname = prefix_target_name(
127 get_ephemeral_name(109 get_ephemeral_name(
128 params.osystem, params.arch, params.subarch,110 params.osystem, params.arch, params.subarch,
@@ -133,43 +115,38 @@
133 "iscsi_target_ip=%s" % params.fs_host,115 "iscsi_target_ip=%s" % params.fs_host,
134 "iscsi_target_port=3260",116 "iscsi_target_port=3260",
135 "iscsi_initiator=%s" % params.hostname,117 "iscsi_initiator=%s" % params.hostname,
136 # Read by cloud-initramfs-dyn-netconf initramfs-tools networking
137 # configuration in the initramfs. Choose IPv4 or IPv6 based on the
138 # family of fs_host. If BOOTIF is set, IPv6 config uses that
139 # exclusively.
140 (
141 "ip=::::%s:BOOTIF" % params.hostname
142 if IPAddress(params.fs_host).version == 4 else "ip=off"
143 ),
144 (
145 "ip6=dhcp"
146 if IPAddress(params.fs_host).version == 6 else "ip6=off"
147 ),
148 # kernel / udev name iscsi devices with this path118 # kernel / udev name iscsi devices with this path
149 "ro root=/dev/disk/by-path/ip-%s:%s-iscsi-%s-lun-1" % (119 "root=/dev/disk/by-path/ip-%s:%s-iscsi-%s-lun-1" % (
150 params.fs_host, "3260", tname),120 params.fs_host, "3260", tname),
151 # Read by overlayroot package.121 ]
152 "overlayroot=tmpfs",122
153 # LP:1533822 - Disable reading overlay data from disk.123 kernel_params += [
154 "overlayroot_cfgdisk=disabled",124 "ro",
155 # Select the MAAS datasource by default.125 # Read by cloud-initramfs-dyn-netconf initramfs-tools networking
156 "cc:{'datasource_list': ['MAAS']}end_cc",126 # configuration in the initramfs. Choose IPv4 or IPv6 based on the
157 # Read by cloud-init.127 # family of fs_host. If BOOTIF is set, IPv6 config uses that
158 "cloud-config-url=%s" % params.preseed_url,128 # exclusively.
159 # Disable apparmor in the ephemeral environment. This addresses129 (
160 # MAAS bug LP: #1677336 due to LP: #1408106130 "ip=::::%s:BOOTIF" % params.hostname
161 "apparmor=0",131 if IPAddress(params.fs_host).version == 4 else "ip=off"
162 ]132 ),
163 return kernel_params133 (
164 else:134 "ip6=dhcp"
165 # These are options used by the Debian Installer.135 if IPAddress(params.fs_host).version == 6 else "ip6=off"
166 return [136 ),
167 "netcfg/choose_interface=auto",137 # Read by overlayroot package.
168 # Use the text installer, display only critical messages.138 "overlayroot=tmpfs",
169 "text priority=critical",139 # LP:1533822 - Disable reading overlay data from disk.
170 compose_preseed_opt(params.preseed_url),140 "overlayroot_cfgdisk=disabled",
171 compose_locale_opt(),141 # Select the MAAS datasource by default.
172 ] + compose_hostname_opts(params)142 "cc:{'datasource_list': ['MAAS']}end_cc",
143 # Read by cloud-init.
144 "cloud-config-url=%s" % params.preseed_url,
145 # Disable apparmor in the ephemeral environment. This addresses
146 # MAAS bug LP: #1677336 due to LP: #1408106
147 "apparmor=0",
148 ]
149 return kernel_params
173150
174151
175def compose_arch_opts(params):152def compose_arch_opts(params):
176153
=== modified file 'src/provisioningserver/rpc/region.py'
--- src/provisioningserver/rpc/region.py 2017-01-28 00:51:47 +0000
+++ src/provisioningserver/rpc/region.py 2017-06-21 22:04:06 +0000
@@ -138,6 +138,7 @@
138 (b"log_host", amp.Unicode()),138 (b"log_host", amp.Unicode()),
139 (b"extra_opts", amp.Unicode()),139 (b"extra_opts", amp.Unicode()),
140 (b"system_id", amp.Unicode(optional=True)),140 (b"system_id", amp.Unicode(optional=True)),
141 (b"http_boot", amp.Boolean(optional=True)),
141 ]142 ]
142 errors = {143 errors = {
143 BootConfigNoResponse: b"BootConfigNoResponse",144 BootConfigNoResponse: b"BootConfigNoResponse",
144145
=== modified file 'src/provisioningserver/tests/test_kernel_opts.py'
--- src/provisioningserver/tests/test_kernel_opts.py 2017-04-12 22:00:36 +0000
+++ src/provisioningserver/tests/test_kernel_opts.py 2017-06-21 22:04:06 +0000
@@ -21,14 +21,10 @@
21from provisioningserver.kernel_opts import (21from provisioningserver.kernel_opts import (
22 compose_arch_opts,22 compose_arch_opts,
23 compose_kernel_command_line,23 compose_kernel_command_line,
24 compose_preseed_opt,
25 CURTIN_KERNEL_CMDLINE_NAME,24 CURTIN_KERNEL_CMDLINE_NAME,
26 get_curtin_kernel_cmdline_sep,25 get_curtin_kernel_cmdline_sep,
27 get_ephemeral_name,
28 get_last_directory,26 get_last_directory,
29 ISCSI_TARGET_NAME_PREFIX,
30 KernelParameters,27 KernelParameters,
31 prefix_target_name,
32)28)
33from testtools.matchers import (29from testtools.matchers import (
34 Contains,30 Contains,
@@ -51,6 +47,8 @@
51 {field: factory.make_name(field)47 {field: factory.make_name(field)
52 for field in KernelParameters._fields48 for field in KernelParameters._fields
53 if field not in parms})49 if field not in parms})
50 if not isinstance(parms['http_boot'], bool):
51 parms['http_boot'] = True
54 params = KernelParameters(**parms)52 params = KernelParameters(**parms)
5553
56 if testcase is not None:54 if testcase is not None:
@@ -86,23 +84,6 @@
86 self.assertTrue(callable(params))84 self.assertTrue(callable(params))
87 self.assertIs(params._replace.__func__, params.__call__.__func__)85 self.assertIs(params._replace.__func__, params.__call__.__func__)
8886
89 def test_prefix_target_name_adds_prefix(self):
90 prefix = factory.make_name('prefix')
91 target = factory.make_name('tgt')
92 self.patch(kernel_opts, 'ISCSI_TARGET_NAME_PREFIX', prefix)
93
94 self.assertEqual(
95 '%s:%s' % (prefix, target),
96 prefix_target_name(target))
97
98 def test_prefix_target_name_produces_exactly_one_separating_colon(self):
99 target = factory.make_name('tgt')
100
101 full_name = prefix_target_name(target)
102
103 self.assertIn(':' + target, full_name)
104 self.assertNotIn('::' + target, full_name)
105
10687
107class TestGetCurtinKernelCmdlineSepTest(MAASTestCase):88class TestGetCurtinKernelCmdlineSepTest(MAASTestCase):
10889
@@ -138,59 +119,6 @@
138 cmdline = compose_kernel_command_line(params)119 cmdline = compose_kernel_command_line(params)
139 self.assertThat(cmdline, Contains("overlayroot_cfgdisk=disabled"))120 self.assertThat(cmdline, Contains("overlayroot_cfgdisk=disabled"))
140121
141 def test_compose_kernel_command_line_includes_preseed_url(self):
142 params = self.make_kernel_parameters()
143 self.assertIn(
144 "auto url=%s" % params.preseed_url,
145 compose_kernel_command_line(params))
146
147 def test_install_compose_kernel_command_line_includes_name_domain(self):
148 params = self.make_kernel_parameters(purpose="install")
149 self.assertThat(
150 compose_kernel_command_line(params),
151 ContainsAll([
152 "hostname=%s" % params.hostname,
153 "domain=%s" % params.domain,
154 ]))
155
156 def test_install_compose_kernel_command_line_omits_domain_if_omitted(self):
157 params = self.make_kernel_parameters(purpose="install", domain=None)
158 kernel_command_line = compose_kernel_command_line(params)
159 self.assertIn("hostname=%s" % params.hostname, kernel_command_line)
160 self.assertNotIn('domain=', kernel_command_line)
161
162 def test_install_compose_kernel_command_line_includes_locale(self):
163 params = self.make_kernel_parameters(purpose="install")
164 locale = "en_US"
165 self.assertIn(
166 "locale=%s" % locale,
167 compose_kernel_command_line(params))
168
169 def test_install_compose_kernel_command_line_includes_log_settings(self):
170 params = self.make_kernel_parameters(purpose="install")
171 # Port 514 (UDP) is syslog.
172 log_port = "514"
173 self.assertThat(
174 compose_kernel_command_line(params),
175 ContainsAll([
176 "log_host=%s" % params.log_host,
177 "log_port=%s" % log_port,
178 ]))
179
180 def test_install_compose_kernel_command_line_includes_di_settings(self):
181 params = self.make_kernel_parameters(purpose="install")
182 self.assertThat(
183 compose_kernel_command_line(params),
184 Contains("text priority=critical"))
185
186 def test_install_compose_kernel_command_line_inc_purpose_opts(self):
187 # The result of compose_kernel_command_line includes the purpose
188 # options for a non "commissioning" node.
189 params = self.make_kernel_parameters(purpose="install")
190 self.assertIn(
191 "netcfg/choose_interface=auto",
192 compose_kernel_command_line(params))
193
194 def test_xinstall_compose_kernel_command_line_inc_purpose_opts4(self):122 def test_xinstall_compose_kernel_command_line_inc_purpose_opts4(self):
195 # The result of compose_kernel_command_line includes the purpose123 # The result of compose_kernel_command_line includes the purpose
196 # options for a non "xinstall" node.124 # options for a non "xinstall" node.
@@ -200,8 +128,7 @@
200 self.assertThat(128 self.assertThat(
201 cmdline,129 cmdline,
202 ContainsAll([130 ContainsAll([
203 "root=/dev/disk/by-path/ip-",131 "root=squash:http://",
204 "iscsi_initiator=",
205 "overlayroot=tmpfs",132 "overlayroot=tmpfs",
206 "ip6=off",133 "ip6=off",
207 "ip=::::%s:BOOTIF" % params.hostname]))134 "ip=::::%s:BOOTIF" % params.hostname]))
@@ -215,8 +142,7 @@
215 self.assertThat(142 self.assertThat(
216 cmdline,143 cmdline,
217 ContainsAll([144 ContainsAll([
218 "root=/dev/disk/by-path/ip-",145 "root=squash:http://",
219 "iscsi_initiator=",
220 "overlayroot=tmpfs",146 "overlayroot=tmpfs",
221 "ip=off",147 "ip=off",
222 "ip6=dhcp"]))148 "ip6=dhcp"]))
@@ -242,8 +168,7 @@
242 self.assertThat(168 self.assertThat(
243 cmdline,169 cmdline,
244 ContainsAll([170 ContainsAll([
245 "root=/dev/disk/by-path/ip-",171 "root=squash:http://",
246 "iscsi_initiator=",
247 "overlayroot=tmpfs",172 "overlayroot=tmpfs",
248 "ip6=off",173 "ip6=off",
249 "ip=::::%s:BOOTIF" % params.hostname]))174 "ip=::::%s:BOOTIF" % params.hostname]))
@@ -257,8 +182,7 @@
257 self.assertThat(182 self.assertThat(
258 cmdline,183 cmdline,
259 ContainsAll([184 ContainsAll([
260 "root=/dev/disk/by-path/ip-",185 "root=squash:http://",
261 "iscsi_initiator=",
262 "overlayroot=tmpfs",186 "overlayroot=tmpfs",
263 "ip=off",187 "ip=off",
264 "ip6=dhcp"]))188 "ip6=dhcp"]))
@@ -284,8 +208,7 @@
284 self.assertThat(208 self.assertThat(
285 cmdline,209 cmdline,
286 ContainsAll([210 ContainsAll([
287 "root=/dev/disk/by-path/ip-",211 "root=squash:http://",
288 "iscsi_initiator=",
289 "overlayroot=tmpfs",212 "overlayroot=tmpfs",
290 "ip6=off",213 "ip6=off",
291 "ip=::::%s:BOOTIF" % params.hostname]))214 "ip=::::%s:BOOTIF" % params.hostname]))
@@ -299,8 +222,7 @@
299 self.assertThat(222 self.assertThat(
300 cmdline,223 cmdline,
301 ContainsAll([224 ContainsAll([
302 "root=/dev/disk/by-path/ip-",225 "root=squash:http://",
303 "iscsi_initiator=",
304 "overlayroot=tmpfs",226 "overlayroot=tmpfs",
305 "ip=off",227 "ip=off",
306 "ip6=dhcp"]))228 "ip6=dhcp"]))
@@ -378,39 +300,17 @@
378 # The result of compose_kernel_command_line includes the purpose300 # The result of compose_kernel_command_line includes the purpose
379 # options for a "xinstall" node.301 # options for a "xinstall" node.
380 params = self.make_kernel_parameters(purpose="xinstall")302 params = self.make_kernel_parameters(purpose="xinstall")
381 ephemeral_name = get_ephemeral_name(
382 params.osystem, params.arch, params.subarch,
383 params.release, params.label)
384 self.assertThat(303 self.assertThat(
385 compose_kernel_command_line(params),304 compose_kernel_command_line(params),
386 ContainsAll([305 ContainsAll(["root=squash:http://"]))
387 "iscsi_target_name=%s:%s" % (
388 ISCSI_TARGET_NAME_PREFIX, ephemeral_name),
389 "iscsi_target_port=3260",
390 "iscsi_target_ip=%s" % params.fs_host,
391 ]))
392306
393 def test_compose_kernel_command_line_inc_purpose_opts_comm_node(self):307 def test_compose_kernel_command_line_inc_purpose_opts_comm_node(self):
394 # The result of compose_kernel_command_line includes the purpose308 # The result of compose_kernel_command_line includes the purpose
395 # options for a "commissioning" node.309 # options for a "commissioning" node.
396 params = self.make_kernel_parameters(purpose="commissioning")310 params = self.make_kernel_parameters(purpose="commissioning")
397 ephemeral_name = get_ephemeral_name(
398 params.osystem, params.arch, params.subarch,
399 params.release, params.label)
400 self.assertThat(311 self.assertThat(
401 compose_kernel_command_line(params),312 compose_kernel_command_line(params),
402 ContainsAll([313 ContainsAll(["root=squash:http://"]))
403 "iscsi_target_name=%s:%s" % (
404 ISCSI_TARGET_NAME_PREFIX, ephemeral_name),
405 "iscsi_target_port=3260",
406 "iscsi_target_ip=%s" % params.fs_host,
407 ]))
408
409 def test_compose_preseed_kernel_opt_returns_kernel_option(self):
410 dummy_preseed_url = factory.make_name("url")
411 self.assertEqual(
412 "auto url=%s" % dummy_preseed_url,
413 compose_preseed_opt(dummy_preseed_url))
414314
415 def test_compose_kernel_command_line_inc_arm_specific_option(self):315 def test_compose_kernel_command_line_inc_arm_specific_option(self):
416 params = self.make_kernel_parameters(arch="armhf", subarch="highbank")316 params = self.make_kernel_parameters(arch="armhf", subarch="highbank")
@@ -432,3 +332,33 @@
432 arch=factory.make_name("arch"),332 arch=factory.make_name("arch"),
433 subarch=factory.make_name("subarch"))333 subarch=factory.make_name("subarch"))
434 self.assertEqual([], compose_arch_opts(params))334 self.assertEqual([], compose_arch_opts(params))
335
336 def test_compose_rootfs_over_http_ipv4(self):
337 params = make_kernel_parameters(fs_host=factory.make_ipv4_address())
338 self.assertThat(
339 compose_kernel_command_line(params),
340 ContainsAll([
341 "ro",
342 "root=squash:http://%s:5248/images/%s/%s/%s/%s/%s/squashfs" % (
343 params.fs_host, params.osystem, params.arch,
344 params.subarch, params.release, params.label)]))
345
346 def test_compose_rootfs_over_http_ipv6(self):
347 params = make_kernel_parameters(fs_host=factory.make_ipv6_address())
348 self.assertThat(
349 compose_kernel_command_line(params),
350 ContainsAll([
351 "ro",
352 "root=squash:http://[%s]:5248/images/%s/%s/%s/%s/%s/squashfs" %
353 (
354 params.fs_host, params.osystem, params.arch,
355 params.subarch, params.release, params.label)]))
356
357 def test_compose_rootfs_over_iscsi(self):
358 params = make_kernel_parameters(
359 fs_host=factory.make_ipv6_address(), http_boot=False)
360 self.assertThat(
361 compose_kernel_command_line(params),
362 ContainsAll([
363 "root=/dev/disk/by-path/ip-",
364 "iscsi_initiator="]))