Merge lp:~julian-edwards/maas/utopic-syslinux-location into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 2441
Proposed branch: lp:~julian-edwards/maas/utopic-syslinux-location
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 137 lines (+78/-4)
2 files modified
src/provisioningserver/boot/pxe.py (+20/-3)
src/provisioningserver/boot/tests/test_pxe.py (+58/-1)
To merge this branch: bzr merge lp:~julian-edwards/maas/utopic-syslinux-location
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+223851@code.launchpad.net

Commit message

Make the boot images importer look through different system directories when copying the pxe files to the TFTP root. This is to enable the same code to work for different Ubuntu releases which may store the files in different places.

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Nice! Thanks for fixing this bug!

review: Approve
Revision history for this message
Raphaël Badin (rvb) wrote :

I'm landing this because I'd like very much to make sure the Utopic package passes the CI tests today.

Revision history for this message
Raphaël Badin (rvb) wrote :

41 + # locate_bootloader might return None but happy to let that
42 + # traceback here is it should never happen unless there's a
43 + # serious problem with packaging.

Now that I've seen the traceback IRL (packaging problem, unrelated to your change), I can't say it was very helpful: http://paste.ubuntu.com/7674410/

Revision history for this message
Julian Edwards (julian-edwards) wrote :

How the hell?

Revision history for this message
Julian Edwards (julian-edwards) wrote :

On 23/06/14 09:36, Julian Edwards wrote:
> How the hell?

Aff, saw your other MP. I was about to show you the canonistack
instance "ls" output I had from Friday, but my instances have done yet
another disappearing act.

Revision history for this message
Raphaël Badin (rvb) wrote :

On 06/23/2014 03:24 AM, Julian Edwards wrote:
> On 23/06/14 09:36, Julian Edwards wrote:
>> How the hell?
>
> Aff, saw your other MP. I was about to show you the canonistack
> instance "ls" output I had from Friday, but my instances have done yet
> another disappearing act.

Yeah, this is fixed now. The Utopic CI job is still failing… but the
image import step works (it's failing to enlist nodes now which happens
*after* the images have been imported).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/provisioningserver/boot/pxe.py'
2--- src/provisioningserver/boot/pxe.py 2014-06-10 14:45:14 +0000
3+++ src/provisioningserver/boot/pxe.py 2014-06-20 04:51:25 +0000
4@@ -27,10 +27,13 @@
5 )
6 from provisioningserver.boot.install_bootloader import install_bootloader
7
8-
9+# Bootloader file names to install.
10 BOOTLOADERS = ['pxelinux.0', 'chain.c32', 'ifcpu64.c32']
11
12-BOOTLOADER_DIR = '/usr/lib/syslinux'
13+# Possible locations in which to find the files. Search these in this
14+# order for each file. (This exists because locations differ across
15+# Ubuntu releases)
16+BOOTLOADER_DIRS = ['/usr/lib/syslinux', '/usr/lib/syslinux/bios']
17
18
19 class ARP_HTYPE:
20@@ -107,11 +110,25 @@
21 namespace = self.compose_template_namespace(kernel_params)
22 return BytesReader(template.substitute(namespace).encode("utf-8"))
23
24+ def locate_bootloader(self, bootloader):
25+ """Search BOOTLOADER_DIRS for bootloader.
26+
27+ :return: The full file path where the bootloader was found, or None.
28+ """
29+ for dir in BOOTLOADER_DIRS:
30+ filename = os.path.join(dir, bootloader)
31+ if os.path.exists(filename):
32+ return filename
33+ return None
34+
35 def install_bootloader(self, destination):
36 """Installs the required files for PXE booting into the
37 tftproot.
38 """
39 for bootloader in BOOTLOADERS:
40- bootloader_src = os.path.join(BOOTLOADER_DIR, bootloader)
41+ # locate_bootloader might return None but happy to let that
42+ # traceback here is it should never happen unless there's a
43+ # serious problem with packaging.
44+ bootloader_src = self.locate_bootloader(bootloader)
45 bootloader_dst = os.path.join(destination, bootloader)
46 install_bootloader(bootloader_src, bootloader_dst)
47
48=== modified file 'src/provisioningserver/boot/tests/test_pxe.py'
49--- src/provisioningserver/boot/tests/test_pxe.py 2014-06-10 14:45:14 +0000
50+++ src/provisioningserver/boot/tests/test_pxe.py 2014-06-20 04:51:25 +0000
51@@ -15,14 +15,21 @@
52 __all__ = []
53
54 from collections import OrderedDict
55+import os
56 import re
57
58 from maastesting.factory import factory
59+from maastesting.matchers import MockCallsMatch
60 from maastesting.testcase import MAASTestCase
61+import mock
62 from provisioningserver import kernel_opts
63-from provisioningserver.boot import BytesReader
64+from provisioningserver.boot import (
65+ BytesReader,
66+ pxe as pxe_module,
67+ )
68 from provisioningserver.boot.pxe import (
69 ARP_HTYPE,
70+ BOOTLOADERS,
71 PXEBootMethod,
72 re_config_file,
73 )
74@@ -67,6 +74,18 @@
75 self.useFixture(set_tftp_root(tftproot))
76 return tftproot
77
78+ def make_dummy_bootloader_sources(self, destination, loader_names):
79+ """install_bootloader requires real files to exist, this method
80+ creates them in the requested location.
81+
82+ :return: list of created filenames
83+ """
84+ created = []
85+ for loader in loader_names:
86+ name = factory.make_file(destination, loader)
87+ created.append(name)
88+ return created
89+
90 def test_compose_config_path_follows_maas_pxe_directory_layout(self):
91 name = factory.make_name('config')
92 self.assertEqual(
93@@ -103,6 +122,44 @@
94 method = PXEBootMethod()
95 self.assertEqual('00:00', method.arch_octet)
96
97+ def test_locate_bootloader(self):
98+ # Put all the BOOTLOADERS except one in dir1, and the last in
99+ # dir2.
100+ dir1 = self.make_dir()
101+ dir2 = self.make_dir()
102+ dirs = [dir1, dir2]
103+ self.patch(pxe_module, "BOOTLOADER_DIRS", dirs)
104+ self.make_dummy_bootloader_sources(dir1, BOOTLOADERS[:-1])
105+ [displaced_loader] = self.make_dummy_bootloader_sources(
106+ dir2, BOOTLOADERS[-1:])
107+ method = PXEBootMethod()
108+ observed = method.locate_bootloader(BOOTLOADERS[-1])
109+
110+ self.assertEqual(displaced_loader, observed)
111+
112+ def test_locate_bootloader_returns_None_if_not_found(self):
113+ method = PXEBootMethod()
114+ self.assertIsNone(method.locate_bootloader("foo"))
115+
116+ def test_install_bootloader_installs_to_destination(self):
117+ tftproot = self.make_tftp_root()
118+ source_dir = self.make_dir()
119+ self.patch(pxe_module, "BOOTLOADER_DIRS", [source_dir])
120+ self.make_dummy_bootloader_sources(source_dir, BOOTLOADERS)
121+ install_bootloader_call = self.patch(pxe_module, "install_bootloader")
122+ method = PXEBootMethod()
123+ method.install_bootloader(tftproot)
124+
125+ expected = [
126+ mock.call(
127+ os.path.join(source_dir, bootloader),
128+ os.path.join(tftproot, bootloader)
129+ )
130+ for bootloader in BOOTLOADERS]
131+ self.assertThat(
132+ install_bootloader_call,
133+ MockCallsMatch(*expected))
134+
135
136 def parse_pxe_config(text):
137 """Parse a PXE config file.