/usr/lib/python2.7/dist-packages/provisioningserver/boot/uefi.py is in python-maas-provisioningserver 1.5.4+bzr2294-0ubuntu1.2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | # Copyright 2012-2014 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""UEFI Boot Method"""
from __future__ import (
absolute_import,
print_function,
unicode_literals,
)
str = None
__metaclass__ = type
__all__ = [
'UEFIBootMethod',
]
from itertools import repeat
import os.path
import re
from textwrap import dedent
import urllib2
from provisioningserver.boot import (
BootMethod,
BootMethodInstallError,
BytesReader,
get_parameters,
utils,
)
from provisioningserver.boot.install_bootloader import (
install_bootloader,
make_destination,
)
from provisioningserver.utils import (
call_and_check,
tempdir,
)
ARCHIVE_URL = "http://archive.ubuntu.com/ubuntu/dists/"
ARCHIVE_PATH = "/main/uefi/grub2-amd64/current/grubnetx64.efi.signed"
CONFIG_FILE = dedent("""
# MAAS GRUB2 pre-loader configuration file
# Load based on MAC address first.
configfile (pxe)/grub/grub.cfg-${net_default_mac}
# Failed to load based on MAC address.
# Load amd64 by default, UEFI only supported by 64-bit
configfile (pxe)/grub/grub.cfg-default-amd64
""")
# GRUB EFINET represents a MAC address in IEEE 802 colon-seperated
# format. Required for UEFI as GRUB2 only presents the MAC address
# in colon-seperated format.
re_mac_address_octet = r'[0-9a-f]{2}'
re_mac_address = re.compile(
':'.join(repeat(re_mac_address_octet, 6)))
# Match the grub/grub.cfg-* request for UEFI (aka. GRUB2)
re_config_file = r'''
# Optional leading slash(es).
^/*
grub/grub[.]cfg # UEFI (aka. GRUB2) expects this.
-
(?: # either a MAC
(?P<mac>{re_mac_address.pattern}) # Capture UEFI MAC.
| # or "default"
default
(?: # perhaps with specified arch, with a separator of '-'
[-](?P<arch>\w+) # arch
(?:-(?P<subarch>\w+))? # optional subarch
)?
)
$
'''
re_config_file = re_config_file.format(
re_mac_address=re_mac_address)
re_config_file = re.compile(re_config_file, re.VERBOSE)
def archive_grubnet_urls():
"""Paths to try to download grubnetx64.efi.signed."""
release = utils.get_distro_release()
# grubnetx64 will not work below version trusty, as efinet is broken
# when loading kernel, force trusty. Note: This is only the grub version
# this should not block any of the previous release from running.
if release in ['lucid', 'precise', 'quantal', 'saucy']:
release = 'trusty'
for dist in ['%s-updates' % release, release]:
yield "%s/%s/%s" % (
ARCHIVE_URL.rstrip("/"),
dist,
ARCHIVE_PATH.rstrip("/"))
def download_grubnet(destination):
"""Downloads grubnetx64.efi.signed from the archive."""
for url in archive_grubnet_urls():
try:
response = urllib2.urlopen(url)
# Okay, if it fails as the updates area might not hold
# that file.
except urllib2.URLError:
continue
with open(destination, 'wb') as stream:
stream.write(response.read())
return True
return False
class UEFIBootMethod(BootMethod):
name = "uefi"
template_subdir = "uefi"
bootloader_path = "bootx64.efi"
arch_octet = "00:07" # AMD64 EFI
def match_path(self, backend, path):
"""Checks path for the configuration file that needs to be
generated.
:param backend: requesting backend
:param path: requested path
:returns: dict of match params from path, None if no match
"""
match = re_config_file.match(path)
if match is None:
return None
params = get_parameters(match)
# MAC address is in the wrong format, fix it
mac = params.get("mac")
if mac is not None:
params["mac"] = mac.replace(':', '-')
return params
def get_reader(self, backend, kernel_params, **extra):
"""Render a configuration file as a unicode string.
:param backend: requesting backend
:param kernel_params: An instance of `KernelParameters`.
:param extra: Allow for other arguments. This is a safety valve;
parameters generated in another component (for example, see
`TFTPBackend.get_boot_method_reader`) won't cause this to break.
"""
template = self.get_template(
kernel_params.purpose, kernel_params.arch,
kernel_params.subarch)
namespace = self.compose_template_namespace(kernel_params)
return BytesReader(template.substitute(namespace).encode("utf-8"))
def install_bootloader(self, destination):
"""Installs the required files for UEFI booting into the
tftproot.
"""
with tempdir() as tmp:
# Download the shim-signed package
data, filename = utils.get_updates_package(
'shim-signed', 'http://archive.ubuntu.com/ubuntu',
'main', 'amd64')
if data is None:
raise BootMethodInstallError(
'Failed to download shim-signed package from '
'the archive.')
shim_output = os.path.join(tmp, filename)
with open(shim_output, 'wb') as stream:
stream.write(data)
# Extract the package with dpkg, and install the shim
call_and_check(["dpkg", "-x", shim_output, tmp])
install_bootloader(
os.path.join(tmp, 'usr', 'lib', 'shim', 'shim.efi.signed'),
os.path.join(destination, self.bootloader_path))
# Download grubnetx64 from the archive and install
grub_tmp = os.path.join(tmp, 'grubnetx64.efi.signed')
if download_grubnet(grub_tmp) is False:
raise BootMethodInstallError(
'Failed to download grubnetx64.efi.signed '
'from the archive.')
grub_dst = os.path.join(destination, 'grubx64.efi')
install_bootloader(grub_tmp, grub_dst)
config_path = os.path.join(destination, 'grub')
config_dst = os.path.join(config_path, 'grub.cfg')
make_destination(config_path)
with open(config_dst, 'wb') as stream:
stream.write(CONFIG_FILE.encode("utf-8"))
|