~ubuntu-branches/ubuntu/raring/maas/raring-security

« back to all changes in this revision

Viewing changes to .pc/99-fix-maas-fpi.patch/src/provisioningserver/pxe/install_image.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez, Chris Van Hook, Steve Langasek, Andres Rodriguez
  • Date: 2013-03-20 13:08:04 UTC
  • mfrom: (1.2.7)
  • Revision ID: package-import@ubuntu.com-20130320130804-ngvuxq6zg75h0ljr
Tags: 1.3+bzr1461+dfsg-0ubuntu1
* This is a new upstream bugfixs releases only. It includes:
  - Fix detection of non-existant ipmi device in nested kvm (LP: #1064527)
  - Fix IPMI User creation. (LP: #1119696)
  - Assign nodes to the correct nodegroup. (LP: #1148016)
  - Fix to provide useful error reporting for power management (LP: #1155175)

[ Chris Van Hook ]
* debian/patches/99-fix-ipmi-stat-lp1086160.patch: Fix ipmi power command
  to correctly use --stat. (LP: #1086160)

[ Steve Langasek ]
* Add missing dependency on iproute to maas-region-controller, for use of
  /sbin/ip in postinst.

[ Andres Rodriguez ]
* debian/patches/99-fix-maas-fpi.patch: Fix FPI, otherwise nodes have the
  risk of not being installed at all.
* debian/control: Depends on apache2 for maas-cluster-controller
* debian/maas-cluster-controller.install: Install maas-cluster-http.conf
* debian/maas-cluster-controller.{postinst,postrm}: Handle symlink and
  removal of maas-cluster-http.conf.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2012 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Install a netboot image directory for TFTP download."""
 
5
 
 
6
from __future__ import (
 
7
    absolute_import,
 
8
    print_function,
 
9
    unicode_literals,
 
10
    )
 
11
 
 
12
__metaclass__ = type
 
13
__all__ = [
 
14
    "add_arguments",
 
15
    "run",
 
16
    ]
 
17
 
 
18
from filecmp import cmpfiles
 
19
import os.path
 
20
from shutil import (
 
21
    copytree,
 
22
    rmtree,
 
23
    )
 
24
 
 
25
from provisioningserver.config import Config
 
26
from provisioningserver.pxe.tftppath import (
 
27
    compose_image_path,
 
28
    locate_tftp_path,
 
29
    )
 
30
from twisted.python.filepath import FilePath
 
31
 
 
32
 
 
33
def make_destination(tftproot, arch, subarch, release, purpose):
 
34
    """Locate an image's destination.  Create containing directory if needed.
 
35
 
 
36
    :param tftproot: The root directory served up by the TFTP server,
 
37
        e.g. /var/lib/maas/tftp/.
 
38
    :param arch: Main architecture to locate the destination for.
 
39
    :param subarch: Sub-architecture of the main architecture.
 
40
    :param release: OS release name, e.g. "precise".
 
41
    :param purpose: Purpose of this image, e.g. "install".
 
42
    :return: Full path describing the image directory's new location and
 
43
        name.  In other words, a file "linux" in the image directory should
 
44
        become os.path.join(make_destination(...), 'linux').
 
45
    """
 
46
    dest = locate_tftp_path(
 
47
        compose_image_path(arch, subarch, release, purpose),
 
48
        tftproot=tftproot)
 
49
    parent = os.path.dirname(dest)
 
50
    if not os.path.isdir(parent):
 
51
        os.makedirs(parent)
 
52
    return dest
 
53
 
 
54
 
 
55
def are_identical_dirs(old, new):
 
56
    """Do directories `old` and `new` contain identical files?
 
57
 
 
58
    It's OK for `old` not to exist; that is considered a difference rather
 
59
    than an error.  But `new` is assumed to exist - if it doesn't, you
 
60
    shouldn't have come far enough to call this function.
 
61
    """
 
62
    assert os.path.isdir(new)
 
63
    if os.path.isdir(old):
 
64
        files = set(os.listdir(old) + os.listdir(new))
 
65
        # The shallow=False is needed to make cmpfiles() compare file
 
66
        # contents.  Otherwise it only compares os.stat() results,
 
67
        match, mismatch, errors = cmpfiles(old, new, files, shallow=False)
 
68
        return len(match) == len(files)
 
69
    else:
 
70
        return False
 
71
 
 
72
 
 
73
def install_dir(new, old):
 
74
    """Install directory `new`, replacing directory `old` if it exists.
 
75
 
 
76
    This works as atomically as possible, but isn't entirely.  Moreover,
 
77
    any TFTP downloads that are reading from the old directory during
 
78
    the move may receive inconsistent data, with some of the files (or
 
79
    parts of files!) coming from the old directory and some from the
 
80
    new.
 
81
 
 
82
    Some temporary paths will be used that are identical to `old`, but with
 
83
    suffixes ".old" or ".new".  If either of these directories already
 
84
    exists, it will be mercilessly deleted.
 
85
 
 
86
    This function makes no promises about whether it moves or copies
 
87
    `new` into place.  The caller should make an attempt to clean it up,
 
88
    but be prepared for it not being there.
 
89
    """
 
90
    # Get rid of any leftover temporary directories from potential
 
91
    # interrupted previous runs.
 
92
    rmtree('%s.old' % old, ignore_errors=True)
 
93
    rmtree('%s.new' % old, ignore_errors=True)
 
94
 
 
95
    # We have to move the existing directory out of the way and the new
 
96
    # one into place.  Between those steps, there is a window where
 
97
    # neither is in place.  To minimize that window, move the new one
 
98
    # into the same location (ensuring that it no longer needs copying
 
99
    # from one partition to another) and then swizzle the two as quickly
 
100
    # as possible.
 
101
    # This could be a simple "remove" if the downloaded image is on the
 
102
    # same filesystem as the destination, but because that isn't
 
103
    # certain, copy instead.  It's not particularly fast, but the extra
 
104
    # work happens outside the critical window so it shouldn't matter
 
105
    # much.
 
106
    copytree(new, '%s.new' % old)
 
107
 
 
108
    # Normalise permissions.
 
109
    for filepath in FilePath('%s.new' % old).walk():
 
110
        if filepath.isdir():
 
111
            filepath.chmod(0755)
 
112
        else:
 
113
            filepath.chmod(0644)
 
114
 
 
115
    # Start of critical window.
 
116
    if os.path.isdir(old):
 
117
        os.rename(old, '%s.old' % old)
 
118
    os.rename('%s.new' % old, old)
 
119
    # End of critical window.
 
120
 
 
121
    # Now delete the old image directory at leisure.
 
122
    rmtree('%s.old' % old, ignore_errors=True)
 
123
 
 
124
 
 
125
def add_arguments(parser):
 
126
    parser.add_argument(
 
127
        '--arch', dest='arch', default=None,
 
128
        help="Main system architecture that the image is for.")
 
129
    parser.add_argument(
 
130
        '--subarch', dest='subarch', default='generic',
 
131
        help="Sub-architecture of the main architecture [%(default)s].")
 
132
    parser.add_argument(
 
133
        '--release', dest='release', default=None,
 
134
        help="Ubuntu release that the image is for.")
 
135
    parser.add_argument(
 
136
        '--purpose', dest='purpose', default=None,
 
137
        help="Purpose of the image (e.g. 'install' or 'commissioning').")
 
138
    parser.add_argument(
 
139
        '--image', dest='image', default=None,
 
140
        help="Netboot image directory, containing kernel & initrd.")
 
141
 
 
142
 
 
143
def run(args):
 
144
    """Move a netboot image into the TFTP directory structure.
 
145
 
 
146
    The image is a directory containing a kernel and an initrd.  If the
 
147
    destination location already has an image of the same name and
 
148
    containing identical files, the new image is deleted and the old one
 
149
    is left untouched.
 
150
    """
 
151
    config = Config.load(args.config_file)
 
152
    tftproot = config["tftp"]["root"]
 
153
    destination = make_destination(
 
154
        tftproot, args.arch, args.subarch, args.release, args.purpose)
 
155
    if not are_identical_dirs(destination, args.image):
 
156
        # Image has changed.  Move the new version into place.
 
157
        install_dir(args.image, destination)
 
158
    rmtree(args.image, ignore_errors=True)