Merge lp:~blake-rouse/maas/fix-bug-1300285 into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 2216
Proposed branch: lp:~blake-rouse/maas/fix-bug-1300285
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 60 lines (+26/-6)
2 files modified
src/provisioningserver/dhcp/config.py (+21/-4)
src/provisioningserver/dhcp/tests/test_config.py (+5/-2)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-bug-1300285
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+213499@code.launchpad.net

Commit message

Resolves issue where if a node does not provide an architecture type on dhcp request, or no other boot method is available for that architecture, the node still uses pxelinux.0 to boot.

Description of the change

[bug=1300285] Fixes generate-dhcp-config, making it output the PXEBootMethod bootloader in a else branch, instead of an if with an architecture type. This allows nodes that do not provide an architecture type on dhcp request, or emulate pxelinux.0 to boot correctly.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :
Download full text (3.3 KiB)

I think I understand what this does now, but the commit message is a bit cryptic. It summarises the code changes, but it doesn't give me much of a high-level idea of what the change means. It's that intention that's essential for review and maintenance. Could you perhaps say something like "make the PXE bootloader the default bootloader in the DHCP config, for when the node does not specify its architecture"? The extra sentence you provide in the description but not in the commit message is actually much more suitable for the commit message.

Also, there is no need to duplicate anything between the commit message and the description. The description is for the merge proposal only, and should help facilitate review. The commit message goes into revision history, with automatically added tags for author, reviewer, and bug number. Which also means: no need to prefix the bug yourself!

Typos in config.py: "sepcial" and, in that same comment, missing punctuation. Could you be a bit more descriptive? Someone reading this code and encountering "the special case PXEBootLoader" may still have trouble finding what that refers to. Are there several PXEBootLoaders, and is one of them a special case? Or do you mean that the PXEBootLoader is a special case?

In compose_conditional_bootloader, the loop structure and the code after the loop are now connected through the pxe_method variable, and through the "continue" statement in the loop. Those are often things worth avoiding, because it will take future maintainers time to figure out and preserve the intended behaviour. Not much, but it adds up — and what's more, they may not be aware of the need.

So might I suggest removing the "continue" and moving the variable closer to its point of use:

    for name, method in BootMethodRegistry:
        # PXEBootMethod is special. No matter what the arch_octet is,
        # it's always the last in the conditional, with an else. So
        # no matter the architecture it will always try to use pxelinux
        # to boot that node.
        if name != "pxe":
            output += CONDITIONAL_BOOTLOADER.format(
                behaviour=next(behaviour), arch_octet=method.arch_octet,
                bootloader=method.bootloader_path).strip() + ' '

    # Now put the PXE bootloader at the end.
    pxe_method = BootMethodRegistry.get_item('pxe')
    if pxe_method is not None:
        output += PXE_BOOTLOADER.format(
            bootloader=pxe_method.bootloader_path).strip()

(Also note that the "is its" in the comment could do with a comma.)

Alternatively, is it actually a problem to mention the PXE bootloader twice? If not, maybe repetition isn't so bad. That would give us something like:

    for name, method in BootMethodRegistry:
        output += CONDITIONAL_BOOTLOADER.format(
            behaviour=next(behaviour), arch_octet=method.arch_octet,
            bootloader=method.bootloader_path).strip() + ' '

    # Fall back on the PXE bootloader if architecture is unknown.
    pxe_method = BootMethodRegistry.get_item('pxe')
    if pxe_method is not None:
        output += PXE_BOOTLOADER.format(
            bootloader=pxe_method.bootloader_path).strip()

I...

Read more...

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

Fixed the commit message, removed the continue from the loop, cleaned up the code comment.

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (16.1 KiB)

The attempt to merge lp:~blake-rouse/maas/fix-bug-1300285 into lp:maas failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Hit http://security.ubuntu.com trusty-security Release.gpg
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Hit http://security.ubuntu.com trusty-security Release
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://nova.clouds.archive.ubuntu.com trusty-updates Release
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Ign http://security.ubuntu.com trusty-security/main Translation-en_US
Ign http://security.ubuntu.com trusty-security/universe Translation-en_US
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en_US
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 bind9 bind9utils build-essential bzr-builddeb curl daemontools debhelper dh-apport distro-info dnsutils firefox freeipmi-tools ipython isc-dhcp-common libjs-raphael libjs-yui3-full libjs-yui3-min libpq-dev make postgresql python-amqplib python-bzrlib python-celery python-convoy python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-formencode python-httplib2 python-jinja2 python-jsonschema python-lockfile python-lxml python-netaddr python-netifaces python-oauth python-oops python-oops-amqp python-oops-datedir-repo python-oops-twisted python-oops-wsgi ...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/provisioningserver/dhcp/config.py'
2--- src/provisioningserver/dhcp/config.py 2014-03-28 15:17:34 +0000
3+++ src/provisioningserver/dhcp/config.py 2014-04-01 13:29:08 +0000
4@@ -38,6 +38,13 @@
5 }}
6 """
7
8+# Used to generate the PXEBootLoader special case
9+PXE_BOOTLOADER = """
10+else {{
11+ filename \"{bootloader}\";
12+ }}
13+"""
14+
15
16 class DHCPConfigError(Exception):
17 """Exception raised for errors processing the DHCP config."""
18@@ -46,10 +53,20 @@
19 def compose_conditional_bootloader():
20 output = ""
21 behaviour = chain(["if"], repeat("elsif"))
22- for _, method in BootMethodRegistry:
23- output += CONDITIONAL_BOOTLOADER.format(
24- behaviour=next(behaviour), arch_octet=method.arch_octet,
25- bootloader=method.bootloader_path).strip() + ' '
26+ for name, method in BootMethodRegistry:
27+ if name != "pxe":
28+ output += CONDITIONAL_BOOTLOADER.format(
29+ behaviour=next(behaviour), arch_octet=method.arch_octet,
30+ bootloader=method.bootloader_path).strip() + ' '
31+
32+ # The PXEBootMethod is used in an else statement for the generated
33+ # dhcpd config. This ensures that a booting node that does not
34+ # provide an architecture octet, or architectures that emulate
35+ # pxelinux can still boot.
36+ pxe_method = BootMethodRegistry.get_item('pxe')
37+ if pxe_method is not None:
38+ output += PXE_BOOTLOADER.format(
39+ bootloader=pxe_method.bootloader_path).strip()
40 return output.strip()
41
42
43
44=== modified file 'src/provisioningserver/dhcp/tests/test_config.py'
45--- src/provisioningserver/dhcp/tests/test_config.py 2014-03-28 15:17:34 +0000
46+++ src/provisioningserver/dhcp/tests/test_config.py 2014-04-01 13:29:08 +0000
47@@ -116,8 +116,11 @@
48
49 def test_compose_conditional_bootloader(self):
50 output = config.compose_conditional_bootloader()
51- for _, method in BootMethodRegistry:
52- self.assertThat(output, Contains(method.arch_octet))
53+ for name, method in BootMethodRegistry:
54+ if name == "pxe":
55+ self.assertThat(output, Contains("else"))
56+ else:
57+ self.assertThat(output, Contains(method.arch_octet))
58 self.assertThat(output, Contains(method.bootloader_path))
59
60 def test_config_contains_compose_conditional_bootloader(self):