~justin-fathomdb/nova/virtualbox-support

7.2.1 by Vishvananda Ishaya
patch from issue 4001
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
114 by Devin Carlen
Updated licenses
2
3
# Copyright 2010 United States Government as represented by the
3.1.9 by Vishvananda Ishaya
Removed trailing whitespace from header
4
# Administrator of the National Aeronautics and Space Administration.
114 by Devin Carlen
Updated licenses
5
# All Rights Reserved.
6
#
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
8
#    not use this file except in compliance with the License. You may obtain
9
#    a copy of the License at
10
#
11
#         http://www.apache.org/licenses/LICENSE-2.0
1 by Jesse Andrews
initial commit
12
#
13
#    Unless required by applicable law or agreed to in writing, software
114 by Devin Carlen
Updated licenses
14
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
#    License for the specific language governing permissions and limitations
17
#    under the License.
1 by Jesse Andrews
initial commit
18
19
"""
20
Utility methods to resize, repartition, and modify disk images.
21
Includes injection of SSH PGP keys into authorized_keys file.
22
"""
23
24
import logging
25
import os
26
import tempfile
150.77.1 by andy
rather comprehensive style fixes
27
7.2.1 by Vishvananda Ishaya
patch from issue 4001
28
from nova import exception
29
139.2.2 by Jesse Andrews
reorder imports spacing
30
7.2.1 by Vishvananda Ishaya
patch from issue 4001
31
def partition(infile, outfile, local_bytes=0, local_type='ext2', execute=None):
32
    """Takes a single partition represented by infile and writes a bootable
33
    drive image into outfile.
118.4.1 by Vishvananda Ishaya
fix key injection script
34
1 by Jesse Andrews
initial commit
35
    The first 63 sectors (0-62) of the resulting image is a master boot record.
36
    Infile becomes the first primary partition.
7.2.1 by Vishvananda Ishaya
patch from issue 4001
37
    If local bytes is specified, a second primary partition is created and
38
    formatted as ext2.
39
1 by Jesse Andrews
initial commit
40
    In the diagram below, dashes represent drive sectors.
150.15.1 by Monty Taylor
Updated sphinx layout to a two-dir layout like swift.
41
    +-----+------. . .-------+------. . .------+
42
    | 0  a| b               c|d               e|
1 by Jesse Andrews
initial commit
43
    +-----+------. . .-------+------. . .------+
44
    | mbr | primary partiton | local partition |
45
    +-----+------. . .-------+------. . .------+
46
    """
47
    sector_size = 512
48
    file_size = os.path.getsize(infile)
49
    if file_size % sector_size != 0:
7.2.1 by Vishvananda Ishaya
patch from issue 4001
50
        logging.warn("Input partition size not evenly divisible by"
51
                     " sector size: %d / %d", file_size, sector_size)
1 by Jesse Andrews
initial commit
52
    primary_sectors = file_size / sector_size
53
    if local_bytes % sector_size != 0:
7.2.1 by Vishvananda Ishaya
patch from issue 4001
54
        logging.warn("Bytes for local storage not evenly divisible"
55
                     " by sector size: %d / %d", local_bytes, sector_size)
1 by Jesse Andrews
initial commit
56
    local_sectors = local_bytes / sector_size
57
58
    mbr_last = 62 # a
59
    primary_first = mbr_last + 1 # b
67 by Vishvananda Ishaya
fix sectors off by one
60
    primary_last = primary_first + primary_sectors - 1 # c
1 by Jesse Andrews
initial commit
61
    local_first = primary_last + 1 # d
67 by Vishvananda Ishaya
fix sectors off by one
62
    local_last = local_first + local_sectors - 1 # e
1 by Jesse Andrews
initial commit
63
    last_sector = local_last # e
64
65
    # create an empty file
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
66
    execute('dd if=/dev/zero of=%s count=1 seek=%d bs=%d'
150.10.1 by Vishvananda Ishaya
fixed bug where partition code was sometimes failing due to initial dd not being yielded properly
67
                  % (outfile, last_sector, sector_size))
1 by Jesse Andrews
initial commit
68
69
    # make mbr partition
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
70
    execute('parted --script %s mklabel msdos' % outfile)
1 by Jesse Andrews
initial commit
71
72
    # make primary partition
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
73
    execute('parted --script %s mkpart primary %ds %ds'
7.2.1 by Vishvananda Ishaya
patch from issue 4001
74
                  % (outfile, primary_first, primary_last))
1 by Jesse Andrews
initial commit
75
76
    # make local partition
77
    if local_bytes > 0:
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
78
        execute('parted --script %s mkpartfs primary %s %ds %ds'
7.2.1 by Vishvananda Ishaya
patch from issue 4001
79
                      % (outfile, local_type, local_first, local_last))
1 by Jesse Andrews
initial commit
80
81
    # copy file into partition
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
82
    execute('dd if=%s of=%s bs=%d seek=%d conv=notrunc,fsync'
7.2.1 by Vishvananda Ishaya
patch from issue 4001
83
                  % (infile, outfile, sector_size, primary_first))
84
150.77.1 by andy
rather comprehensive style fixes
85
7.2.1 by Vishvananda Ishaya
patch from issue 4001
86
@defer.inlineCallbacks
158.1.4 by Justin Santa Barbara
Clarified what the 'Mapped device not found' exception really means.
87
def inject_data(    image, key=None, net=None, dns=None, 
88
                    remove_network_udev=False, 
89
                    partition=None, execute=None):
116.1.17 by Vishvananda Ishaya
Simple network injection
90
    """Injects a ssh key and optionally net data into a disk image.
91
7.2.1 by Vishvananda Ishaya
patch from issue 4001
92
    it will mount the image as a fully partitioned disk and attempt to inject
93
    into the specified partition number.
116.1.17 by Vishvananda Ishaya
Simple network injection
94
1 by Jesse Andrews
initial commit
95
    If partition is not specified it mounts the image as a single partition.
116.1.17 by Vishvananda Ishaya
Simple network injection
96
1 by Jesse Andrews
initial commit
97
    """
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
98
    out, err = execute('sudo losetup --find --show %s' % image)
1 by Jesse Andrews
initial commit
99
    if err:
7.2.1 by Vishvananda Ishaya
patch from issue 4001
100
        raise exception.Error('Could not attach image to loopback: %s' % err)
1 by Jesse Andrews
initial commit
101
    device = out.strip()
102
    try:
103
        if not partition is None:
104
            # create partition
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
105
            out, err = execute('sudo kpartx -a %s' % device)
1 by Jesse Andrews
initial commit
106
            if err:
7.2.1 by Vishvananda Ishaya
patch from issue 4001
107
                raise exception.Error('Failed to load partition: %s' % err)
108
            mapped_device = '/dev/mapper/%sp%s' % (device.split('/')[-1],
109
                                                   partition)
1 by Jesse Andrews
initial commit
110
        else:
111
            mapped_device = device
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
112
158.1.4 by Justin Santa Barbara
Clarified what the 'Mapped device not found' exception really means.
113
        # We can only loopback mount raw images.  If the device isn't there,
114
        #  it's normally because it's a .vmdk or a .vdi etc
161 by Justin Santa Barbara
Fixed up some of the raw disk stuff that broke in the abstraction out of libvirt
115
        if not os.path.exists(mapped_device):
158.1.4 by Justin Santa Barbara
Clarified what the 'Mapped device not found' exception really means.
116
            raise exception.Error(
117
                'Mapped device was not found (we can only inject raw disk images): %s'
118
                % mapped_device)
161 by Justin Santa Barbara
Fixed up some of the raw disk stuff that broke in the abstraction out of libvirt
119
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
120
        # Configure ext2fs so that it doesn't auto-check every N boots
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
121
        out, err = execute('sudo tune2fs -c 0 -i 0 %s' % mapped_device)
1 by Jesse Andrews
initial commit
122
123
        tmpdir = tempfile.mkdtemp()
124
        try:
125
            # mount loopback to dir
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
126
            out, err = execute(
7.2.1 by Vishvananda Ishaya
patch from issue 4001
127
                    'sudo mount %s %s' % (mapped_device, tmpdir))
1 by Jesse Andrews
initial commit
128
            if err:
7.2.1 by Vishvananda Ishaya
patch from issue 4001
129
                raise exception.Error('Failed to mount filesystem: %s' % err)
1 by Jesse Andrews
initial commit
130
131
            try:
116.1.17 by Vishvananda Ishaya
Simple network injection
132
                if key:
133
                    # inject key file
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
134
                    _inject_key_into_fs(key, tmpdir, execute=execute)
116.1.17 by Vishvananda Ishaya
Simple network injection
135
                if net:
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
136
                    _inject_net_into_fs(net, tmpdir, execute=execute)
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
137
                if dns:
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
138
                    _inject_dns_into_fs(dns, tmpdir, execute=execute)
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
139
                if remove_network_udev:
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
140
                    _remove_network_udev(tmpdir, execute=execute)
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
141
1 by Jesse Andrews
initial commit
142
            finally:
143
                # unmount device
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
144
                execute('sudo umount %s' % mapped_device)
1 by Jesse Andrews
initial commit
145
        finally:
146
            # remove temporary directory
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
147
            execute('rmdir %s' % tmpdir)
1 by Jesse Andrews
initial commit
148
            if not partition is None:
149
                # remove partitions
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
150
                execute('sudo kpartx -d %s' % device)
1 by Jesse Andrews
initial commit
151
    finally:
152
        # remove loopback
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
153
        execute('sudo losetup --detach %s' % device)
1 by Jesse Andrews
initial commit
154
116.1.17 by Vishvananda Ishaya
Simple network injection
155
def _inject_key_into_fs(key, fs, execute=None):
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
156
    sshdir = os.path.join(fs, 'root', '.ssh')
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
157
    execute('sudo mkdir -p %s' % sshdir) # existing dir doesn't matter
158
    execute('sudo chown root %s' % sshdir)
159
    execute('sudo chmod 700 %s' % sshdir)
1 by Jesse Andrews
initial commit
160
    keyfile = os.path.join(sshdir, 'authorized_keys')
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
161
    execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n')
1 by Jesse Andrews
initial commit
162
150.85.8 by Justin Santa Barbara
Merged with trunk (via virtualbox-support)
163
116.1.17 by Vishvananda Ishaya
Simple network injection
164
def _inject_net_into_fs(net, fs, execute=None):
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
165
    netfile = os.path.join(fs, 'etc', 'network', 'interfaces')
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
166
    execute('sudo tee %s' % netfile, net)
116.1.17 by Vishvananda Ishaya
Simple network injection
167
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
168
def _inject_dns_into_fs(dns, fs, execute=None):
169
    dnsfile = os.path.join(fs, 'etc', 'resolv.conf')
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
170
    execute('sudo tee %s' % dnsfile, dns)
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
171
172
def _remove_network_udev(fs, execute=None):
158.1.4 by Justin Santa Barbara
Clarified what the 'Mapped device not found' exception really means.
173
    # TODO(justinsb): This is correct for Ubuntu, but might not be right for
174
    #  other distros.  There is a much bigger discussion to be had about what
175
    #  we inject and how we inject it.
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
176
    rulesfile = os.path.join(fs, 'etc', 'udev', 'rules.d', '70-persistent-net.rules')
150.85.6 by Justin Santa Barbara
Removed twisted from the compute service
177
    execute('rm -f %s' % rulesfile)
154 by Justin Santa Barbara
Able to set up DNS, and remove udev network rules
178