~ubuntu-branches/ubuntu/quantal/virtinst/quantal-proposed

« back to all changes in this revision

Viewing changes to .pc/0001-fix-path-to-hvmloader.patch/virtinst/Installer.py

  • Committer: Bazaar Package Importer
  • Author(s): Marc Deslauriers
  • Date: 2011-02-01 15:40:11 UTC
  • mfrom: (1.3.16 experimental)
  • Revision ID: james.westby@ubuntu.com-20110201154011-op0nusgc240xajvb
Tags: 0.500.5-1ubuntu1
* Merge from debian experimental. Remaining changes:
  - debian/patches/9001_Ubuntu.patch:
     + Updated to add maverick and natty to OS list and enable virtio
       for them.
  - debian/patches/9003-fix-path-to-hvmloader-in-testsuite.patch: adjust
    testsuite for 0001-fix-path-to-hvmloader.patch and
    0002-Fix-path-to-pygrub.patch. (refreshed)
  - debian/control: added acl package to depends.
  - Demote virt-viewer to Suggests, as it's in universe.
  - Recommends libvirt-bin
* Removed patches:
  - debian/patches/9002-libvirt_disk_format.patch: Upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Common code for all guests
 
3
#
 
4
# Copyright 2006-2009  Red Hat, Inc.
 
5
# Jeremy Katz <katzj@redhat.com>
 
6
#
 
7
# This program is free software; you can redistribute it and/or modify
 
8
# it under the terms of the GNU General Public License as published by
 
9
# the Free  Software Foundation; either version 2 of the License, or
 
10
# (at your option)  any later version.
 
11
#
 
12
# This program is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
# GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License
 
18
# along with this program; if not, write to the Free Software
 
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
20
# MA 02110-1301 USA.
 
21
 
 
22
import os
 
23
import errno
 
24
import struct
 
25
import platform
 
26
import logging
 
27
import copy
 
28
 
 
29
import _util
 
30
import virtinst
 
31
import XMLBuilderDomain
 
32
from XMLBuilderDomain import _xml_property
 
33
from virtinst import CapabilitiesParser
 
34
from virtinst import _virtinst as _
 
35
from VirtualDisk import VirtualDisk
 
36
from Boot import Boot
 
37
 
 
38
XEN_SCRATCH = "/var/lib/xen"
 
39
LIBVIRT_SCRATCH = "/var/lib/libvirt/boot"
 
40
 
 
41
def _get_scratchdir(typ):
 
42
    scratch = None
 
43
    if platform.system() == 'SunOS':
 
44
        scratch = '/var/tmp'
 
45
 
 
46
    if os.geteuid() == 0:
 
47
        if typ == "xen" and os.path.exists(XEN_SCRATCH):
 
48
            scratch = XEN_SCRATCH
 
49
        elif os.path.exists(LIBVIRT_SCRATCH):
 
50
            scratch = LIBVIRT_SCRATCH
 
51
 
 
52
    if not scratch:
 
53
        scratch = os.path.expanduser("~/.virtinst/boot")
 
54
        if not os.path.exists(scratch):
 
55
            os.makedirs(scratch, 0751)
 
56
        _util.selinux_restorecon(scratch)
 
57
 
 
58
    return scratch
 
59
 
 
60
class Installer(XMLBuilderDomain.XMLBuilderDomain):
 
61
    """
 
62
    Installer classes attempt to encapsulate all the parameters needed
 
63
    to 'install' a guest: essentially, booting the guest with the correct
 
64
    media for the OS install phase (if there is one), and setting up the
 
65
    guest to boot to the correct media for all subsequent runs.
 
66
 
 
67
    Some of the actual functionality:
 
68
 
 
69
        - Determining what type of install media has been requested, and
 
70
          representing it correctly to the Guest
 
71
 
 
72
        - Fetching install kernel/initrd or boot.iso from a URL
 
73
 
 
74
        - Setting the boot device as appropriate depending on whether we
 
75
          are booting into an OS install, or booting post-install
 
76
 
 
77
    Some of the information that the Installer needs to know to accomplish
 
78
    this:
 
79
 
 
80
        - Install media location (could be a URL, local path, ...)
 
81
        - Virtualization type (parameter 'os_type') ('xen', 'hvm', etc.)
 
82
        - Hypervisor name (parameter 'type') ('qemu', 'kvm', 'xen', etc.)
 
83
        - Guest architecture ('i686', 'x86_64')
 
84
    """
 
85
 
 
86
    _dumpxml_xpath = "/domain/os"
 
87
    def __init__(self, type="xen", location=None, boot=None,
 
88
                 extraargs=None, os_type=None, conn=None,
 
89
                 parsexml=None, parsexmlnode=None, caps=None):
 
90
        XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
 
91
                                                   parsexmlnode, caps=caps)
 
92
 
 
93
        self._type = None
 
94
        self._location = None
 
95
        self._initrd_injections = []
 
96
        self._cdrom = False
 
97
        self._os_type = None
 
98
        self._scratchdir = None
 
99
        self._arch = None
 
100
        self._machine = None
 
101
        self._loader = None
 
102
        self._install_bootconfig = Boot(self.conn)
 
103
        self._bootconfig = Boot(self.conn, parsexml, parsexmlnode)
 
104
 
 
105
        # Devices created/added during the prepare() stage
 
106
        self.install_devices = []
 
107
 
 
108
        if self._is_parse():
 
109
            return
 
110
 
 
111
        # FIXME: Better solution? Skip validating this since we may not be
 
112
        # able to install a VM of the host arch
 
113
        if self._get_caps():
 
114
            self._arch = self._get_caps().host.arch
 
115
 
 
116
        if type is None:
 
117
            type = "xen"
 
118
        self.type = type
 
119
 
 
120
        if not os_type is None:
 
121
            self.os_type = os_type
 
122
        else:
 
123
            self.os_type = "xen"
 
124
        if not location is None:
 
125
            self.location = location
 
126
 
 
127
        if not boot is None:
 
128
            self.boot = boot
 
129
        self.extraargs = extraargs
 
130
 
 
131
        self._tmpfiles = []
 
132
 
 
133
    def get_conn(self):
 
134
        return self._conn
 
135
    conn = property(get_conn)
 
136
 
 
137
    def _get_bootconfig(self):
 
138
        return self._bootconfig
 
139
    bootconfig = property(_get_bootconfig)
 
140
 
 
141
    # Hypervisor name (qemu, kvm, xen, lxc, etc.)
 
142
    def get_type(self):
 
143
        return self._type
 
144
    def set_type(self, val):
 
145
        self._type = val
 
146
    type = _xml_property(get_type, set_type,
 
147
                         xpath="./@type")
 
148
 
 
149
    # Virtualization type ('xen' == xen paravirt, or 'hvm)
 
150
    def get_os_type(self):
 
151
        return self._os_type
 
152
    def set_os_type(self, val):
 
153
        # Older libvirt back compat: if user specifies 'linux', convert
 
154
        # internally to newer equivalent value 'xen'
 
155
        if val == "linux":
 
156
            val = "xen"
 
157
 
 
158
        # XXX: Need to validate this: have some whitelist based on caps?
 
159
        self._os_type = val
 
160
    os_type = _xml_property(get_os_type, set_os_type,
 
161
                            xpath="./os/type")
 
162
 
 
163
    def get_arch(self):
 
164
        return self._arch
 
165
    def set_arch(self, val):
 
166
        # XXX: Sanitize to a consisten value (i368 -> i686)
 
167
        # XXX: Validate against caps
 
168
        self._arch = val
 
169
    arch = _xml_property(get_arch, set_arch,
 
170
                         xpath="./os/type/@arch")
 
171
 
 
172
    def _get_machine(self):
 
173
        return self._machine
 
174
    def _set_machine(self, val):
 
175
        self._machine = val
 
176
    machine = _xml_property(_get_machine, _set_machine,
 
177
                            xpath="./os/type/@machine")
 
178
 
 
179
    def _get_loader(self):
 
180
        return self._loader
 
181
    def _set_loader(self, val):
 
182
        self._loader = val
 
183
    loader = _xml_property(_get_loader, _set_loader,
 
184
                           xpath="./os/loader")
 
185
 
 
186
    def get_scratchdir(self):
 
187
        if not self.scratchdir_required():
 
188
            return None
 
189
 
 
190
        if not self._scratchdir:
 
191
            self._scratchdir = _get_scratchdir(self.type)
 
192
        return self._scratchdir
 
193
    scratchdir = property(get_scratchdir)
 
194
 
 
195
    def get_cdrom(self):
 
196
        return self._cdrom
 
197
    def set_cdrom(self, enable):
 
198
        if enable not in [True, False]:
 
199
            raise ValueError(_("Guest.cdrom must be a boolean type"))
 
200
        self._cdrom = enable
 
201
    cdrom = property(get_cdrom, set_cdrom)
 
202
 
 
203
    def get_location(self):
 
204
        return self._location
 
205
    def set_location(self, val):
 
206
        self._location = val
 
207
    location = property(get_location, set_location)
 
208
 
 
209
    def get_initrd_injections(self):
 
210
        return self._initrd_injections
 
211
    def set_initrd_injections(self, val):
 
212
        self._initrd_injections = val
 
213
    initrd_injections = property(get_initrd_injections, set_initrd_injections)
 
214
 
 
215
    # kernel + initrd pair to use for installing as opposed to using a location
 
216
    def get_boot(self):
 
217
        return {"kernel" : self._install_bootconfig.kernel,
 
218
                "initrd" : self._install_bootconfig.initrd}
 
219
    def set_boot(self, val):
 
220
        self.cdrom = False
 
221
        boot = {}
 
222
        if type(val) == tuple:
 
223
            if len(val) != 2:
 
224
                raise ValueError(_("Must pass both a kernel and initrd"))
 
225
            (k, i) = val
 
226
            boot = {"kernel": k, "initrd": i}
 
227
 
 
228
        elif type(val) == dict:
 
229
            if "kernel" not in val or "initrd" not in val:
 
230
                raise ValueError(_("Must pass both a kernel and initrd"))
 
231
            boot = val
 
232
 
 
233
        elif type(val) == list:
 
234
            if len(val) != 2:
 
235
                raise ValueError(_("Must pass both a kernel and initrd"))
 
236
            boot = {"kernel": val[0], "initrd": val[1]}
 
237
 
 
238
        else:
 
239
            raise ValueError(_("Kernel and initrd must be specified by "
 
240
                               "a list, dict, or tuple."))
 
241
 
 
242
        self._install_bootconfig.kernel = boot.get("kernel")
 
243
        self._install_bootconfig.initrd = boot.get("initrd")
 
244
 
 
245
    boot = property(get_boot, set_boot)
 
246
 
 
247
    # extra arguments to pass to the guest installer
 
248
    def get_extra_args(self):
 
249
        return self._install_bootconfig.kernel_args
 
250
    def set_extra_args(self, val):
 
251
        self._install_bootconfig.kernel_args = val
 
252
    extraargs = property(get_extra_args, set_extra_args)
 
253
 
 
254
 
 
255
    # Public helper methods
 
256
    def scratchdir_required(self):
 
257
        """
 
258
        Returns true if scratchdir is needed for the passed install parameters.
 
259
        Apps can use this to determine if they should attempt to ensure
 
260
        scratchdir permissions are adequate
 
261
        """
 
262
        return False
 
263
 
 
264
    # Private methods
 
265
    def _get_bootdev(self, isinstall, guest):
 
266
        raise NotImplementedError
 
267
 
 
268
    def _build_boot_order(self, isinstall, guest):
 
269
        bootorder = [self._get_bootdev(isinstall, guest)]
 
270
 
 
271
        # If guest has an attached disk, always have 'hd' in the boot
 
272
        # list, so disks are marked as bootable/installable (needed for
 
273
        # windows virtio installs, and booting local disk from PXE)
 
274
        for disk in guest.get_devices("disk"):
 
275
            if disk.device == disk.DEVICE_DISK:
 
276
                bootdev = self.bootconfig.BOOT_DEVICE_HARDDISK
 
277
                if bootdev not in bootorder:
 
278
                    bootorder.append(bootdev)
 
279
                break
 
280
 
 
281
        return bootorder
 
282
 
 
283
    def _get_osblob_helper(self, guest, isinstall, bootconfig):
 
284
        ishvm = self.os_type == "hvm"
 
285
        conn = guest.conn
 
286
        arch = self.arch
 
287
        loader = self.loader
 
288
        if not loader and ishvm and self.type == "xen":
 
289
            loader = "/usr/lib/xen/boot/hvmloader"
 
290
 
 
291
        if not isinstall and not ishvm and not self.bootconfig.kernel:
 
292
            return "<bootloader>%s</bootloader>" % _util.pygrub_path(conn)
 
293
 
 
294
        osblob = "<os>\n"
 
295
 
 
296
        os_type = self.os_type
 
297
        # Hack for older libvirt: use old value 'linux' for best back compat,
 
298
        # new libvirt will adjust the value accordingly.
 
299
        if os_type == "xen" and self.type == "xen":
 
300
            os_type = "linux"
 
301
 
 
302
        osblob += "    <type"
 
303
        if arch:
 
304
            osblob += " arch='%s'" % arch
 
305
        if self.machine:
 
306
            osblob += " machine='%s'" % self.machine
 
307
        osblob += ">%s</type>\n" % os_type
 
308
 
 
309
        if loader:
 
310
            osblob += "    <loader>%s</loader>\n" % loader
 
311
 
 
312
        osblob += bootconfig.get_xml_config()
 
313
        osblob = _util.xml_append(osblob, "  </os>")
 
314
 
 
315
        return osblob
 
316
 
 
317
 
 
318
    # Method definitions
 
319
 
 
320
    def _get_xml_config(self, guest, isinstall):
 
321
        """
 
322
        Generate the portion of the guest xml that determines boot devices
 
323
        and parameters. (typically the <os></os> block)
 
324
 
 
325
        @param guest: Guest instance we are installing
 
326
        @type guest: L{Guest}
 
327
        @param isinstall: Whether we want xml for the 'install' phase or the
 
328
                          'post-install' phase.
 
329
        @type isinstall: C{bool}
 
330
        """
 
331
        if isinstall:
 
332
            bootconfig = self._install_bootconfig
 
333
        else:
 
334
            bootconfig = self.bootconfig
 
335
 
 
336
        if isinstall and not self.has_install_phase():
 
337
            return
 
338
 
 
339
        bootorder = self._build_boot_order(isinstall, guest)
 
340
        bootconfig = copy.copy(bootconfig)
 
341
        if not bootconfig.bootorder:
 
342
            bootconfig.bootorder = bootorder
 
343
 
 
344
        return self._get_osblob_helper(guest, isinstall, bootconfig)
 
345
 
 
346
    def has_install_phase(self):
 
347
        """
 
348
        Return True if the requested setup is actually installing an OS
 
349
        into the guest. Things like LiveCDs, Import, or a manually specified
 
350
        bootorder do not have an install phase.
 
351
        """
 
352
        return True
 
353
 
 
354
    def cleanup(self):
 
355
        """
 
356
        Remove any temporary files retrieved during installation
 
357
        """
 
358
        for f in self._tmpfiles:
 
359
            logging.debug("Removing " + f)
 
360
            os.unlink(f)
 
361
        self._tmpfiles = []
 
362
        self.install_devices = []
 
363
 
 
364
    def prepare(self, guest, meter):
 
365
        """
 
366
        Fetch any files needed for installation.
 
367
        @param guest: guest instance being installed
 
368
        @type L{Guest}
 
369
        @param meter: progress meter
 
370
        @type Urlgrabber ProgressMeter
 
371
        """
 
372
        raise NotImplementedError("Must be implemented in subclass")
 
373
 
 
374
    def post_install_check(self, guest):
 
375
        """
 
376
        Attempt to verify that installing to disk was successful.
 
377
        @param guest: guest instance that was installed
 
378
        @type L{Guest}
 
379
        """
 
380
 
 
381
        if _util.is_uri_remote(guest.conn.getURI()):
 
382
            # XXX: Use block peek for this?
 
383
            return True
 
384
 
 
385
        if (len(guest.disks) == 0 or
 
386
            guest.disks[0].device != VirtualDisk.DEVICE_DISK):
 
387
            return True
 
388
 
 
389
        disk = guest.disks[0]
 
390
 
 
391
        if _util.is_vdisk(disk.path):
 
392
            return True
 
393
 
 
394
        if (disk.driver_type and
 
395
            disk.driver_type not in [disk.DRIVER_TAP_RAW,
 
396
                                     disk.DRIVER_QEMU_RAW]):
 
397
            # Might be a non-raw format
 
398
            return True
 
399
 
 
400
        # Check for the 0xaa55 signature at the end of the MBR
 
401
        try:
 
402
            fd = os.open(disk.path, os.O_RDONLY)
 
403
        except OSError, (err, msg):
 
404
            logging.debug("Failed to open guest disk: %s" % msg)
 
405
            if err == errno.EACCES and os.geteuid() != 0:
 
406
                return True # non root might not have access to block devices
 
407
            else:
 
408
                raise
 
409
 
 
410
        buf = os.read(fd, 512)
 
411
        os.close(fd)
 
412
        return (len(buf) == 512 and
 
413
                struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,))
 
414
 
 
415
    def detect_distro(self):
 
416
        """
 
417
        Attempt to detect the distro for the Installer's 'location'. If
 
418
        an error is encountered in the detection process (or if detection
 
419
        is not relevant for the Installer type), (None, None) is returned
 
420
 
 
421
        @returns: (distro type, distro variant) tuple
 
422
        """
 
423
        return (None, None)
 
424
 
 
425
    def guest_from_installer(self):
 
426
        """
 
427
        Return a L{Guest} instance wrapping the current installer.
 
428
 
 
429
        If all the appropriate values are present in the installer
 
430
        (conn, type, os_type, arch, machine), we have everything we need
 
431
        to determine what L{Guest} class is expected and what default values
 
432
        to pass it. This is a convenience method to save the API user from
 
433
        having to enter all these known details twice.
 
434
        """
 
435
 
 
436
        if not self.conn:
 
437
            raise ValueError(_("A connection must be specified."))
 
438
 
 
439
        guest, domain = CapabilitiesParser.guest_lookup(conn=self.conn,
 
440
                                                        caps=self._get_caps(),
 
441
                                                        os_type=self.os_type,
 
442
                                                        type=self.type,
 
443
                                                        arch=self.arch,
 
444
                                                        machine=self.machine)
 
445
 
 
446
        if self.os_type not in ["xen", "hvm"]:
 
447
            raise ValueError(_("No 'Guest' class for virtualization type '%s'"
 
448
                             % self.type))
 
449
 
 
450
        gobj = virtinst.Guest(installer=self, connection=self.conn)
 
451
        gobj.arch = guest.arch
 
452
        gobj.emulator = domain.emulator
 
453
        self.loader = domain.loader
 
454
 
 
455
        return gobj
 
456
 
 
457
# Back compat
 
458
Installer.get_install_xml = Installer.get_xml_config