1
# Copyright 2012-2014 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Install a pre-boot loader for TFTP download."""
6
from __future__ import (
22
from shutil import copyfile
24
from provisioningserver.boot.tftppath import locate_tftp_path
25
from provisioningserver.config import Config
28
def make_destination(tftproot):
29
"""Locate a loader's destination, creating the directory if needed.
31
:param tftproot: The root directory served up by the TFTP server,
32
e.g. /var/lib/maas/tftp/.
33
:return: Full path describing the directory that the installed loader
36
path = locate_tftp_path('', tftproot=tftproot)
37
directory = os.path.dirname(path)
38
if not os.path.isdir(directory):
39
os.makedirs(directory)
43
def are_identical_files(old, new):
44
"""Are `old` and `new` identical?
46
If `old` does not exist, the two are considered different (`new` is
49
if os.path.isfile(old):
50
return filecmp.cmp(old, new, shallow=False)
55
def install_bootloader(loader, destination):
56
"""Install bootloader file at path `loader` as `destination`.
58
Installation will be atomic. If an identical loader is already
59
installed, it will be left untouched.
61
However it is still conceivable, depending on the TFTP implementation,
62
that a download that is already in progress may suddenly start receiving
63
data from the new file instead of the one it originally started
66
:param loader: Name of loader to install.
67
:param destination: Loader's intended filename, including full path,
68
where it will become available over TFTP.
70
if are_identical_files(destination, loader):
73
# Copy new loader next to the old one, to ensure that it is on the
74
# same filesystem. Once it is, we can replace the old one with an
75
# atomic rename operation.
76
temp_file = '%s.new' % destination
77
if os.path.exists(temp_file):
79
copyfile(loader, temp_file)
80
os.rename(temp_file, destination)
83
def add_arguments(parser):
85
'--loader', dest='loader', default=None,
86
help="Pre-boot loader to install.")
90
"""Install a pre-boot loader into the TFTP directory structure.
92
This won't overwrite an existing loader if its contents are unchanged.
94
config = Config.load(args.config_file)
95
tftproot = config["tftp"]["resource_root"]
96
destination_path = make_destination(tftproot)
97
destination = os.path.join(destination_path, os.path.basename(args.loader))
98
install_bootloader(args.loader, destination)