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

« back to all changes in this revision

Viewing changes to virt-install

  • 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:
21
21
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22
22
# MA 02110-1301 USA.
23
23
 
24
 
import os, sys
 
24
import os
 
25
import sys
25
26
import time
26
27
import errno
27
28
import re
35
36
import virtinst
36
37
import virtinst.CapabilitiesParser
37
38
import virtinst.cli as cli
 
39
import virtinst.util as util
 
40
import virtinst._util as _util
38
41
from virtinst.VirtualCharDevice import VirtualCharDevice
39
42
from virtinst.VirtualDevice import VirtualDevice
40
 
from virtinst.cli import fail
 
43
from virtinst.cli import fail, print_stdout, print_stderr
41
44
 
42
45
cli.setupGettext()
43
46
 
44
47
DEFAULT_POOL_PATH = "/var/lib/libvirt/images"
45
48
DEFAULT_POOL_NAME = "default"
46
49
 
47
 
def partition(string, sep):
48
 
    if not string:
49
 
        return (None, None, None)
50
 
 
51
 
    if string.count(sep):
52
 
        splitres = string.split(sep, 1)
53
 
        ret = (splitres[0], sep, splitres[1])
54
 
    else:
55
 
        ret = (string, None, None)
56
 
    return ret
 
50
install_methods = "--location URL, --cdrom CD/ISO, --pxe, --import, --boot hd|cdrom|..."
 
51
install_missing = (_("An install method must be specified\n(%(methods)s)") %
 
52
                   {"methods" : install_methods })
 
53
disk_missing = _("--disk storage must be specified (override with --nodisks)")
 
54
 
 
55
def install_specified(location, cdpath, pxe, import_install):
 
56
    return bool(pxe or cdpath or location or import_install)
 
57
 
 
58
def cdrom_specified(guest, diskopts=None):
 
59
    for disk in guest.disks:
 
60
        if disk.device == virtinst.VirtualDisk.DEVICE_CDROM:
 
61
            return True
 
62
 
 
63
    # Probably haven't set up disks yet
 
64
    if not guest.disks and diskopts:
 
65
        for opts in diskopts:
 
66
            if opts.count("device=cdrom"):
 
67
                return True
 
68
 
 
69
    return False
 
70
 
 
71
def storage_specified(files, disks, nodisks):
 
72
    return bool(files or disks or nodisks)
57
73
 
58
74
def build_default_pool(guest):
59
75
 
81
97
        raise RuntimeError(_("Couldn't create default storage pool '%s': %s") %
82
98
                             (DEFAULT_POOL_PATH, str(e)))
83
99
 
84
 
def parse_char_option(guest, char_type, optstring):
 
100
def supports_pxe(guest):
 
101
    """
 
102
    Return False if we are pretty sure the config doesn't support PXE
 
103
    """
 
104
    for nic in guest.get_devices("interface"):
 
105
        if nic.type == nic.TYPE_USER:
 
106
            continue
 
107
        if nic.type != nic.TYPE_VIRTUAL:
 
108
            return True
 
109
 
 
110
        try:
 
111
            xml = nic.conn.networkLookupByName(nic.network).XMLDesc(0)
 
112
            if util.get_xml_path(xml, "/network/ip/dhcp/bootp/@file"):
 
113
                return True
 
114
 
 
115
            forward = util.get_xml_path(xml, "/network/forward/@mode")
 
116
            if forward and forward != "nat":
 
117
                return True
 
118
        except:
 
119
            _util.log_exception("Error checking if PXE supported")
 
120
            return True
 
121
 
 
122
    return False
 
123
 
 
124
def parse_boot_option(guest, optstring):
 
125
    """
 
126
    Helper to parse --boot string
 
127
    """
 
128
    ignore, opts = cli.parse_optstr(optstring)
 
129
    optlist = map(lambda x: x[0], cli.parse_optstr_tuples(optstring))
 
130
    menu = None
 
131
 
 
132
    def set_param(paramname, dictname, val=None):
 
133
        val = cli.get_opt_param(opts, dictname, val)
 
134
        if val == None:
 
135
            return
 
136
 
 
137
        setattr(guest.installer.bootconfig, paramname, val)
 
138
 
 
139
    # Convert menu= value
 
140
    if "menu" in opts:
 
141
        menustr = opts["menu"]
 
142
        menu = None
 
143
 
 
144
        if menustr.lower() == "on":
 
145
            menu = True
 
146
        elif menustr.lower() == "off":
 
147
            menu = False
 
148
        else:
 
149
            menu = cli.yes_or_no_convert(menustr)
 
150
 
 
151
        if menu == None:
 
152
            fail(_("--boot menu must be 'on' or 'off'"))
 
153
 
 
154
    set_param("enable_bootmenu", "menu", menu)
 
155
    set_param("kernel", "kernel")
 
156
    set_param("initrd", "initrd")
 
157
    set_param("kernel_args", ["kernel_args", "extra_args"])
 
158
 
 
159
    # Build boot order
 
160
    if opts:
 
161
        boot_order = []
 
162
        for boot_dev in optlist:
 
163
            if not boot_dev in guest.installer.bootconfig.boot_devices:
 
164
                continue
 
165
 
 
166
            del(opts[boot_dev])
 
167
            if boot_dev not in boot_order:
 
168
                boot_order.append(boot_dev)
 
169
 
 
170
        guest.installer.bootconfig.bootorder = boot_order
 
171
 
 
172
    if opts:
 
173
        raise ValueError(_("Unknown options %s") % opts.keys())
 
174
 
 
175
 
 
176
def parse_char_option(guest, dev_type, optstring):
85
177
    """
86
178
    Helper to parse --serial/--parallel options
87
179
    """
88
180
    # Peel the char type off the front
89
 
    dev_type, ignore, optstring = partition(optstring, ",")
90
 
 
91
 
    opts = cli.parse_optstr(optstring)
92
 
 
93
 
    dev = VirtualCharDevice.get_dev_instance(guest.conn, char_type, dev_type)
 
181
    char_type, opts = cli.parse_optstr(optstring, remove_first=True)
 
182
    dev = VirtualCharDevice.get_dev_instance(guest.conn, dev_type, char_type)
94
183
 
95
184
    def set_param(paramname, dictname, val=None):
96
 
        if not val:
97
 
            if opts.has_key(dictname):
98
 
                val = opts[dictname]
99
 
                del(opts[dictname])
100
 
            else:
101
 
                return
 
185
        val = cli.get_opt_param(opts, dictname, val)
 
186
        if val == None:
 
187
            return
102
188
 
103
 
        if not hasattr(dev, paramname):
 
189
        if not dev.supports_property(paramname):
104
190
            raise ValueError(_("%(chartype)s type %(devtype)s does not "
105
191
                                "support '%(optname)s' option.") %
106
 
                                {"chartype" : char_type, "devtype": dev_type,
 
192
                                {"devtype" : dev_type, "chartype": char_type,
107
193
                                 "optname" : dictname} )
108
194
        setattr(dev, paramname, val)
109
195
 
110
196
    def parse_host(key):
111
 
        host, ignore, port = partition(opts.get(key), ":")
112
 
        if opts.has_key(key):
 
197
        host, ignore, port = cli.partition(opts.get(key), ":")
 
198
        if key in opts:
113
199
            del(opts[key])
114
200
 
115
201
        return host, port
116
202
 
117
203
    host, port = parse_host("host")
118
204
    bind_host, bind_port = parse_host("bind_host")
 
205
    target_addr, target_port = parse_host("target_address")
119
206
 
120
207
    set_param("source_path", "path")
121
208
    set_param("source_mode", "mode")
124
211
    set_param("source_port", "host", port)
125
212
    set_param("bind_host", "bind_host", bind_host)
126
213
    set_param("bind_port", "bind_host", bind_port)
 
214
    set_param("target_name", "name")
 
215
    set_param("target_type", "target_type")
 
216
    set_param("target_address", "target_address", target_addr)
 
217
    set_param("target_port", "target_address", target_port)
127
218
 
128
 
    # If extra parameters, then user passed some garbage param
129
219
    if opts:
130
 
        raise ValueError(_("Unknown option(s) %s") % opts.keys())
 
220
        raise ValueError(_("Unknown options %s") % opts.keys())
131
221
 
132
222
    # Try to generate dev XML to perform upfront validation
133
223
    dev.get_xml_config()
142
232
            guest.add_device(dev)
143
233
        except Exception, e:
144
234
            fail(_("Error in %(chartype)s device parameters: %(err)s") %
145
 
                 {"chartype": char_type, "err": str(e) })
 
235
                 {"chartype": char_type, "err": str(e)})
146
236
 
147
237
def parse_watchdog(guest, optstring):
148
238
    # Peel the model type off the front
149
 
    model, ignore, optstring = partition(optstring, ",")
150
 
    opts = cli.parse_optstr(optstring)
 
239
    model, opts = cli.parse_optstr(optstring, remove_first=True)
151
240
    dev = virtinst.VirtualWatchdog(guest.conn)
152
241
 
153
242
    def set_param(paramname, dictname, val=None):
154
 
        if not val:
155
 
            if opts.has_key(dictname):
156
 
                val = opts[dictname]
157
 
                del(opts[dictname])
158
 
            else:
159
 
                return
 
243
        val = cli.get_opt_param(opts, dictname, val)
 
244
        if val == None:
 
245
            return
160
246
 
161
247
        setattr(dev, paramname, val)
162
248
 
163
249
    set_param("model", "model", model)
164
250
    set_param("action", "action")
165
251
 
166
 
    # If extra parameters, then user passed some garbage param
167
252
    if opts:
168
 
        raise ValueError(_("Unknown option(s) %s") % opts.keys())
 
253
        raise ValueError(_("Unknown options %s") % opts.keys())
169
254
 
170
255
    return dev
171
256
 
184
269
        return
185
270
 
186
271
    # Parse security opts
187
 
    opts = cli.parse_optstr(secopts)
188
 
    secmodel = virtinst.Seclabel(guest.conn)
189
 
 
190
 
    def get_and_clear(dictname):
191
 
        val = None
192
 
        if opts.has_key(dictname):
193
 
            val = opts[dictname]
194
 
            del(opts[dictname])
195
 
        return val
196
 
 
197
 
    mode = get_and_clear("type")
198
 
    label = get_and_clear("label")
 
272
    ignore, opts = cli.parse_optstr(secopts)
 
273
    arglist = secopts.split(",")
 
274
    secmodel = guest.seclabel
 
275
 
 
276
    # Beware, adding boolean options here could upset label comma handling
 
277
    mode = cli.get_opt_param(opts, "type")
 
278
    label = cli.get_opt_param(opts, "label")
 
279
 
 
280
    # Try to fix up label if it contained commas
 
281
    if label:
 
282
        tmparglist = arglist[:]
 
283
        for idx in range(len(tmparglist)):
 
284
            arg = tmparglist[idx]
 
285
            if not arg.split("=")[0] == "label":
 
286
                continue
 
287
 
 
288
            for arg in tmparglist[idx + 1:]:
 
289
                if arg.count("="):
 
290
                    break
 
291
 
 
292
                if arg:
 
293
                    label += "," + arg
 
294
                    del(opts[arg])
 
295
 
 
296
            break
199
297
 
200
298
    if label:
201
299
        secmodel.label = label
204
302
    if mode:
205
303
        secmodel.type = mode
206
304
 
207
 
    # If extra parameters, then user passed some garbage param
208
305
    if opts:
209
 
        raise ValueError(_("Unknown option(s) %s") % opts.keys())
 
306
        raise ValueError(_("Unknown options %s") % opts.keys())
210
307
 
 
308
    # Run for validation purposes
211
309
    secmodel.get_xml_config()
212
 
    guest.seclabel = secmodel
213
310
 
214
311
def parse_disk_option(guest, path, size):
215
312
    """helper to properly parse --disk options"""
220
317
    shared = False
221
318
    sparse = True
222
319
    option_whitelist = ["perms", "cache", "bus", "device", "size", "sparse",
223
 
                        "format"]
 
320
                        "format", "driver_name", "driver_type"]
224
321
 
225
322
    # Strip media type
226
 
    path, ignore, optstr = partition(path, ",")
 
323
    path, ignore, optstr = cli.partition(path, ",")
227
324
    path_type = None
228
325
    if path.startswith("path="):
229
326
        path_type = "path="
239
336
        path_type = "path="
240
337
 
241
338
    # Parse out comma separated options
242
 
    opts = cli.parse_optstr(optstr)
 
339
    ignore, opts = cli.parse_optstr(optstr)
243
340
    for opt_type, opt_val in opts.items():
244
341
        if opt_type not in option_whitelist:
245
342
            fail(_("Unknown --disk option '%s'.") % opt_type)
249
346
                ro = True
250
347
            elif opt_val == "sh":
251
348
                shared = True
 
349
            elif opt_val == "rw":
 
350
                pass  # It's default. Nothing to do.
252
351
            else:
253
352
                fail(_("Unknown '%s' value '%s'" % (opt_type, opt_val)))
254
353
        elif opt_type == "size":
269
368
    bus     = opts.get("bus")
270
369
    cache   = opts.get("cache")
271
370
    fmt     = opts.get("format")
 
371
    drvname = opts.get("driver_name")
 
372
    drvtype = opts.get("driver_type")
272
373
 
273
374
    # We return (path, (poolname, volname), volinst, device, bus, readonly,
274
375
    #            shared)
289
390
                                                              name=guest.name,
290
391
                                                              suffix=".img")
291
392
        volinst = vc(pool_name=path, name=vname, conn=guest.conn,
292
 
                     allocation=0, capacity=(size and size*1024*1024*1024))
 
393
                     allocation=0, capacity=(size and
 
394
                                             size * 1024 * 1024 * 1024))
293
395
        if fmt:
294
396
            if not hasattr(volinst, "format"):
295
397
                raise ValueError(_("Format attribute not supported for this "
325
427
               'device': devtype,
326
428
               'bus': bus,
327
429
               'driverCache': cache,
328
 
               'format': fmt }
 
430
               'format': fmt,
 
431
               'driverName': drvname,
 
432
               'driverType': drvtype}
329
433
 
330
434
    logging.debug("parse_disk: returning %s" % str(kwargs))
331
435
    return kwargs
350
454
def get_disks(file_paths, disk_paths, size, sparse, nodisks, guest, conn):
351
455
    if nodisks:
352
456
        if file_paths or disk_paths or size:
353
 
            fail(_("Cannot use --file, --file-size, or --disk with --nodisks"))
 
457
            fail(_("Cannot specify storage and use --nodisks"))
354
458
        return
355
459
    if (file_paths or size or sparse == False) and disk_paths:
356
460
        fail(_("Cannot mix --file, --nonsparse, or --file-size with --disk "
357
 
               "options. Please see the manual for --disk syntax."))
358
 
    elif not file_paths and not disk_paths and not cli.is_prompt():
359
 
        fail(_("A disk must be specified (use --nodisks to override)"))
 
461
               "options. Use --disk PATH[,size=SIZE][,sparse=yes|no]"))
 
462
    if (not storage_specified(file_paths, disk_paths, nodisks) and
 
463
        not cli.is_prompt()):
 
464
        fail(disk_missing)
360
465
 
361
466
    is_file_path = (file_paths or (not disk_paths and cli.is_prompt()))
362
467
    disk = (file_paths or disk_paths)
430
535
    for guest in caps.guests:
431
536
        if guest.os_type == req_virt_type and guest.arch == arch:
432
537
            for dom in guest.domains:
433
 
                if dom.hypervisor_type in [ "kvm", "kqemu" ]:
 
538
                if dom.is_accelerated():
434
539
                    supports_accel = True
435
540
                    accel_type = dom.hypervisor_type.upper()
436
541
 
478
583
 
479
584
    try:
480
585
        (capsguest,
481
 
         capsdomain) = virtinst.CapabilitiesParser.guest_lookup(conn=conn,
482
 
                        caps=capabilities, os_type=req_virt_type,
483
 
                        arch=arch, type=req_hv_type, accelerated=req_accel)
 
586
         capsdomain) = virtinst.CapabilitiesParser.guest_lookup(
 
587
                        conn=conn,
 
588
                        caps=capabilities,
 
589
                        os_type=req_virt_type,
 
590
                        arch=arch,
 
591
                        type=req_hv_type,
 
592
                        accelerated=req_accel,
 
593
                        machine=options.machine)
484
594
    except Exception, e:
485
595
        fail(e)
486
596
 
 
597
    if (not req_virt_type and
 
598
        req_accel and
 
599
        _util.is_qemu(conn) and
 
600
        capsguest.arch in ["i686", "x86_64"] and
 
601
        not capsdomain.is_accelerated()):
 
602
        logging.warn("KVM acceleration not available, using '%s'" %
 
603
                     capsdomain.hypervisor_type)
 
604
 
487
605
    return (capsguest, capsdomain)
488
606
 
489
607
 
494
612
    for m in [pxe, location, cdpath, import_install]:
495
613
        if m:
496
614
            if found:
497
 
                fail(_("Only one install method (%s) can be used") %
498
 
                       "--pxe, --location, --cdrom, --import")
 
615
                fail(_("Only one install method can be used (%(methods)s)") %
 
616
                       {"methods" : install_methods})
499
617
            found = True
500
618
 
501
619
    if not ishvm:
508
626
    if location and virtinst.util.is_uri_remote(guest.conn.getURI()):
509
627
        fail(_("--location can not be specified for remote connections."))
510
628
 
 
629
    # Make sure some install option is specified
511
630
    cdinstall = (cdpath or False)
512
 
    if not (pxe or cdpath or location or import_install):
513
 
        # Look at Guest disks: if there is a cdrom, use for install
514
 
        for disk in guest.disks:
515
 
            if disk.device == virtinst.VirtualDisk.DEVICE_CDROM:
516
 
                cdinstall = True
 
631
    if not install_specified(location, cdpath, pxe, import_install):
 
632
        cdinstall = cdrom_specified(guest)
517
633
        if not cdinstall and not cli.is_prompt():
518
 
            fail(_("One of %s, or cdrom media must be specified.") %
519
 
                  "--pxe, --location, --import")
 
634
            fail(install_missing)
520
635
 
521
636
 
522
637
    if pxe or import_install:
523
638
        return
524
639
 
525
640
    try:
526
 
        if not location and not cdpath and cli.is_prompt():
 
641
        if not location and not cdinstall and cli.is_prompt():
527
642
            media_prompt(guest, ishvm)
528
643
        else:
529
644
            validate_install_media(guest, location, cdpath, cdinstall)
542
657
        cdpath = None
543
658
        media = cli.prompt_for_input("", prompt_txt, None)
544
659
 
 
660
        if not len(media):
 
661
            continue
 
662
 
545
663
        if not ishvm or media.count(":/"):
546
664
            location = media
547
665
        else:
555
673
        break
556
674
 
557
675
def validate_install_media(guest, location, cdpath, cdinstall=False):
558
 
    if location or cdpath:
559
 
        guest.location = (location or cdpath)
560
 
 
561
676
    if cdinstall or cdpath:
562
677
        guest.installer.cdrom = True
 
678
    if location or cdpath:
 
679
        guest.installer.location = (location or cdpath)
563
680
 
 
681
    if hasattr(guest.installer, "check_location"):
 
682
        guest.installer.check_location()
564
683
 
565
684
### Option parsing
566
685
def parse_args():
579
698
    geng.add_option("-r", "--ram", type="int", dest="memory",
580
699
                    help=_("Memory to allocate for guest instance in "
581
700
                           "megabytes"))
582
 
    geng.add_option("", "--vcpus", type="int", dest="vcpus",
583
 
                    help=_("Number of vcpus to configure for your guest"))
584
 
    geng.add_option("", "--cpuset", type="string", dest="cpuset",
585
 
                    action="callback", callback=cli.check_before_store,
586
 
                    help=_("Set which physical CPUs Domain can use."))
 
701
    cli.vcpu_cli_options(geng)
587
702
    geng.add_option("", "--description", type="string", dest="description",
588
703
                    action="callback", callback=cli.check_before_store,
589
704
                    help=_("Human readable description of the VM to store in "
609
724
                    help=_("Treat the CD-ROM media as a Live CD"))
610
725
    insg.add_option("-x", "--extra-args", type="string", dest="extra",
611
726
                    default="",
612
 
                    help=_("Additional arguments to pass to the kernel "
 
727
                    help=_("Additional arguments to pass to the install kernel "
613
728
                           "booted from --location"))
 
729
    insg.add_option("", "--initrd-inject", type="string",
 
730
                    dest="initrd_injections", action="callback",
 
731
                    callback=cli.check_before_append,
 
732
                    help=_("Add given file to root of initrd from --location"))
614
733
    insg.add_option("", "--os-type", type="string", dest="distro_type",
615
734
                    action="callback", callback=cli.check_before_store,
616
735
                    help=_("The OS type being installed, e.g. "
617
736
                           "'linux', 'unix', 'windows'"))
618
737
    insg.add_option("", "--os-variant", type="string", dest="distro_variant",
619
 
                      action="callback", callback=cli.check_before_store,
620
 
                      help=_("The OS variant being installed guests, "
621
 
                             "e.g. 'fedora6', 'rhel5', 'solaris10', 'win2k'"))
 
738
                    action="callback", callback=cli.check_before_store,
 
739
                    help=_("The OS variant being installed guests, "
 
740
                           "e.g. 'fedora6', 'rhel5', 'solaris10', 'win2k'"))
 
741
    insg.add_option("", "--boot", type="string", dest="bootopts", default="",
 
742
                    help=_("Optionally configure post-install boot order, "
 
743
                           "menu, permanent kernel boot, etc."))
622
744
    parser.add_option_group(insg)
623
745
 
624
746
    stog = OptionGroup(parser, _("Storage Configuration"))
664
786
 
665
787
    vncg = cli.graphics_option_group(parser)
666
788
    vncg.add_option("", "--noautoconsole", action="store_false",
667
 
                    dest="autoconsole",
 
789
                    dest="autoconsole", default=True,
668
790
                    help=_("Don't automatically try to connect to the guest "
669
791
                           "console"))
670
792
    parser.add_option_group(vncg)
676
798
    devg.add_option("", "--parallel", type="string", dest="parallels",
677
799
                    action="callback", callback=cli.check_before_append,
678
800
                    help=_("Add a parallel device to the domain."))
 
801
    geng.add_option("", "--channel", type="string", dest="channels",
 
802
                    action="callback", callback=cli.check_before_append,
 
803
                    help=_("Add a guest communication channel."))
 
804
    geng.add_option("", "--console", type="string", dest="consoles",
 
805
                    action="callback", callback=cli.check_before_append,
 
806
                    help=_("Add a text console connection between the guest "
 
807
                           "and host."))
679
808
    devg.add_option("", "--host-device", type="string", dest="hostdevs",
680
809
                    action="callback", callback=cli.check_before_append,
681
810
                    help=_("Physical host device to attach to the domain."))
708
837
    virg.add_option("", "--arch", type="string", dest="arch",
709
838
                    action="callback", callback=cli.check_before_store,
710
839
                    help=_("The CPU architecture to simulate"))
 
840
    virg.add_option("", "--machine", type="string", dest="machine",
 
841
                    action="callback", callback=cli.check_before_store,
 
842
                    help=_("The machine type to emulate"))
711
843
    virg.add_option("", "--noapic", action="store_true", dest="noapic",
712
844
                    default=False,
713
845
                    help=_("Disables APIC for fully virtualized guest "
725
857
    misc.add_option("", "--autostart", action="store_true", default=False,
726
858
                    dest="autostart",
727
859
                    help=_("Have domain autostart on host boot up."))
 
860
    misc.add_option("", "--print-xml", action="store_true", dest="xmlonly",
 
861
                    help=_("Print the generated domain XML rather than define "
 
862
                           "the guest."))
 
863
    misc.add_option("", "--print-step", type="str", dest="xmlstep",
 
864
                    help=_("Print XML of a specific install step "
 
865
                           "(1, 2, 3, all) rather than define the guest."))
728
866
    misc.add_option("", "--noreboot", action="store_true", dest="noreboot",
729
867
                    help=_("Disables the automatic rebooting when the "
730
868
                           "installation is complete."))
731
869
    misc.add_option("", "--wait", type="int", dest="wait",
732
870
                    help=_("Time to wait (in minutes)"))
 
871
    misc.add_option("", "--dry-run", action="store_true", dest="dry",
 
872
                    help=_("Run through install process, but do not "
 
873
                           "create devices or define the guest."))
733
874
    misc.add_option("", "--force", action="store_true", dest="force",
734
875
                    help=_("Forces 'yes' for any applicable prompts, "
735
876
                           "terminates for all others"),
736
877
                      default=False)
 
878
    misc.add_option("-q", "--quiet", action="store_true", dest="quiet",
 
879
                    help=_("Suppress non-error output"))
737
880
    misc.add_option("", "--prompt", action="store_true", dest="prompt",
738
881
                    help=_("Request user input for ambiguous situations or "
739
882
                           "required options."), default=False)
740
 
    misc.add_option("", "--check-cpu", action="store_true", dest="check_cpu",
741
 
                    help=_("Check that vcpus do not exceed physical CPUs "
742
 
                             "and warn if they do."))
743
883
    misc.add_option("-d", "--debug", action="store_true", dest="debug",
744
884
                    help=_("Print debugging information"))
745
885
    parser.add_option_group(misc)
746
886
 
747
 
    (options, dummy) = parser.parse_args()
748
 
    return options
 
887
    (options, cliargs) = parser.parse_args()
 
888
    return options, cliargs
749
889
 
750
890
 
751
891
def vnc_console(dom, uri):
759
899
            os.execvp(args[0], args)
760
900
        except OSError, (err, msg):
761
901
            if err == errno.ENOENT:
762
 
                print _("Unable to connect to graphical console: virt-viewer not installed. Please install the 'virt-viewer' package.")
 
902
                logging.warn(_("Unable to connect to graphical console: "
 
903
                               "virt-viewer not installed. Please install "
 
904
                               "the 'virt-viewer' package."))
763
905
            else:
764
 
                raise OSError, (err, msg)
 
906
                raise OSError(err, msg)
765
907
        os._exit(1)
766
908
 
767
909
    return child
778
920
 
779
921
    return child
780
922
 
781
 
### Let's do it!
782
 
def main():
783
 
    options = parse_args()
784
 
 
785
 
    # Default setup options
786
 
    cli.setupLogging("virt-install", options.debug)
787
 
    cli.set_force(options.force)
788
 
    cli.set_prompt(options.prompt)
789
 
    conn = cli.getConnection(options.connect)
790
 
 
 
923
def build_guest_instance(conn, options):
791
924
    capsguest, capsdomain = get_virt_type(conn, options)
792
925
 
793
926
    virt_type = capsguest.os_type
804
937
            fail(_("Can't use --pxe with --nonetworks"))
805
938
 
806
939
        instclass = virtinst.PXEInstaller
807
 
    elif options.import_install:
 
940
    elif options.cdrom or options.location:
 
941
        instclass = virtinst.DistroInstaller
 
942
    elif options.import_install or options.bootopts:
 
943
        if options.import_install and options.nodisks:
 
944
            fail(_("A disk device must be specified with --import."))
 
945
        options.import_install = True
808
946
        instclass = virtinst.ImportInstaller
809
947
    else:
810
948
        instclass = virtinst.DistroInstaller
 
949
 
811
950
    installer = instclass(type=hv_name, os_type=virt_type, conn=conn)
812
951
    installer.arch = capsguest.arch
813
 
 
 
952
    installer.initrd_injections = options.initrd_injections
 
953
    installer.machine = options.machine
814
954
 
815
955
    # Get Guest instance from installer parameters.
816
956
    guest = installer.guest_from_installer()
817
957
 
818
958
 
819
959
    # now let's get some of the common questions out of the way
820
 
    if virt_type == "hvm":
821
 
        ishvm = True
822
 
    else:
823
 
        ishvm = False
824
 
 
825
 
    cli.get_name(options.name, guest)
826
 
    cli.get_memory(options.memory, guest)
 
960
    ishvm = bool(virt_type == "hvm")
 
961
 
 
962
    # Optional config
 
963
    get_networks(options.mac, options.bridge, options.network,
 
964
                 options.nonetworks, guest)
 
965
    cli.get_graphics(options.vnc, options.vncport, options.vnclisten,
 
966
                     options.nographics, options.sdl, options.keymap,
 
967
                     options.video, options.graphics, guest)
 
968
 
827
969
    cli.get_uuid(options.uuid, guest)
828
 
    cli.get_vcpus(options.vcpus, options.check_cpu, guest, conn)
829
 
    cli.get_cpuset(options.cpuset, guest.memory, guest, conn)
 
970
    cli.get_vcpus(options.vcpus, options.check_cpu, guest)
 
971
    cli.get_cpuset(options.cpuset, guest.memory, guest)
 
972
    cli.parse_cpu(guest, options.cpu)
830
973
    get_security(options.security, guest)
 
974
    parse_boot_option(guest, options.bootopts)
831
975
 
832
976
    get_watchdog(options.watchdog, guest)
833
977
    cli.get_sound(options.sound, options.soundhw, guest)
834
978
    get_chardevs(VirtualDevice.VIRTUAL_DEV_SERIAL, options.serials, guest)
835
979
    get_chardevs(VirtualDevice.VIRTUAL_DEV_PARALLEL, options.parallels, guest)
 
980
    get_chardevs(VirtualDevice.VIRTUAL_DEV_CHANNEL, options.channels, guest)
 
981
    get_chardevs(VirtualDevice.VIRTUAL_DEV_CONSOLE, options.consoles, guest)
836
982
 
837
983
    guest.autostart = options.autostart
838
984
    guest.description = options.description
839
985
 
840
 
    # set up disks
841
 
    get_disks(options.file_path, options.diskopts, options.disksize,
842
 
              options.sparse, options.nodisks, guest, conn)
843
 
 
844
 
    # set up network information
845
 
    get_networks(options.mac, options.bridge, options.network,
846
 
                 options.nonetworks, guest)
847
 
 
848
 
    # set up graphics and video device information
849
 
    cli.get_graphics(options.vnc, options.vncport, options.vnclisten,
850
 
                     options.nographics, options.sdl, options.keymap,
851
 
                     options.video, guest)
852
 
 
853
986
    # Set host device info
854
987
    cli.get_hostdevs(options.hostdevs, guest)
855
988
 
856
989
    guest.extraargs = options.extra
 
990
    guest.features["acpi"] = not options.noacpi
 
991
    guest.features["apic"] = not options.noapic
 
992
 
857
993
    cli.set_os_variant(guest, options.distro_type, options.distro_variant)
858
994
 
859
 
    # and now for the full-virt vs paravirt specific questions
 
995
    # Required config. Don't error right away if nothing is specified,
 
996
    # aggregate the errors to help first time users get it right
 
997
    msg = ""
 
998
    if not cli.is_prompt():
 
999
        if not options.name:
 
1000
            msg += "\n" + cli.name_missing
 
1001
        if not options.memory:
 
1002
            msg += "\n" + cli.ram_missing
 
1003
        if not storage_specified(options.file_path, options.diskopts,
 
1004
                                 options.nodisks):
 
1005
            msg += "\n" + disk_missing
 
1006
        if ((not install_specified(options.location, options.cdrom,
 
1007
                                   options.pxe, options.import_install)) and
 
1008
            (not cdrom_specified(guest, options.diskopts))):
 
1009
            msg += "\n" + install_missing
 
1010
        if msg:
 
1011
            fail(msg)
 
1012
 
 
1013
    cli.get_name(options.name, guest)
 
1014
    cli.get_memory(options.memory, guest)
 
1015
    get_disks(options.file_path, options.diskopts, options.disksize,
 
1016
              options.sparse, options.nodisks, guest, conn)
860
1017
    get_install_media(options.location, options.cdrom, options.pxe,
861
 
                      options.livecd, options.import_install, guest, ishvm)
862
 
 
863
 
    continue_inst = guest.get_continue_inst()
864
 
    if options.noacpi:
865
 
        guest.features["acpi"] = False
866
 
    if options.noapic:
867
 
        guest.features["apic"] = False
868
 
 
 
1018
                      options.livecd, options.import_install,
 
1019
                      guest, ishvm)
 
1020
 
 
1021
    if not options.location and options.extra:
 
1022
        fail(_("--extra-args only work if specified with --location."))
 
1023
 
 
1024
    if options.pxe and not supports_pxe(guest):
 
1025
        logging.warn(_("The guest's network configuration does not support "
 
1026
                       "PXE"))
 
1027
 
 
1028
    return guest
 
1029
 
 
1030
def start_install(guest, continue_inst, options):
869
1031
    def show_console(dom):
870
1032
        if guest.graphics_dev:
871
1033
            if guest.graphics_dev.type == virtinst.VirtualGraphics.TYPE_VNC:
872
 
                return vnc_console(dom, options.connect)
 
1034
                return vnc_console(dom, guest.conn.getURI())
873
1035
            else:
874
1036
                return None # SDL needs no viewer app
875
1037
        else:
876
 
            return txt_console(dom, options.connect)
 
1038
            return txt_console(dom, guest.conn.getURI())
877
1039
 
878
1040
    # There are two main cases we care about:
879
1041
    #
885
1047
    # waiting.  Otherwise, we can exit before the domain has finished
886
1048
    # installing. Passing --wait will give the above semantics.
887
1049
    #
888
 
    wait = continue_inst
 
1050
    wait_on_install = continue_inst
889
1051
    wait_time = -1
890
 
    autoconsole = options.autoconsole
891
 
 
892
1052
    if options.wait != None:
893
 
        wait = True
 
1053
        wait_on_install = True
894
1054
        wait_time = options.wait * 60
895
 
        if wait_time == 0:
896
 
            # --wait 0 implies --noautoconsole
897
 
            autoconsole = False
898
 
 
899
 
    if autoconsole is False:
900
 
        conscb = None
901
 
    else:
902
 
        conscb = show_console
903
 
 
904
 
    progresscb = progress.TextMeter()
905
 
 
 
1055
 
 
1056
    # If --wait specified, we don't want the default behavior of waiting
 
1057
    # for virt-viewer to exit, since then we can't exit the app when time
 
1058
    # expires
 
1059
    wait_on_console = not wait_on_install
 
1060
 
 
1061
    # --wait 0 implies --noautoconsole
 
1062
    options.autoconsole = (wait_time != 0) and options.autoconsole or False
 
1063
 
 
1064
    conscb = options.autoconsole and show_console or None
 
1065
    meter = options.quiet and progress.BaseMeter() or progress.TextMeter()
 
1066
    logging.debug("Guest.has_install_phase: %s" %
 
1067
                  guest.installer.has_install_phase())
906
1068
 
907
1069
    # we've got everything -- try to start the install
908
 
    print _("\n\nStarting install...")
 
1070
    print_stdout(_("\nStarting install..."))
909
1071
 
910
1072
    try:
911
1073
        start_time = time.time()
912
1074
 
913
1075
        # Do first install phase
914
 
        dom = do_install(guest, conscb, progresscb, wait, wait_time,
915
 
                         start_time, guest.start_install)
 
1076
 
 
1077
        dom = guest.start_install(conscb, meter, wait=wait_on_console)
 
1078
        dom = check_domain(guest, dom, conscb,
 
1079
                           wait_on_install, wait_time, start_time)
916
1080
 
917
1081
        # This should be valid even before doing continue install
918
1082
        if not guest.post_install_check():
919
 
            print _("Domain installation does not appear to have been\n "
920
 
                    "successful.  If it was, you can restart your domain\n "
921
 
                    "by running 'virsh start %s'; otherwise, please\n "
922
 
                    "restart your installation.") % guest.name
923
 
            sys.exit(0)
 
1083
            cli.install_fail(guest)
924
1084
 
925
1085
        if continue_inst:
926
 
            dom = do_install(guest, conscb, progresscb, wait, wait_time,
927
 
                             start_time, guest.continue_install)
 
1086
            dom = guest.continue_install(conscb, meter, wait=wait_on_console)
 
1087
            dom = check_domain(guest, dom, conscb,
 
1088
                               wait_on_install, wait_time, start_time)
928
1089
 
929
 
        if options.noreboot:
930
 
            print _("Guest installation complete... you can restart your "
931
 
                    "domain\nby running 'virsh start %s'") % guest.name
 
1090
        if options.noreboot or not guest.installer.has_install_phase():
 
1091
            # XXX: --noreboot doesn't work with say import/livecd. what
 
1092
            # should it do? never startup the guest?
 
1093
            print_stdout(
 
1094
            _("Domain creation completed. You can restart your domain by "
 
1095
              "running:\n  %s") % cli.virsh_start_cmd(guest))
932
1096
        else:
933
 
            # FIXME: Should we say 'installation' complete for livecd, import?
934
 
            print _("Guest installation complete... restarting guest.")
 
1097
            print_stdout(
 
1098
                _("Guest installation complete... restarting guest."))
935
1099
            dom.create()
936
1100
            guest.connect_console(conscb)
937
1101
 
938
1102
    except KeyboardInterrupt, e:
 
1103
        cli.log_exception()
939
1104
        guest.terminate_console()
940
 
        print _("Guest install interrupted.")
 
1105
        print_stderr(_("Domain install interrupted."))
941
1106
    except RuntimeError, e:
942
1107
        fail(e)
943
1108
    except SystemExit, e:
944
1109
        sys.exit(e.code)
945
1110
    except Exception, e:
946
 
        logging.error(e)
947
 
        print _("Domain installation does not appear to have been\n "
948
 
                "successful.  If it was, you can restart your domain\n "
949
 
                "by running 'virsh start %s'; otherwise, please\n "
950
 
                "restart your installation.") % guest.name
951
 
        raise
952
 
 
953
 
 
954
 
def do_install(guest, conscb, progresscb, wait, wait_time, start_time,
955
 
               install_func):
956
 
 
957
 
    dom = install_func(conscb, progresscb, wait=(not wait))
 
1111
        fail(e, do_exit=False)
 
1112
        cli.install_fail(guest)
 
1113
 
 
1114
def check_domain(guest, dom, conscb, wait_for_install, wait_time, start_time):
 
1115
    """
 
1116
    Make sure domain ends up in expected state, and wait if for install
 
1117
    to complete if requested
 
1118
    """
 
1119
    wait_forever = (wait_time < 0)
958
1120
 
959
1121
    # Wait a bit so info is accurate
960
1122
    def check_domain_state():
962
1124
        state = dominfo[0]
963
1125
 
964
1126
        if guest.domain_is_crashed():
965
 
            print _("Domain has crashed.")
966
 
            sys.exit(1)
 
1127
            fail(_("Domain has crashed."))
967
1128
 
968
1129
        if guest.domain_is_shutdown():
969
1130
            return dom, state
988
1149
    if ret:
989
1150
        return ret
990
1151
 
 
1152
    # Domain seems to be running
991
1153
    logging.debug("Domain state after install: %s" % state)
992
1154
 
993
 
    # Domain seems to be running
994
 
    if wait and wait_time != 0:
995
 
        timestr = ""
996
 
        if wait_time > 0:
997
 
            timestr = _("%d minutes ") % (int(wait_time) / 60)
998
 
 
999
 
        print _("Domain installation still in progress. Waiting %s"
1000
 
                "for installation to complete.") % timestr
1001
 
 
1002
 
        # Wait loop
1003
 
        while True:
1004
 
            if guest.domain_is_shutdown():
1005
 
                print _("Domain has shutdown. Continuing.")
1006
 
                try:
1007
 
                    # Lookup a new domain object incase current
1008
 
                    # one returned bogus data (see comment in
1009
 
                    # domain_is_shutdown
1010
 
                    dom = guest.conn.lookupByName(guest.name)
1011
 
                except Exception, e:
1012
 
                    raise RuntimeError(_("Could not lookup domain after "
1013
 
                                         "install: %s" % str(e)))
1014
 
                break
1015
 
 
1016
 
            if wait_time < 0 or ((time.time() - start_time) < wait_time):
1017
 
                time.sleep(2)
1018
 
            else:
1019
 
                print _("Installation has exceeded specified time limit. "
1020
 
                        "Exiting application.")
1021
 
                sys.exit(1)
1022
 
    else:
1023
 
        # User specified --wait 0, which means --noautoconsole, which
1024
 
        # means just exit.
1025
 
        print _("Domain installation still in progress. You can reconnect"
1026
 
                " to \nthe console to complete the installation process.")
 
1155
    if not wait_for_install or wait_time == 0:
 
1156
        # User either:
 
1157
        #   used --noautoconsole
 
1158
        #   used --wait 0
 
1159
        #   killed console and guest is still running
 
1160
        if not guest.installer.has_install_phase():
 
1161
            return dom
 
1162
 
 
1163
        print_stdout(
 
1164
            _("Domain installation still in progress. You can reconnect"
 
1165
              " to \nthe console to complete the installation process."))
1027
1166
        sys.exit(0)
1028
1167
 
 
1168
    timestr = (not wait_forever and
 
1169
               _("%d minutes ") % (int(wait_time) / 60) or "")
 
1170
    print_stdout(
 
1171
        _("Domain installation still in progress. Waiting %s"
 
1172
          "for installation to complete.") % timestr)
 
1173
 
 
1174
    # Wait loop
 
1175
    while True:
 
1176
        if guest.domain_is_shutdown():
 
1177
            print_stdout(_("Domain has shutdown. Continuing."))
 
1178
            try:
 
1179
                # Lookup a new domain object incase current
 
1180
                # one returned bogus data (see comment in
 
1181
                # domain_is_shutdown
 
1182
                dom = guest.conn.lookupByName(guest.name)
 
1183
            except Exception, e:
 
1184
                raise RuntimeError(_("Could not lookup domain after "
 
1185
                                     "install: %s" % str(e)))
 
1186
            break
 
1187
 
 
1188
        time_elapsed = (time.time() - start_time)
 
1189
        if not wait_forever and time_elapsed >= wait_time:
 
1190
            print_stdout(
 
1191
                _("Installation has exceeded specified time limit. "
 
1192
                        "Exiting application."))
 
1193
            sys.exit(1)
 
1194
 
 
1195
        time.sleep(2)
 
1196
 
1029
1197
    return dom
1030
1198
 
 
1199
def xml_to_print(guest, continue_inst, xmlonly, xmlstep, dry):
 
1200
    start_xml, final_xml = guest.start_install(dry=dry, return_xml=True)
 
1201
    second_xml = None
 
1202
    if not start_xml:
 
1203
        start_xml = final_xml
 
1204
        final_xml = None
 
1205
 
 
1206
    if continue_inst:
 
1207
        second_xml, final_xml = guest.continue_install(dry=dry,
 
1208
                                                       return_xml=True)
 
1209
 
 
1210
    if dry and not (xmlonly or xmlstep):
 
1211
        print_stdout(_("Dry run completed successfully"))
 
1212
        return
 
1213
 
 
1214
    # --xml-only
 
1215
    if xmlonly and not xmlstep:
 
1216
        if second_xml or final_xml:
 
1217
            fail(_("--xml-only can only be used with guests that do not have "
 
1218
                   "an installation phase (--import, --boot, etc.). To see all"
 
1219
                   "all generated XML, please use --xml-step all."))
 
1220
        return start_xml
 
1221
 
 
1222
    # --xml-step
 
1223
    if xmlstep == "1":
 
1224
        return start_xml
 
1225
    if xmlstep == "2":
 
1226
        if not (second_xml or final_xml):
 
1227
            fail(_("Requested installation does not have XML step 2"))
 
1228
        return second_xml or final_xml
 
1229
    if xmlstep == "3":
 
1230
        if not second_xml:
 
1231
            fail(_("Requested installation does not have XML step 3"))
 
1232
        return final_xml
 
1233
 
 
1234
    # "all" case
 
1235
    xml = start_xml
 
1236
    if second_xml:
 
1237
        xml += second_xml
 
1238
    if final_xml:
 
1239
        xml += final_xml
 
1240
    return xml
 
1241
 
 
1242
def main():
 
1243
    cli.earlyLogging()
 
1244
    options, cliargs = parse_args()
 
1245
 
 
1246
    # Default setup options
 
1247
    options.quiet = options.xmlstep or options.xmlonly or options.quiet
 
1248
    cli.setupLogging("virt-install", options.debug, options.quiet)
 
1249
    if cliargs:
 
1250
        fail(_("Unknown argument '%s'") % cliargs[0])
 
1251
 
 
1252
    cli.set_force(options.force)
 
1253
    cli.set_prompt(options.prompt)
 
1254
    conn = cli.getConnection(options.connect)
 
1255
 
 
1256
    if options.xmlstep not in [None, "1", "2", "3", "all"]:
 
1257
        fail(_("--print-step must be 1, 2, 3, or all"))
 
1258
 
 
1259
    guest = build_guest_instance(conn, options)
 
1260
    continue_inst = guest.get_continue_inst()
 
1261
 
 
1262
    if options.xmlstep or options.xmlonly or options.dry:
 
1263
        xml = xml_to_print(guest, continue_inst,
 
1264
                           options.xmlonly, options.xmlstep, options.dry)
 
1265
        if xml:
 
1266
            print_stdout(xml, do_force=True)
 
1267
    else:
 
1268
        start_install(guest, continue_inst, options)
 
1269
 
 
1270
    return 0
 
1271
 
1031
1272
if __name__ == "__main__":
1032
1273
    try:
1033
 
        main()
 
1274
        sys.exit(main())
1034
1275
    except SystemExit, sys_e:
1035
1276
        sys.exit(sys_e.code)
1036
1277
    except KeyboardInterrupt:
1037
 
        print >> sys.stderr, _("Installation aborted at user request")
 
1278
        cli.log_exception()
 
1279
        print_stderr(_("Installation aborted at user request"))
1038
1280
    except Exception, main_e:
1039
 
        logging.exception(main_e)
1040
 
        sys.exit(1)
1041
 
 
 
1281
        fail(main_e)