~lutostag/ubuntu/trusty/maas/1.5.2+packagefix

« back to all changes in this revision

Viewing changes to src/provisioningserver/boot/__init__.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2014-03-28 10:43:53 UTC
  • mto: This revision was merged to the branch mainline in revision 57.
  • Revision ID: package-import@ubuntu.com-20140328104353-ekpolg0pm5xnvq2s
Tags: upstream-1.5+bzr2204
ImportĀ upstreamĀ versionĀ 1.5+bzr2204

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Boot Methods."""
 
5
 
 
6
from __future__ import (
 
7
    absolute_import,
 
8
    print_function,
 
9
    unicode_literals,
 
10
    )
 
11
 
 
12
str = None
 
13
 
 
14
__metaclass__ = type
 
15
__all__ = [
 
16
    "BootMethod",
 
17
    "BootMethodRegistry",
 
18
    ]
 
19
 
 
20
from abc import (
 
21
    ABCMeta,
 
22
    abstractmethod,
 
23
    abstractproperty,
 
24
    )
 
25
from errno import ENOENT
 
26
from os import path
 
27
 
 
28
from provisioningserver.boot.tftppath import compose_image_path
 
29
from provisioningserver.kernel_opts import compose_kernel_command_line
 
30
from provisioningserver.utils import locate_config
 
31
from provisioningserver.utils.registry import Registry
 
32
import tempita
 
33
 
 
34
 
 
35
class BootMethodError(Exception):
 
36
    """Exception raised for errors from a BootMethod."""
 
37
 
 
38
 
 
39
class BootMethodInstallError(BootMethodError):
 
40
    """Exception raised for errors from a BootMethod performing
 
41
    install_bootloader.
 
42
    """
 
43
 
 
44
 
 
45
def get_parameters(match):
 
46
    """Helper that gets the matched parameters from the
 
47
    regex match.
 
48
    """
 
49
    return {
 
50
        key: value
 
51
        for key, value in match.groupdict().items()
 
52
        if value is not None
 
53
        }
 
54
 
 
55
 
 
56
def gen_template_filenames(purpose, arch, subarch):
 
57
    """List possible template filenames.
 
58
 
 
59
    :param purpose: The boot purpose, e.g. "local".
 
60
    :param arch: Main machine architecture.
 
61
    :param subarch: Sub-architecture, or "generic" if there is none.
 
62
 
 
63
    Returns a list of possible PXE template filenames using the following
 
64
    lookup order:
 
65
 
 
66
      config.{purpose}.{arch}.{subarch}.template
 
67
      config.{purpose}.{arch}.template
 
68
      config.{purpose}.template
 
69
      config.template
 
70
 
 
71
    """
 
72
    elements = [purpose, arch, subarch]
 
73
    while len(elements) >= 1:
 
74
        yield "config.%s.template" % ".".join(elements)
 
75
        elements.pop()
 
76
    yield "config.template"
 
77
 
 
78
 
 
79
class BootMethod:
 
80
    """Skeleton for a boot method."""
 
81
 
 
82
    __metaclass__ = ABCMeta
 
83
 
 
84
    @abstractproperty
 
85
    def name(self):
 
86
        """Name of the boot method."""
 
87
 
 
88
    @abstractproperty
 
89
    def template_subdir(self):
 
90
        """Name of template sub-directory."""
 
91
 
 
92
    @abstractproperty
 
93
    def bootloader_path(self):
 
94
        """Relative path from tftproot to boot loader."""
 
95
 
 
96
    @abstractproperty
 
97
    def arch_octet(self):
 
98
        """Architecture type that supports this method. Used for the
 
99
        dhcpd.conf file that is generated. Must be in the format XX:XX.
 
100
        """
 
101
 
 
102
    @abstractmethod
 
103
    def match_config_path(self, path):
 
104
        """Checks path for the configuration file that needs to be
 
105
        generated.
 
106
 
 
107
        :param path: requested path
 
108
        :returns: dict of match params from path, None if no match
 
109
        """
 
110
 
 
111
    @abstractmethod
 
112
    def render_config(self, kernel_params, **extra):
 
113
        """Render a configuration file as a unicode string.
 
114
 
 
115
        :param kernel_params: An instance of `KernelParameters`.
 
116
        :param extra: Allow for other arguments. This is a safety valve;
 
117
            parameters generated in another component (for example, see
 
118
            `TFTPBackend.get_config_reader`) won't cause this to break.
 
119
        """
 
120
 
 
121
    @abstractmethod
 
122
    def install_bootloader(self, destination):
 
123
        """Installs the required files for this boot method into the
 
124
        destination.
 
125
 
 
126
        :param destination: path to install bootloader
 
127
        """
 
128
 
 
129
    def get_template_dir(self):
 
130
        """Gets the template directory for the boot method."""
 
131
        return locate_config("templates/%s" % self.template_subdir)
 
132
 
 
133
    def get_template(self, purpose, arch, subarch):
 
134
        """Gets the best avaliable template for the boot method.
 
135
 
 
136
        Templates are loaded each time here so that they can be changed on
 
137
        the fly without restarting the provisioning server.
 
138
 
 
139
        :param purpose: The boot purpose, e.g. "local".
 
140
        :param arch: Main machine architecture.
 
141
        :param subarch: Sub-architecture, or "generic" if there is none.
 
142
        :returns: `tempita.Template`
 
143
        """
 
144
        pxe_templates_dir = self.get_template_dir()
 
145
        for filename in gen_template_filenames(purpose, arch, subarch):
 
146
            template_name = path.join(pxe_templates_dir, filename)
 
147
            try:
 
148
                return tempita.Template.from_filename(
 
149
                    template_name, encoding="UTF-8")
 
150
            except IOError as error:
 
151
                if error.errno != ENOENT:
 
152
                    raise
 
153
        else:
 
154
            error = (
 
155
                "No PXE template found in %r for:\n"
 
156
                "  Purpose: %r, Arch: %r, Subarch: %r\n"
 
157
                "This can happen if you manually power up a node when its "
 
158
                "state is not one that allows it. Is the node in the "
 
159
                "'Declared' or 'Ready' states? It needs to be Enlisting, "
 
160
                "Commissioning or Allocated." % (
 
161
                    pxe_templates_dir, purpose, arch, subarch))
 
162
 
 
163
            raise AssertionError(error)
 
164
 
 
165
    def compose_template_namespace(self, kernel_params):
 
166
        """Composes the namespace variables that are used by a boot
 
167
        method template.
 
168
        """
 
169
        def image_dir(params):
 
170
            return compose_image_path(
 
171
                params.arch, params.subarch,
 
172
                params.release, params.label)
 
173
 
 
174
        def initrd_path(params):
 
175
            if params.purpose == "install":
 
176
                return "%s/di-initrd" % image_dir(params)
 
177
            else:
 
178
                return "%s/boot-initrd" % image_dir(params)
 
179
 
 
180
        def kernel_path(params):
 
181
            if params.purpose == "install":
 
182
                return "%s/di-kernel" % image_dir(params)
 
183
            else:
 
184
                return "%s/boot-kernel" % image_dir(params)
 
185
 
 
186
        def kernel_command(params):
 
187
            return compose_kernel_command_line(params)
 
188
 
 
189
        namespace = {
 
190
            "initrd_path": initrd_path,
 
191
            "kernel_command": kernel_command,
 
192
            "kernel_params": kernel_params,
 
193
            "kernel_path": kernel_path,
 
194
            }
 
195
        return namespace
 
196
 
 
197
 
 
198
class BootMethodRegistry(Registry):
 
199
    """Registry for boot method classes."""
 
200
 
 
201
 
 
202
# Import the supported boot methods after defining BootMethod.
 
203
from provisioningserver.boot.pxe import PXEBootMethod
 
204
from provisioningserver.boot.uefi import UEFIBootMethod
 
205
 
 
206
 
 
207
builtin_boot_methods = [
 
208
    PXEBootMethod(),
 
209
    UEFIBootMethod(),
 
210
]
 
211
for method in builtin_boot_methods:
 
212
    BootMethodRegistry.register_item(method.name, method)