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

« back to all changes in this revision

Viewing changes to .pc/0004-virt-install-Fix-CDROM-attach-for-windows-installs.patch/virtinst/Guest.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, os.path
23
 
import time
24
 
import re
25
 
import urlgrabber.progress as progress
26
 
import _util
27
 
import libvirt
28
 
import CapabilitiesParser
29
 
import VirtualGraphics
30
 
import support
31
 
from VirtualDevice import VirtualDevice
32
 
from VirtualDisk import VirtualDisk
33
 
from Clock import Clock
34
 
from Seclabel import Seclabel
35
 
 
36
 
import osdict
37
 
from virtinst import _virtinst as _
38
 
import logging
39
 
import signal
40
 
 
41
 
def _validate_cpuset(conn, val):
42
 
    if val is None or val == "":
43
 
        return
44
 
 
45
 
    if type(val) is not type("string") or len(val) == 0:
46
 
        raise ValueError, _("cpuset must be string")
47
 
    if re.match("^[0-9,-]*$", val) is None:
48
 
        raise ValueError, _("cpuset can only contain numeric, ',', or "
49
 
                            "'-' characters")
50
 
 
51
 
    pcpus = _util.get_phy_cpus(conn)
52
 
    for c in val.split(','):
53
 
        if c.find('-') != -1:
54
 
            (x, y) = c.split('-')
55
 
            if int(x) > int(y):
56
 
                raise ValueError, _("cpuset contains invalid format.")
57
 
            if int(x) >= pcpus or int(y) >= pcpus:
58
 
                raise ValueError, _("cpuset's pCPU numbers must be less "
59
 
                                    "than pCPUs.")
60
 
        else:
61
 
            if len(c) == 0:
62
 
                continue
63
 
 
64
 
            if int(c) >= pcpus:
65
 
                raise ValueError, _("cpuset's pCPU numbers must be less "
66
 
                                    "than pCPUs.")
67
 
    return
68
 
 
69
 
class Guest(object):
70
 
 
71
 
    # OS Dictionary static variables and methods
72
 
    _DEFAULTS = osdict.DEFAULTS
73
 
    _OS_TYPES = osdict.OS_TYPES
74
 
 
75
 
    def list_os_types():
76
 
        return osdict.sort_helper(Guest._OS_TYPES)
77
 
    list_os_types = staticmethod(list_os_types)
78
 
 
79
 
    def list_os_variants(type):
80
 
        return osdict.sort_helper(Guest._OS_TYPES[type]["variants"])
81
 
    list_os_variants = staticmethod(list_os_variants)
82
 
 
83
 
    def get_os_type_label(type):
84
 
        return Guest._OS_TYPES[type]["label"]
85
 
    get_os_type_label = staticmethod(get_os_type_label)
86
 
 
87
 
    def get_os_variant_label(type, variant):
88
 
        return Guest._OS_TYPES[type]["variants"][variant]["label"]
89
 
    get_os_variant_label = staticmethod(get_os_variant_label)
90
 
 
91
 
    def cpuset_str_to_tuple(conn, cpuset):
92
 
        _validate_cpuset(conn, cpuset)
93
 
        pinlist = [False] * _util.get_phy_cpus(conn)
94
 
 
95
 
        entries = cpuset.split(",")
96
 
        for e in entries:
97
 
            series = e.split("-", 1)
98
 
 
99
 
            if len(series) == 1:
100
 
                pinlist[int(series[0])] = True
101
 
                continue
102
 
 
103
 
            start = int(series[0])
104
 
            end = int(series[1])
105
 
 
106
 
            for i in range(start, end):
107
 
                pinlist[i] = True
108
 
 
109
 
        return tuple(pinlist)
110
 
    cpuset_str_to_tuple = staticmethod(cpuset_str_to_tuple)
111
 
 
112
 
    def __init__(self, type=None, connection=None, hypervisorURI=None,
113
 
                 installer=None):
114
 
 
115
 
        # Set up the connection, since it is fundamental for other init
116
 
        self.conn = connection
117
 
        if self.conn == None:
118
 
            logging.debug("No conn passed to Guest, opening URI '%s'" % \
119
 
                          hypervisorURI)
120
 
            self.conn = libvirt.open(hypervisorURI)
121
 
 
122
 
        if self.conn == None:
123
 
            raise RuntimeError, _("Unable to connect to hypervisor, aborting "
124
 
                                  "installation!")
125
 
 
126
 
        # We specifically ignore the 'type' parameter here, since
127
 
        # it has been replaced by installer.type, and child classes can
128
 
        # use it when creating a default installer.
129
 
        ignore = type
130
 
        self._installer = installer
131
 
        self._name = None
132
 
        self._uuid = None
133
 
        self._memory = None
134
 
        self._maxmemory = None
135
 
        self._vcpus = 1
136
 
        self._cpuset = None
137
 
        self._graphics_dev = None
138
 
        self._autostart = False
139
 
        self._clock = Clock(self.conn)
140
 
        self._seclabel = None
141
 
        self._description = None
142
 
        self.features = None
143
 
        self._replace = None
144
 
 
145
 
        self._os_type = None
146
 
        self._os_variant = None
147
 
        self._os_autodetect = False
148
 
 
149
 
        # DEPRECATED: Public device lists unaltered by install process
150
 
        self.disks = []
151
 
        self.nics = []
152
 
        self.sound_devs = []
153
 
        self.hostdevs = []
154
 
 
155
 
        # General device list. Only access through API calls (even internally)
156
 
        self._devices = []
157
 
 
158
 
        # Device list to use/alter during install process. Don't access
159
 
        # directly, use internal APIs
160
 
        self._install_devices = []
161
 
 
162
 
        # The libvirt virDomain object we 'Create'
163
 
        self.domain = None
164
 
        self._consolechild = None
165
 
 
166
 
        # Default disk target prefix ('hd' or 'xvd'). Set in subclass
167
 
        self.disknode = None
168
 
 
169
 
        # Default bus for disks (set in subclass)
170
 
        self._diskbus = None
171
 
 
172
 
        # Indicates that default devices have been assigned, so look for
173
 
        # the user to overwrite
174
 
        self._default_console_assigned = None
175
 
        self._default_input_assigned = None
176
 
 
177
 
        self._caps = CapabilitiesParser.parse(self.conn.getCapabilities())
178
 
 
179
 
 
180
 
    ######################
181
 
    # Property accessors #
182
 
    ######################
183
 
 
184
 
    def get_installer(self):
185
 
        return self._installer
186
 
    def set_installer(self, val):
187
 
        self._installer = val
188
 
    installer = property(get_installer, set_installer)
189
 
 
190
 
    def get_clock(self):
191
 
        return self._clock
192
 
    clock = property(get_clock)
193
 
 
194
 
    def get_seclabel(self):
195
 
        return self._seclabel
196
 
    def set_seclabel(self, val):
197
 
        if val and not isinstance(val, Seclabel):
198
 
            raise ValueError("'seclabel' must be a Seclabel() instance.")
199
 
 
200
 
        if val:
201
 
            # Check for validation purposes
202
 
            val.get_xml_config()
203
 
        self._seclabel = val
204
 
    seclabel = property(get_seclabel, set_seclabel)
205
 
 
206
 
    # Domain name of the guest
207
 
    def get_name(self):
208
 
        return self._name
209
 
    def set_name(self, val):
210
 
        _util.validate_name(_("Guest"), val)
211
 
 
212
 
        do_fail = False
213
 
        if self.replace != True:
214
 
            try:
215
 
                self.conn.lookupByName(val)
216
 
                do_fail = True
217
 
            except:
218
 
                # Name not found
219
 
                pass
220
 
 
221
 
        if do_fail:
222
 
            raise ValueError(_("Guest name '%s' is already in use.") % val)
223
 
 
224
 
        self._name = val
225
 
    name = property(get_name, set_name)
226
 
 
227
 
    # Memory allocated to the guest.  Should be given in MB
228
 
    def get_memory(self):
229
 
        return self._memory
230
 
    def set_memory(self, val):
231
 
        if (type(val) is not type(1) or val <= 0):
232
 
            raise ValueError, _("Memory value must be an integer greater "
233
 
                                "than 0")
234
 
        self._memory = val
235
 
        if self._maxmemory is None or self._maxmemory < val:
236
 
            self._maxmemory = val
237
 
    memory = property(get_memory, set_memory)
238
 
 
239
 
    # Memory allocated to the guest.  Should be given in MB
240
 
    def get_maxmemory(self):
241
 
        return self._maxmemory
242
 
    def set_maxmemory(self, val):
243
 
        if (type(val) is not type(1) or val <= 0):
244
 
            raise ValueError, _("Max Memory value must be an integer greater "
245
 
                                "than 0")
246
 
        self._maxmemory = val
247
 
    maxmemory = property(get_maxmemory, set_maxmemory)
248
 
 
249
 
    # UUID for the guest
250
 
    def get_uuid(self):
251
 
        return self._uuid
252
 
    def set_uuid(self, val):
253
 
        val = _util.validate_uuid(val)
254
 
        self._uuid = val
255
 
    uuid = property(get_uuid, set_uuid)
256
 
 
257
 
    # number of vcpus for the guest
258
 
    def get_vcpus(self):
259
 
        return self._vcpus
260
 
    def set_vcpus(self, val):
261
 
        maxvcpus = _util.get_max_vcpus(self.conn, self.type)
262
 
        if type(val) is not int or val < 1:
263
 
            raise ValueError, _("Number of vcpus must be a postive integer.")
264
 
        if val > maxvcpus:
265
 
            raise ValueError, _("Number of vcpus must be no greater than %d "
266
 
                                "for this vm type.") % maxvcpus
267
 
        self._vcpus = val
268
 
    vcpus = property(get_vcpus, set_vcpus)
269
 
 
270
 
    # set phy-cpus for the guest
271
 
    def get_cpuset(self):
272
 
        return self._cpuset
273
 
    def set_cpuset(self, val):
274
 
        if val is None or val == "":
275
 
            self._cpuset = None
276
 
            return
277
 
 
278
 
        _validate_cpuset(self.conn, val)
279
 
        self._cpuset = val
280
 
    cpuset = property(get_cpuset, set_cpuset)
281
 
 
282
 
    def get_graphics_dev(self):
283
 
        return self._graphics_dev
284
 
    def set_graphics_dev(self, val):
285
 
        self._graphics_dev = val
286
 
    graphics_dev = property(get_graphics_dev, set_graphics_dev)
287
 
 
288
 
    # GAH! - installer.os_type = "hvm" or "xen" (aka xen paravirt)
289
 
    #        guest.os_type     = "Solaris", "Windows", "Linux"
290
 
    # FIXME: We should really rename this property to something else,
291
 
    #        change it throughout the codebase for readability sake, but
292
 
    #        maintain back compat.
293
 
    def get_os_type(self):
294
 
        return self._os_type
295
 
    def set_os_type(self, val):
296
 
        if type(val) is not str:
297
 
            raise ValueError(_("OS type must be a string."))
298
 
        val = val.lower()
299
 
 
300
 
        if self._OS_TYPES.has_key(val):
301
 
            if self._os_type != val:
302
 
                # Invalidate variant, since it may not apply to the new os type
303
 
                self._os_type = val
304
 
                self._os_variant = None
305
 
        else:
306
 
            raise ValueError, _("OS type '%s' does not exist in our "
307
 
                                "dictionary") % val
308
 
 
309
 
        # Default may have changed with OS info
310
 
        self._set_default_input_dev()
311
 
    os_type = property(get_os_type, set_os_type)
312
 
 
313
 
    def get_os_variant(self):
314
 
        return self._os_variant
315
 
    def set_os_variant(self, val):
316
 
        if type(val) is not str:
317
 
            raise ValueError(_("OS variant must be a string."))
318
 
        val = val.lower()
319
 
 
320
 
        if self.os_type:
321
 
            if self._OS_TYPES[self.os_type]["variants"].has_key(val):
322
 
                self._os_variant = val
323
 
            else:
324
 
                raise ValueError, _("OS variant '%(var)s' does not exist in "
325
 
                                    "our dictionary for OS type '%(ty)s'" ) % \
326
 
                                    {'var' : val, 'ty' : self._os_type}
327
 
        else:
328
 
            found = False
329
 
            for ostype in self.list_os_types():
330
 
                if self._OS_TYPES[ostype]["variants"].has_key(val) and \
331
 
                   not self._OS_TYPES[ostype]["variants"][val].get("skip"):
332
 
                    logging.debug("Setting os type to '%s' for variant '%s'" %\
333
 
                                  (ostype, val))
334
 
                    self.os_type = ostype
335
 
                    self._os_variant = val
336
 
                    found = True
337
 
 
338
 
            if not found:
339
 
                raise ValueError, _("Unknown OS variant '%s'" % val)
340
 
 
341
 
        # Default may have changed with OS info
342
 
        self._set_default_input_dev()
343
 
    os_variant = property(get_os_variant, set_os_variant)
344
 
 
345
 
    def set_os_autodetect(self, val):
346
 
        self._os_autodetect = bool(val)
347
 
    def get_os_autodetect(self):
348
 
        return self._os_autodetect
349
 
    os_autodetect = property(get_os_autodetect, set_os_autodetect)
350
 
 
351
 
    # Get the current variants 'distro' tag: 'rhel', 'fedora', etc.
352
 
    def get_os_distro(self):
353
 
        return self._lookup_osdict_key("distro")
354
 
    os_distro = property(get_os_distro)
355
 
 
356
 
    def get_autostart(self):
357
 
        return self._autostart
358
 
    def set_autostart(self, val):
359
 
        self._autostart = bool(val)
360
 
    autostart = property(get_autostart, set_autostart,
361
 
                         doc="Have domain autostart when the host boots.")
362
 
 
363
 
    def _get_description(self):
364
 
        return self._description
365
 
    def _set_description(self, val):
366
 
        self._description = val
367
 
    description = property(_get_description, _set_description)
368
 
 
369
 
    def _get_replace(self):
370
 
        return self._replace
371
 
    def _set_replace(self, val):
372
 
        self._replace = bool(val)
373
 
    replace = property(_get_replace, _set_replace,
374
 
                       doc=_("Whether we should overwrite an existing guest "
375
 
                             "with the same name."))
376
 
 
377
 
    #########################
378
 
    # DEPRECATED PROPERTIES #
379
 
    #########################
380
 
 
381
 
    # Deprecated: Should set graphics_dev.keymap directly
382
 
    def get_keymap(self):
383
 
        if self._graphics_dev is None:
384
 
            return None
385
 
        return self._graphics_dev.keymap
386
 
    def set_keymap(self, val):
387
 
        if self._graphics_dev is not None:
388
 
            self._graphics_dev.keymap = val
389
 
    keymap = property(get_keymap, set_keymap)
390
 
 
391
 
    # Deprecated: Should set guest.graphics_dev = VirtualGraphics(...)
392
 
    def get_graphics(self):
393
 
        if self._graphics_dev is None:
394
 
            return { "enabled" : False }
395
 
        return { "enabled" : True, "type" : self._graphics_dev, \
396
 
                 "keymap"  : self._graphics_dev.keymap}
397
 
    def set_graphics(self, val):
398
 
 
399
 
        # val can be:
400
 
        #   a dictionary with keys:  enabled, type, port, keymap
401
 
        #   a tuple of the form   : (enabled, type, port, keymap)
402
 
        #                            last 2 optional
403
 
        #                         : "vnc", "sdl", or false
404
 
        port = None
405
 
        gtype = None
406
 
        enabled = False
407
 
        keymap = None
408
 
        gdev = None
409
 
        if type(val) == dict:
410
 
            if not val.has_key("enabled"):
411
 
                raise ValueError, _("Must specify whether graphics are enabled")
412
 
            enabled = val["enabled"]
413
 
            if val.has_key("type"):
414
 
                gtype = val["type"]
415
 
                if val.has_key("opts"):
416
 
                    port = val["opts"]
417
 
        elif type(val) == tuple:
418
 
            if len(val) >= 1:
419
 
                enabled = val[0]
420
 
            if len(val) >= 2:
421
 
                gtype = val[1]
422
 
            if len(val) >= 3:
423
 
                port = val[2]
424
 
            if len(val) >= 4:
425
 
                keymap = val[3]
426
 
        else:
427
 
            if val in ("vnc", "sdl"):
428
 
                gtype = val
429
 
                enabled = True
430
 
            else:
431
 
                enabled = val
432
 
 
433
 
        if enabled not in (True, False):
434
 
            raise ValueError, _("Graphics enabled must be True or False")
435
 
 
436
 
        if enabled:
437
 
            gdev = VirtualGraphics.VirtualGraphics(type=gtype)
438
 
            if port:
439
 
                gdev.port = port
440
 
            if keymap:
441
 
                gdev.keymap = keymap
442
 
        self._graphics_dev = gdev
443
 
 
444
 
    graphics = property(get_graphics, set_graphics)
445
 
 
446
 
    # Hypervisor name (qemu, xen, kvm, etc.)
447
 
    # Deprecated: should be pulled directly from the installer
448
 
    def get_type(self):
449
 
        return self._installer.type
450
 
    def set_type(self, val):
451
 
        self._installer.type = val
452
 
    type = property(get_type, set_type)
453
 
 
454
 
    # Deprecated: should be pulled directly from the installer
455
 
    def get_arch(self):
456
 
        return self.installer.arch
457
 
    def set_arch(self, val):
458
 
        self.installer.arch = val
459
 
    arch = property(get_arch, set_arch)
460
 
 
461
 
    # Deprecated: Should be called from the installer directly
462
 
    def get_location(self):
463
 
        return self._installer.location
464
 
    def set_location(self, val):
465
 
        self._installer.location = val
466
 
    location = property(get_location, set_location)
467
 
 
468
 
    # Deprecated: Should be called from the installer directly
469
 
    def get_scratchdir(self):
470
 
        return self._installer.scratchdir
471
 
    scratchdir = property(get_scratchdir)
472
 
 
473
 
    # Deprecated: Should be called from the installer directly
474
 
    def get_boot(self):
475
 
        return self._installer.boot
476
 
    def set_boot(self, val):
477
 
        self._installer.boot = val
478
 
    boot = property(get_boot, set_boot)
479
 
 
480
 
    # Deprecated: Should be called from the installer directly
481
 
    def get_extraargs(self):
482
 
        return self._installer.extraargs
483
 
    def set_extraargs(self, val):
484
 
        self._installer.extraargs = val
485
 
    extraargs = property(get_extraargs, set_extraargs)
486
 
 
487
 
    # Deprecated: Should set the installer values directly
488
 
    def get_cdrom(self):
489
 
        return self._installer.location
490
 
    def set_cdrom(self, val):
491
 
        self._installer.location = val
492
 
        self._installer.cdrom = True
493
 
    cdrom = property(get_cdrom, set_cdrom)
494
 
 
495
 
 
496
 
    ########################################
497
 
    # Device Add/Remove Public API methods #
498
 
    ########################################
499
 
 
500
 
    def _dev_build_list(self, devtype, devlist=None):
501
 
        if not devlist:
502
 
            devlist = self._devices
503
 
 
504
 
        newlist = []
505
 
        for i in devlist:
506
 
            if i.virtual_device_type == devtype:
507
 
                newlist.append(i)
508
 
        return newlist
509
 
 
510
 
    def add_device(self, dev):
511
 
        """
512
 
        Add the passed device to the guest's device list.
513
 
 
514
 
        @param dev: VirtualDevice instance to attach to guest
515
 
        """
516
 
        if not isinstance(dev, VirtualDevice):
517
 
            raise ValueError(_("Must pass a VirtualDevice instance."))
518
 
        devtype = dev.virtual_device_type
519
 
 
520
 
        # Handling for back compat default device behavior
521
 
        if ((devtype == VirtualDevice.VIRTUAL_DEV_CONSOLE or
522
 
             devtype == VirtualDevice.VIRTUAL_DEV_SERIAL) and
523
 
            self._default_console_assigned == True):
524
 
 
525
 
            rmdev = self.get_devices(VirtualDevice.VIRTUAL_DEV_CONSOLE)[0]
526
 
            self.remove_device(rmdev)
527
 
            self._default_console_assigned = False
528
 
 
529
 
        if (devtype == VirtualDevice.VIRTUAL_DEV_INPUT and
530
 
            self._default_input_assigned == True):
531
 
 
532
 
            rmdev = self.get_devices(VirtualDevice.VIRTUAL_DEV_INPUT)[0]
533
 
            self.remove_device(rmdev)
534
 
            self._default_input_assigned = False
535
 
 
536
 
        # Actually add the device
537
 
        if   devtype == VirtualDevice.VIRTUAL_DEV_DISK:
538
 
            self.disks.append(dev)
539
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_NET:
540
 
            self.nics.append(dev)
541
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_AUDIO:
542
 
            self.sound_devs.append(dev)
543
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_GRAPHICS:
544
 
            self._graphics_dev = dev
545
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_HOSTDEV:
546
 
            self.hostdevs.append(dev)
547
 
        else:
548
 
            self._devices.append(dev)
549
 
 
550
 
    def get_devices(self, devtype):
551
 
        """
552
 
        Return a list of devices of type 'devtype' that will installed on
553
 
        the guest.
554
 
 
555
 
        @param devtype: Device type to search for (one of
556
 
                        VirtualDevice.virtual_device_types)
557
 
        """
558
 
        if   devtype == VirtualDevice.VIRTUAL_DEV_DISK:
559
 
            return self.disks[:]
560
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_NET:
561
 
            return self.nics[:]
562
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_AUDIO:
563
 
            return self.sound_devs[:]
564
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_GRAPHICS:
565
 
            return self._graphics_dev and [self._graphics_dev] or []
566
 
        elif devtype == VirtualDevice.VIRTUAL_DEV_HOSTDEV:
567
 
            return self.hostdevs[:]
568
 
        else:
569
 
            return self._dev_build_list(devtype)
570
 
 
571
 
    def get_all_devices(self):
572
 
        """
573
 
        Return a list of all devices being installed with the guest
574
 
        """
575
 
        retlist = []
576
 
        for devtype in VirtualDevice.virtual_device_types:
577
 
            retlist.extend(self.get_devices(devtype))
578
 
        return retlist
579
 
 
580
 
    def remove_device(self, dev):
581
 
        """
582
 
        Remove the passed device from the guest's device list
583
 
 
584
 
        @param dev: VirtualDevice instance
585
 
        """
586
 
        found = False
587
 
        if dev == self._graphics_dev:
588
 
            self._graphics_dev = None
589
 
            found = True
590
 
 
591
 
        for devlist in [self.disks, self.nics, self.sound_devs, self.hostdevs,
592
 
                        self._devices]:
593
 
            if found:
594
 
                break
595
 
 
596
 
            if dev in devlist:
597
 
                devlist.remove(dev)
598
 
                found = True
599
 
                break
600
 
 
601
 
        if not found:
602
 
            raise ValueError(_("Did not find device %s") % str(dev))
603
 
 
604
 
        if (dev.virtual_device_type == VirtualDevice.VIRTUAL_DEV_INPUT and
605
 
            self._default_input_assigned == True):
606
 
            self._default_input_assigned = False
607
 
        if (dev.virtual_device_type in [VirtualDevice.VIRTUAL_DEV_CONSOLE,
608
 
                                        VirtualDevice.VIRTUAL_DEV_SERIAL] and
609
 
            self._default_console_assigned == True):
610
 
            self._default_console_assigned = False
611
 
 
612
 
 
613
 
    # Device fetching functions used internally during the install process.
614
 
    # These allow us to change dev defaults, add install media, etc. during
615
 
    # the install, but revert to a clean state if the install fails
616
 
    def _get_install_devs(self, devtype):
617
 
        return self._dev_build_list(devtype, self._install_devices)
618
 
 
619
 
    def _add_install_dev(self, dev):
620
 
        self._install_devices.append(dev)
621
 
 
622
 
    def _get_all_install_devs(self):
623
 
        retlist = []
624
 
        for devtype in VirtualDevice.virtual_device_types:
625
 
            retlist.extend(self._get_install_devs(devtype))
626
 
        return retlist
627
 
 
628
 
 
629
 
    ################################
630
 
    # Private xml building methods #
631
 
    ################################
632
 
 
633
 
    def _get_input_device(self):
634
 
        """ Return a tuple of the form (devtype, bus) for the desired
635
 
            input device. (Must be implemented in subclass) """
636
 
        raise NotImplementedError
637
 
 
638
 
    def _get_device_xml(self, install=True):
639
 
        # If install hasn't been prepared yet, make sure we use
640
 
        # the regular device list
641
 
        inst_devs = self._get_all_install_devs()
642
 
        reg_devs = self.get_all_devices()
643
 
        if len(inst_devs) >= len(reg_devs):
644
 
            devs = inst_devs[:]
645
 
        else:
646
 
            devs = reg_devs[:]
647
 
 
648
 
        def do_remove_media(d):
649
 
            # Keep cdrom around, but with no media attached,
650
 
            # But only if we are a distro that doesn't have a multi
651
 
            # stage install (aka not Windows)
652
 
            return (d.virtual_device_type == VirtualDevice.VIRTUAL_DEV_DISK and
653
 
                    d.device == VirtualDisk.DEVICE_CDROM
654
 
                    and d.transient
655
 
                    and not install and
656
 
                    not self.get_continue_inst())
657
 
 
658
 
        # Wrapper for building disk XML, handling transient CDROMs
659
 
        def get_dev_xml(dev):
660
 
            origpath = None
661
 
            try:
662
 
                if do_remove_media(dev):
663
 
                    origpath = dev.path
664
 
                    dev.path = None
665
 
 
666
 
                return dev.get_xml_config()
667
 
            finally:
668
 
                if origpath:
669
 
                    dev.path = origpath
670
 
 
671
 
        xml = ""
672
 
        # Build XML
673
 
        for dev in devs:
674
 
            xml = _util.xml_append(xml, get_dev_xml(dev))
675
 
 
676
 
        return xml
677
 
 
678
 
    def _get_features(self):
679
 
        """
680
 
        Determine the guest features, based on explicit settings in FEATURES
681
 
        and the OS_TYPE and OS_VARIANT. FEATURES takes precedence over the OS
682
 
        preferences
683
 
        """
684
 
        if self.features is None:
685
 
            return None
686
 
 
687
 
        # explicitly disabling apic and acpi will override OS_TYPES values
688
 
        features = dict(self.features)
689
 
        for f in ["acpi", "apic"]:
690
 
            val = self._lookup_osdict_key(f)
691
 
            if features.get(f) == None:
692
 
                features[f] = val
693
 
        return features
694
 
 
695
 
    def _get_features_xml(self):
696
 
        """
697
 
        Return features (pae, acpi, apic) xml
698
 
        """
699
 
        features = self._get_features()
700
 
        found_feature = False
701
 
 
702
 
        ret = "  <features>\n"
703
 
 
704
 
        if features:
705
 
            ret += "    "
706
 
            for k in sorted(features.keys()):
707
 
                v = features[k]
708
 
                if v:
709
 
                    ret += "<%s/>" % k
710
 
                    found_feature = True
711
 
            ret += "\n"
712
 
 
713
 
        if not found_feature:
714
 
            # Don't print empty feature block if no features added
715
 
            return ""
716
 
 
717
 
        return ret + "  </features>"
718
 
 
719
 
    def _get_cpu_xml(self):
720
 
        """
721
 
        Return <cpu> XML
722
 
        """
723
 
        # Just a stub for now
724
 
        return ""
725
 
 
726
 
    def _get_clock_xml(self):
727
 
        """
728
 
        Return <clock/> xml
729
 
        """
730
 
        return self.clock.get_xml_config()
731
 
 
732
 
    def _get_seclabel_xml(self):
733
 
        """
734
 
        Return <seclabel> XML
735
 
        """
736
 
        xml = ""
737
 
        if self.seclabel:
738
 
            xml = self.seclabel.get_xml_config()
739
 
 
740
 
        return xml
741
 
 
742
 
    def _get_osblob(self, install):
743
 
        """
744
 
        Return os, features, and clock xml (Implemented in subclass)
745
 
        """
746
 
        xml = ""
747
 
 
748
 
        osxml = self.installer.get_install_xml(self, install)
749
 
        if not osxml:
750
 
            return None
751
 
 
752
 
        xml = _util.xml_append(xml,
753
 
                               self.installer.get_install_xml(self, install))
754
 
        return xml
755
 
 
756
 
    ############################
757
 
    # Install Helper functions #
758
 
    ############################
759
 
 
760
 
    def _prepare_install(self, meter):
761
 
        # Initialize install device list
762
 
        self._install_devices = self.get_all_devices()[:]
763
 
 
764
 
        # Set regular device defaults
765
 
        self.set_defaults()
766
 
 
767
 
        self._installer.prepare(guest = self,
768
 
                                meter = meter)
769
 
        if self._installer.install_disk is not None:
770
 
            self._add_install_dev(self._installer.install_disk)
771
 
 
772
 
        # Run 'set_defaults' after install prep, since some installers
773
 
        # (ImageInstaller) alter the device list.
774
 
        self._set_defaults(self._get_install_devs)
775
 
 
776
 
    def _cleanup_install(self):
777
 
        # Empty install dev list
778
 
        self._install_devices = []
779
 
 
780
 
        self._installer.cleanup()
781
 
 
782
 
    def _create_devices(self, progresscb):
783
 
        """
784
 
        Ensure that devices are setup
785
 
        """
786
 
        for dev in self._get_all_install_devs():
787
 
            dev.setup_dev(self.conn, progresscb)
788
 
 
789
 
    ##############
790
 
    # Public API #
791
 
    ##############
792
 
 
793
 
    def get_config_xml(self, install = True, disk_boot = False):
794
 
        """
795
 
        Return the full Guest xml configuration.
796
 
 
797
 
        @param install: Whether we want the 'OS install' configuration or
798
 
                        the 'post-install' configuration. (Some Installers,
799
 
                        like the LiveCDInstaller may not have an 'install'
800
 
                        config.)
801
 
        @type install: C{bool}
802
 
        @param disk_boot: Whether we should boot off the harddisk, regardless
803
 
                          of our position in the install process (this is
804
 
                          used for 2 stage installs, where the second stage
805
 
                          boots off the disk. You probably don't need to touch
806
 
                          this.)
807
 
        @type disk_boot: C{bool}
808
 
        """
809
 
 
810
 
        # Set device defaults so we can validly generate XML
811
 
        self._set_defaults(self.get_devices)
812
 
 
813
 
        if install:
814
 
            action = "destroy"
815
 
        else:
816
 
            action = "restart"
817
 
 
818
 
        osblob_install = install
819
 
        if disk_boot:
820
 
            osblob_install = False
821
 
 
822
 
        osblob = self._get_osblob(osblob_install)
823
 
        if not osblob:
824
 
            # This means there is no 'install' phase, so just return
825
 
            return None
826
 
 
827
 
        cpuset = ""
828
 
        if self.cpuset is not None:
829
 
            cpuset = " cpuset='" + self.cpuset + "'"
830
 
 
831
 
        desc_xml = ""
832
 
        if self.description is not None:
833
 
            desc = str(self.description)
834
 
            desc_xml = ("  <description>%s</description>" %
835
 
                        _util.xml_escape(desc))
836
 
 
837
 
        xml = ""
838
 
        add = lambda x: _util.xml_append(xml, x)
839
 
 
840
 
        xml = add("<domain type='%s'>" % self.type)
841
 
        xml = add("  <name>%s</name>" % self.name)
842
 
        xml = add("  <currentMemory>%s</currentMemory>" % (self.memory * 1024))
843
 
        xml = add("  <memory>%s</memory>" % (self.maxmemory * 1024))
844
 
        xml = add("  <uuid>%s</uuid>" % self.uuid)
845
 
        xml = add(desc_xml)
846
 
        xml = add("  %s" % osblob)
847
 
        xml = add(self._get_features_xml())
848
 
        xml = add(self._get_cpu_xml())
849
 
        xml = add(self._get_clock_xml())
850
 
        xml = add("  <on_poweroff>destroy</on_poweroff>")
851
 
        xml = add("  <on_reboot>%s</on_reboot>" % action)
852
 
        xml = add("  <on_crash>%s</on_crash>" % action)
853
 
        xml = add("  <vcpu%s>%d</vcpu>" % (cpuset, self.vcpus))
854
 
        xml = add("  <devices>")
855
 
        xml = add(self._get_device_xml(install))
856
 
        xml = add("  </devices>")
857
 
        xml = add(self._get_seclabel_xml())
858
 
        xml = add("</domain>\n")
859
 
 
860
 
        return xml
861
 
 
862
 
    def post_install_check(self):
863
 
        """
864
 
        Back compat mapping to installer post_install_check
865
 
        """
866
 
        return self.installer.post_install_check(self)
867
 
 
868
 
    def get_continue_inst(self):
869
 
        """
870
 
        Return True if this guest requires a call to 'continue_install',
871
 
        which means the OS requires a 2 stage install (windows)
872
 
        """
873
 
        val = self._lookup_osdict_key("continue")
874
 
        if not val:
875
 
            val = False
876
 
 
877
 
        if val == True:
878
 
            # If we are doing an 'import' or 'liveCD' install, there is
879
 
            # no true install process, so continue install has no meaning
880
 
            if not self.get_config_xml(install=True):
881
 
                val = False
882
 
        return val
883
 
 
884
 
    def validate_parms(self):
885
 
        """
886
 
        Do some pre-install domain validation
887
 
        """
888
 
        if self.domain is not None:
889
 
            raise RuntimeError, _("Domain has already been started!")
890
 
 
891
 
        if self.name is None or self.memory is None:
892
 
            raise RuntimeError(_("Name and memory must be specified for "
893
 
                                 "all guests!"))
894
 
 
895
 
        if _util.vm_uuid_collision(self.conn, self.uuid):
896
 
            raise RuntimeError(_("The UUID you entered is already in "
897
 
                                 "use by another guest!"))
898
 
 
899
 
    def connect_console(self, consolecb, wait=True):
900
 
        """
901
 
        Launched the passed console callback for the already defined
902
 
        domain. If domain isn't running, return an error.
903
 
        """
904
 
        (self.domain,
905
 
         self._consolechild) = self._wait_and_connect_console(consolecb)
906
 
 
907
 
        # If we connected the console, wait for it to finish
908
 
        self._waitpid_console(self._consolechild, wait)
909
 
 
910
 
    def terminate_console(self):
911
 
        """
912
 
        Kill guest console if it is open (and actually exists), otherwise
913
 
        do nothing
914
 
        """
915
 
        if self._consolechild:
916
 
            try:
917
 
                os.kill(self._consolechild, signal.SIGKILL)
918
 
            except:
919
 
                pass
920
 
 
921
 
    def domain_is_shutdown(self):
922
 
        """
923
 
        Return True if the created domain object is shutdown
924
 
        """
925
 
        dom = self.domain
926
 
        if not dom:
927
 
            return False
928
 
 
929
 
        dominfo = dom.info()
930
 
 
931
 
        state    = dominfo[0]
932
 
        cpu_time = dominfo[4]
933
 
 
934
 
        if state == libvirt.VIR_DOMAIN_SHUTOFF:
935
 
            return True
936
 
 
937
 
        # If 'wait' was specified, the dom object we have was looked up
938
 
        # before initially shutting down, which seems to bogus up the
939
 
        # info data (all 0's). So, if it is bogus, assume the domain is
940
 
        # shutdown. We will catch the error later.
941
 
        return state == libvirt.VIR_DOMAIN_NOSTATE and cpu_time == 0
942
 
 
943
 
    def domain_is_crashed(self):
944
 
        """
945
 
        Return True if the created domain object is in a crashed state
946
 
        """
947
 
        if not self.domain:
948
 
            return False
949
 
 
950
 
        dominfo = self.domain.info()
951
 
        state = dominfo[0]
952
 
 
953
 
        return state == libvirt.VIR_DOMAIN_CRASHED
954
 
 
955
 
    ##########################
956
 
    # Actual install methods #
957
 
    ##########################
958
 
 
959
 
    def start_install(self, consolecb=None, meter=None, removeOld=None,
960
 
                      wait=True):
961
 
        """
962
 
        Begin the guest install (stage1).
963
 
        """
964
 
        if removeOld == None:
965
 
            removeOld = self.replace
966
 
 
967
 
        self.validate_parms()
968
 
        self._consolechild = None
969
 
 
970
 
        self._prepare_install(meter)
971
 
        try:
972
 
            return self._do_install(consolecb, meter, removeOld, wait)
973
 
        finally:
974
 
            self._cleanup_install()
975
 
 
976
 
    def continue_install(self, consolecb=None, meter=None, wait=True):
977
 
        """
978
 
        Continue with stage 2 of a guest install. Only required for
979
 
        guests which have the 'continue' flag set (accessed via
980
 
        get_continue_inst)
981
 
        """
982
 
        return self._create_guest(consolecb, meter, wait,
983
 
                                  _("Starting domain..."), "continue",
984
 
                                  disk_boot=True,
985
 
                                  first_create=False)
986
 
 
987
 
    def _create_guest(self, consolecb, meter, wait,
988
 
                      meter_label, log_label,
989
 
                      disk_boot=False,
990
 
                      first_create=True):
991
 
        """
992
 
        Actually do the XML logging, guest defining/creating, console
993
 
        launching and waiting
994
 
        """
995
 
        if meter == None:
996
 
            meter = progress.BaseMeter()
997
 
 
998
 
        start_xml = self.get_config_xml(install=True, disk_boot=disk_boot)
999
 
        final_xml = self.get_config_xml(install=False)
1000
 
        logging.debug("Generated %s XML: %s" %
1001
 
                      (log_label,
1002
 
                      (start_xml and ("\n" + start_xml) or "None required")))
1003
 
 
1004
 
        if start_xml:
1005
 
            meter.start(size=None, text=meter_label)
1006
 
 
1007
 
            if first_create:
1008
 
                dom = self.conn.createLinux(start_xml, 0)
1009
 
            else:
1010
 
                dom = self.conn.defineXML(start_xml)
1011
 
                dom.create()
1012
 
 
1013
 
            self.domain = dom
1014
 
            meter.end(0)
1015
 
 
1016
 
            logging.debug("Started guest, looking to see if it is running")
1017
 
            (self.domain,
1018
 
             self._consolechild) = self._wait_and_connect_console(consolecb)
1019
 
 
1020
 
        logging.debug("Generated boot XML: \n%s" % final_xml)
1021
 
        self.domain = self.conn.defineXML(final_xml)
1022
 
 
1023
 
        # if we connected the console, wait for it to finish
1024
 
        self._waitpid_console(self._consolechild, wait)
1025
 
 
1026
 
        return self.conn.lookupByName(self.name)
1027
 
 
1028
 
    def _do_install(self, consolecb, meter, removeOld=False, wait=True):
1029
 
        # Remove existing VM if requested
1030
 
        self._replace_original_vm(removeOld)
1031
 
 
1032
 
        # Create devices if required (disk images, etc.)
1033
 
        self._create_devices(meter)
1034
 
 
1035
 
        self.domain = self._create_guest(consolecb, meter, wait,
1036
 
                                         _("Creating domain..."),
1037
 
                                         "install")
1038
 
 
1039
 
        # Set domain autostart flag if requested
1040
 
        self._flag_autostart()
1041
 
 
1042
 
        return self.domain
1043
 
 
1044
 
    def _wait_and_connect_console(self, consolecb):
1045
 
        """
1046
 
        Wait for domain to appear and be running, then connect to
1047
 
        the console if necessary
1048
 
        """
1049
 
        child = None
1050
 
        dom = _wait_for_domain(self.conn, self.name)
1051
 
 
1052
 
        if dom is None:
1053
 
            raise RuntimeError(_("Domain has not existed.  You should be "
1054
 
                                 "able to find more information in the logs"))
1055
 
        elif dom.ID() == -1:
1056
 
            raise RuntimeError(_("Domain has not run yet.  You should be "
1057
 
                                 "able to find more information in the logs"))
1058
 
 
1059
 
        if consolecb:
1060
 
            logging.debug("Launching console callback")
1061
 
            child = consolecb(dom)
1062
 
 
1063
 
        return dom, child
1064
 
 
1065
 
    def _waitpid_console(self, console_child, do_wait):
1066
 
        """
1067
 
        Wait for console to close if it was launched
1068
 
        """
1069
 
        if not console_child or not do_wait:
1070
 
            return
1071
 
 
1072
 
        try:
1073
 
            os.waitpid(console_child, 0)
1074
 
        except OSError, (err_no, msg):
1075
 
            logging.debug("waitpid: %s: %s" % (err_no, msg))
1076
 
 
1077
 
        # ensure there's time for the domain to finish destroying if the
1078
 
        # install has finished or the guest crashed
1079
 
        time.sleep(1)
1080
 
 
1081
 
    def _replace_original_vm(self, removeOld):
1082
 
        """
1083
 
        Remove the existing VM with the same name if requested, or error
1084
 
        if there is a collision.
1085
 
        """
1086
 
        vm = None
1087
 
        try:
1088
 
            vm = self.conn.lookupByName(self.name)
1089
 
        except libvirt.libvirtError:
1090
 
            pass
1091
 
 
1092
 
        if vm is None:
1093
 
            return
1094
 
 
1095
 
        if not removeOld:
1096
 
            raise RuntimeError(_("Domain named %s already exists!") %
1097
 
                               self.name)
1098
 
 
1099
 
        try:
1100
 
            if vm.ID() != -1:
1101
 
                logging.info("Destroying image %s" % self.name)
1102
 
                vm.destroy()
1103
 
 
1104
 
            logging.info("Removing old definition for image %s" % self.name)
1105
 
            vm.undefine()
1106
 
        except libvirt.libvirtError, e:
1107
 
            raise RuntimeError(_("Could not remove old vm '%s': %s") %
1108
 
                               (self.name, str(e)))
1109
 
 
1110
 
 
1111
 
    def _flag_autostart(self):
1112
 
        """
1113
 
        Set the autostart flag for self.domain if the user requested it
1114
 
        """
1115
 
        if not self.autostart:
1116
 
            return
1117
 
 
1118
 
        try:
1119
 
            self.domain.setAutostart(True)
1120
 
        except libvirt.libvirtError, e:
1121
 
            if support.is_error_nosupport(e):
1122
 
                logging.warn("Could not set autostart flag: libvirt "
1123
 
                             "connection does not support autostart.")
1124
 
            else:
1125
 
                raise e
1126
 
 
1127
 
 
1128
 
    ###################
1129
 
    # Device defaults #
1130
 
    ###################
1131
 
 
1132
 
    def _set_default_input_dev(self):
1133
 
        # This is called at init time, but also whenever the OS changes,
1134
 
        # since the input dev maybe be dependent on OS
1135
 
        if self._default_input_assigned == False:
1136
 
            return
1137
 
 
1138
 
        for d in self.get_devices(VirtualDevice.VIRTUAL_DEV_INPUT):
1139
 
            self.remove_device(d)
1140
 
 
1141
 
        # Add default input device
1142
 
        self._default_input_assigned = False
1143
 
        self.add_device(self._get_input_device())
1144
 
        self._default_input_assigned = True
1145
 
 
1146
 
    def set_defaults(self):
1147
 
        """
1148
 
        Public function to set guest defaults. Things like preferred
1149
 
        disk bus (unless one is specified). This will be called by the
1150
 
        install process, but can be called earlier if needed
1151
 
        """
1152
 
        self._set_defaults(self.get_devices)
1153
 
 
1154
 
    def _set_defaults(self, devlist_func):
1155
 
        soundtype = VirtualDevice.VIRTUAL_DEV_AUDIO
1156
 
        videotype = VirtualDevice.VIRTUAL_DEV_VIDEO
1157
 
 
1158
 
        # Generate disk targets, and set preferred disk bus
1159
 
        used_targets = []
1160
 
        for disk in devlist_func(VirtualDevice.VIRTUAL_DEV_DISK):
1161
 
            if not disk.bus:
1162
 
                if disk.device == disk.DEVICE_FLOPPY:
1163
 
                    disk.bus = "fdc"
1164
 
                else:
1165
 
                    disk.bus = self._diskbus
1166
 
            used_targets.append(disk.generate_target(used_targets))
1167
 
 
1168
 
        # Set sound device model
1169
 
        sound_model  = self._lookup_device_param(soundtype, "model")
1170
 
        for sound in devlist_func(soundtype):
1171
 
            if sound.model == sound.MODEL_DEFAULT:
1172
 
                sound.model = sound_model
1173
 
 
1174
 
        # Set video device model
1175
 
        video_model  = self._lookup_device_param(videotype, "model_type")
1176
 
        for video in devlist_func(videotype):
1177
 
            if video.model_type == video.MODEL_DEFAULT:
1178
 
                video.model_type = video_model
1179
 
 
1180
 
        # Generate UUID
1181
 
        if self.uuid is None:
1182
 
            while 1:
1183
 
                self.uuid = _util.uuidToString(_util.randomUUID())
1184
 
                if _util.vm_uuid_collision(self.conn, self.uuid):
1185
 
                    continue
1186
 
                break
1187
 
 
1188
 
 
1189
 
    ###################################
1190
 
    # Guest Dictionary Helper methods #
1191
 
    ###################################
1192
 
 
1193
 
    def _lookup_osdict_key(self, key):
1194
 
        """
1195
 
        Using self.os_type and self.os_variant to find key in OSTYPES
1196
 
        @returns: dict value, or None if os_type/variant wasn't set
1197
 
        """
1198
 
        return osdict.lookup_osdict_key(self.conn, self.type, self.os_type,
1199
 
                                        self.os_variant, key)
1200
 
 
1201
 
    def _lookup_device_param(self, device_key, param):
1202
 
        """
1203
 
        Check the OS dictionary for the prefered device setting for passed
1204
 
        device type and param (bus, model, etc.)
1205
 
        """
1206
 
        return osdict.lookup_device_param(self.conn, self.type, self.os_type,
1207
 
                                          self.os_variant, device_key, param)
1208
 
 
1209
 
 
1210
 
def _wait_for_domain(conn, name):
1211
 
    # sleep in .25 second increments until either a) we get running
1212
 
    # domain ID or b) it's been 5 seconds.  this is so that
1213
 
    # we can try to gracefully handle domain restarting failures
1214
 
    dom = None
1215
 
    for ignore in range(1, int(5 / .25)): # 5 seconds, .25 second sleeps
1216
 
        try:
1217
 
            dom = conn.lookupByName(name)
1218
 
            if dom and dom.ID() != -1:
1219
 
                break
1220
 
        except libvirt.libvirtError, e:
1221
 
            logging.debug("No guest running yet: " + str(e))
1222
 
            dom = None
1223
 
        time.sleep(0.25)
1224
 
 
1225
 
    return dom
1226
 
 
1227
 
# Back compat class to avoid ABI break
1228
 
XenGuest = Guest