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

« back to all changes in this revision

Viewing changes to src/provisioningserver/boot/install_bootloader.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 2012-2014 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Install a pre-boot loader for TFTP download."""
 
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
    "add_arguments",
 
17
    "run",
 
18
    ]
 
19
 
 
20
import filecmp
 
21
import os.path
 
22
from shutil import copyfile
 
23
 
 
24
from provisioningserver.boot.tftppath import locate_tftp_path
 
25
from provisioningserver.config import Config
 
26
 
 
27
 
 
28
def make_destination(tftproot):
 
29
    """Locate a loader's destination, creating the directory if needed.
 
30
 
 
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
 
34
        should end up having.
 
35
    """
 
36
    path = locate_tftp_path('', tftproot=tftproot)
 
37
    directory = os.path.dirname(path)
 
38
    if not os.path.isdir(directory):
 
39
        os.makedirs(directory)
 
40
    return directory
 
41
 
 
42
 
 
43
def are_identical_files(old, new):
 
44
    """Are `old` and `new` identical?
 
45
 
 
46
    If `old` does not exist, the two are considered different (`new` is
 
47
    assumed to exist).
 
48
    """
 
49
    if os.path.isfile(old):
 
50
        return filecmp.cmp(old, new, shallow=False)
 
51
    else:
 
52
        return False
 
53
 
 
54
 
 
55
def install_bootloader(loader, destination):
 
56
    """Install bootloader file at path `loader` as `destination`.
 
57
 
 
58
    Installation will be atomic.  If an identical loader is already
 
59
    installed, it will be left untouched.
 
60
 
 
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
 
64
    downloading.
 
65
 
 
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.
 
69
    """
 
70
    if are_identical_files(destination, loader):
 
71
        return
 
72
 
 
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):
 
78
        os.remove(temp_file)
 
79
    copyfile(loader, temp_file)
 
80
    os.rename(temp_file, destination)
 
81
 
 
82
 
 
83
def add_arguments(parser):
 
84
    parser.add_argument(
 
85
        '--loader', dest='loader', default=None,
 
86
        help="Pre-boot loader to install.")
 
87
 
 
88
 
 
89
def run(args):
 
90
    """Install a pre-boot loader into the TFTP directory structure.
 
91
 
 
92
    This won't overwrite an existing loader if its contents are unchanged.
 
93
    """
 
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)