~ubuntu-branches/ubuntu/oneiric/virtinst/oneiric

« back to all changes in this revision

Viewing changes to virtinst/cli.py

  • Committer: Bazaar Package Importer
  • Author(s): Marc Deslauriers
  • Date: 2011-08-08 13:43:26 UTC
  • mfrom: (1.6.4 sid)
  • Revision ID: james.westby@ubuntu.com-20110808134326-9ej41e5x1o1pg2ha
Tags: 0.600.0-1ubuntu1
* Merge from debian unstable. Remaining changes:
  - 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.
* Removed patches:
  - 9002_Lucid.patch: upstream
  - 9004_ubuntu_correct_tree.patch: upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
import logging.handlers
26
26
import gettext
27
27
import locale
28
 
import optparse
29
 
from optparse import OptionValueError, OptionParser, OptionGroup
30
28
import re
31
29
import difflib
32
30
import tempfile
 
31
import optparse
 
32
import shlex
33
33
 
34
34
import libvirt
35
 
import _util
 
35
 
36
36
import virtinst
 
37
from virtinst import _util
 
38
from _util import log_exception
37
39
from _util import listify
38
 
from virtinst import VirtualNetworkInterface, Guest, \
39
 
                     VirtualGraphics, VirtualAudio, VirtualDisk, User
40
 
from virtinst import _virtinst as _
 
40
from virtinst import _gettext as _
 
41
 
 
42
from virtinst import Guest
 
43
from virtinst import VirtualNetworkInterface
 
44
from virtinst import VirtualGraphics
 
45
from virtinst import VirtualAudio
 
46
from virtinst import VirtualDisk
 
47
from virtinst import VirtualCharDevice
 
48
from virtinst import VirtualDevice
 
49
from virtinst import User
 
50
 
 
51
DEFAULT_POOL_PATH = "/var/lib/libvirt/images"
 
52
DEFAULT_POOL_NAME = "default"
41
53
 
42
54
MIN_RAM = 64
43
55
force = False
44
56
quiet = False
45
57
doprompt = True
46
58
 
47
 
log_exception = _util.log_exception
48
 
_virtinst_uri_magic = "__virtinst_test__"
49
 
 
50
 
def _is_virtinst_test_uri(uri):
51
 
    return uri and uri.startswith(_virtinst_uri_magic)
52
 
 
53
 
def _open_test_uri(uri):
54
 
    """
55
 
    This hack allows us to fake various drivers via passing a magic
56
 
    URI string to virt-*. Helps with testing
57
 
    """
58
 
    uri = uri.replace(_virtinst_uri_magic, "")
59
 
    ret = uri.split(",", 1)
60
 
    uri = ret[0]
61
 
    ignore, opts = parse_optstr(len(ret) > 1 and ret[1] or "")
62
 
 
63
 
    conn = open_connection(uri)
64
 
 
65
 
    def sanitize_xml(xml):
66
 
        orig = xml
67
 
        xml = re.sub("arch='.*'", "arch='i686'", xml)
68
 
        xml = re.sub("domain type='.*'", "domain type='test'", xml)
69
 
        xml = re.sub("machine type='.*'", "", xml)
70
 
 
71
 
        logging.debug("virtinst test sanitizing diff\n:%s" %
72
 
                      "\n".join(difflib.unified_diff(orig.split("\n"),
73
 
                                                     xml.split("\n"))))
74
 
        return xml
75
 
 
76
 
    # Need tmpfile names to be deterministic
77
 
    if "predictable" in opts:
78
 
        def fakemkstemp(prefix, *args, **kwargs):
79
 
            ignore = args
80
 
            ignore = kwargs
81
 
            filename = os.path.join(".", prefix)
82
 
            return os.open(filename, os.O_RDWR | os.O_CREAT), filename
83
 
        tempfile.mkstemp = fakemkstemp
84
 
 
85
 
        _util.randomMAC = lambda type_: "00:11:22:33:44:55"
86
 
        _util.uuidToString = lambda r: "00000000-1111-2222-3333-444444444444"
87
 
 
88
 
    # Fake remote status
89
 
    if "remote" in opts:
90
 
        _util.is_uri_remote = lambda uri_: True
91
 
 
92
 
    # Fake capabilities
93
 
    if "caps" in opts:
94
 
        capsxml = file(opts["caps"]).read()
95
 
        conn.getCapabilities = lambda: capsxml
96
 
 
97
 
    if "qemu" in opts or "xen" in opts:
98
 
        conn.getVersion = lambda: 10000000000
99
 
 
100
 
        origcreate = conn.createLinux
101
 
        origdefine = conn.defineXML
102
 
        def newcreate(xml, flags):
103
 
            xml = sanitize_xml(xml)
104
 
            origcreate(xml, flags)
105
 
        def newdefine(xml):
106
 
            xml = sanitize_xml(xml)
107
 
            origdefine(xml)
108
 
        conn.createLinux = newcreate
109
 
        conn.defineXML = newdefine
110
 
 
111
 
        if "qemu" in opts:
112
 
            conn.getURI = lambda: "qemu+abc:///system"
113
 
        if "xen" in opts:
114
 
            conn.getURI = lambda: "xen+abc:///"
115
 
 
116
 
    return conn
 
59
 
 
60
####################
 
61
# CLI init helpers #
 
62
####################
117
63
 
118
64
class VirtStreamHandler(logging.StreamHandler):
119
65
 
138
84
        except:
139
85
            self.handleError(record)
140
86
 
141
 
class VirtOptionParser(OptionParser):
 
87
class VirtOptionParser(optparse.OptionParser):
142
88
    '''Subclass to get print_help to work properly with non-ascii text'''
143
89
 
144
90
    def _get_encoding(self, f):
189
135
        for line in text.split("\n"):
190
136
            ret.extend(self.oldwrap(line, width))
191
137
        return ret
192
 
#
193
 
# Setup helpers
194
 
#
195
138
 
196
139
def setupParser(usage=None):
197
140
    parse_class = VirtOptionParser
203
146
 
204
147
def setupGettext():
205
148
    locale.setlocale(locale.LC_ALL, '')
206
 
    gettext.bindtextdomain(virtinst.gettext_app, virtinst.gettext_dir)
207
 
    gettext.install(virtinst.gettext_app, virtinst.gettext_dir)
 
149
    gettext.bindtextdomain("virtinst")
 
150
    gettext.install("virtinst")
208
151
 
209
152
def earlyLogging():
210
153
    logging.basicConfig(level=logging.DEBUG, format='%(message)s')
278
221
    # Log the app command string
279
222
    logging.debug("Launched with command line:\n%s" % " ".join(sys.argv))
280
223
 
281
 
def fail(msg, do_exit=True):
282
 
    """Convenience function when failing in cli app"""
283
 
    logging.error(msg)
284
 
    log_exception()
285
 
    if do_exit:
286
 
        _fail_exit()
287
 
 
288
 
def print_stdout(msg, do_force=False):
289
 
    if do_force or not quiet:
290
 
        print msg
291
 
 
292
 
def print_stderr(msg):
293
 
    logging.debug(msg)
294
 
    print >> sys.stderr, msg
295
 
 
296
 
def _fail_exit():
297
 
    sys.exit(1)
298
 
 
299
 
def nice_exit():
300
 
    print_stdout(_("Exiting at user request."))
301
 
    sys.exit(0)
302
 
 
303
 
def virsh_start_cmd(guest):
304
 
    return ("virsh --connect %s start %s" % (guest.conn.getURI(), guest.name))
305
 
 
306
 
def install_fail(guest):
307
 
    virshcmd = virsh_start_cmd(guest)
308
 
 
309
 
    print_stderr(
310
 
        _("Domain installation does not appear to have been successful.\n"
311
 
          "If it was, you can restart your domain by running:\n"
312
 
          "  %s\n"
313
 
          "otherwise, please restart your installation.") % virshcmd)
314
 
    sys.exit(1)
315
 
 
316
 
# Connection opening helper functions
 
224
 
 
225
#######################################
 
226
# Libvirt connection helpers          #
 
227
#######################################
 
228
 
 
229
_virtinst_uri_magic = "__virtinst_test__"
 
230
 
 
231
def _is_virtinst_test_uri(uri):
 
232
    return uri and uri.startswith(_virtinst_uri_magic)
 
233
 
 
234
def _open_test_uri(uri):
 
235
    """
 
236
    This hack allows us to fake various drivers via passing a magic
 
237
    URI string to virt-*. Helps with testing
 
238
    """
 
239
    uri = uri.replace(_virtinst_uri_magic, "")
 
240
    ret = uri.split(",", 1)
 
241
    uri = ret[0]
 
242
    opts = parse_optstr(len(ret) > 1 and ret[1] or "")
 
243
 
 
244
    conn = open_connection(uri)
 
245
 
 
246
    def sanitize_xml(xml):
 
247
        orig = xml
 
248
        xml = re.sub("arch='.*'", "arch='i686'", xml)
 
249
        xml = re.sub("domain type='.*'", "domain type='test'", xml)
 
250
        xml = re.sub("machine type='.*'", "", xml)
 
251
        xml = re.sub(">exe<", ">hvm<", xml)
 
252
 
 
253
        logging.debug("virtinst test sanitizing diff\n:%s" %
 
254
                      "\n".join(difflib.unified_diff(orig.split("\n"),
 
255
                                                     xml.split("\n"))))
 
256
        return xml
 
257
 
 
258
    # Need tmpfile names to be deterministic
 
259
    if "predictable" in opts:
 
260
        def fakemkstemp(prefix, *args, **kwargs):
 
261
            ignore = args
 
262
            ignore = kwargs
 
263
            filename = os.path.join(".", prefix)
 
264
            return os.open(filename, os.O_RDWR | os.O_CREAT), filename
 
265
        tempfile.mkstemp = fakemkstemp
 
266
 
 
267
        _util.randomMAC = lambda type_: "00:11:22:33:44:55"
 
268
        _util.uuidToString = lambda r: "00000000-1111-2222-3333-444444444444"
 
269
 
 
270
    # Fake remote status
 
271
    if "remote" in opts:
 
272
        _util.is_uri_remote = lambda uri_: True
 
273
 
 
274
    # Fake capabilities
 
275
    if "caps" in opts:
 
276
        capsxml = file(opts["caps"]).read()
 
277
        conn.getCapabilities = lambda: capsxml
 
278
 
 
279
    if ("qemu" in opts) or ("xen" in opts) or ("lxc" in opts):
 
280
        conn.getVersion = lambda: 10000000000
 
281
 
 
282
        origcreate = conn.createLinux
 
283
        origdefine = conn.defineXML
 
284
        def newcreate(xml, flags):
 
285
            xml = sanitize_xml(xml)
 
286
            return origcreate(xml, flags)
 
287
        def newdefine(xml):
 
288
            xml = sanitize_xml(xml)
 
289
            return origdefine(xml)
 
290
        conn.createLinux = newcreate
 
291
        conn.defineXML = newdefine
 
292
 
 
293
        if "qemu" in opts:
 
294
            conn.getURI = lambda: "qemu+abc:///system"
 
295
        if "xen" in opts:
 
296
            conn.getURI = lambda: "xen+abc:///"
 
297
        if "lxc" in opts:
 
298
            conn.getURI = lambda: "lxc+abc:///"
 
299
 
 
300
    # These need to come after the HV setter, since that sets a default
 
301
    # conn version
 
302
    if "connver" in opts:
 
303
        ver = int(opts["connver"])
 
304
        def newconnversion():
 
305
            return ver
 
306
        conn.getVersion = newconnversion
 
307
 
 
308
    if "libver" in opts:
 
309
        ver = int(opts["libver"])
 
310
        def newlibversion(drv=None):
 
311
            if drv:
 
312
                return (ver, ver)
 
313
            return ver
 
314
        libvirt.getVersion = newlibversion
 
315
 
 
316
    return conn
 
317
 
317
318
def getConnection(uri):
318
319
    if (uri and not User.current().has_priv(User.PRIV_CREATE_DOMAIN, uri)):
319
320
        fail(_("Must be root to create Xen guests"))
427
428
 
428
429
    return 0
429
430
 
430
 
#
431
 
# Prompting
432
 
#
 
431
 
 
432
##############################
 
433
# Misc CLI utility functions #
 
434
##############################
 
435
 
 
436
def fail(msg, do_exit=True):
 
437
    """
 
438
    Convenience function when failing in cli app
 
439
    """
 
440
    logging.error(msg)
 
441
    log_exception()
 
442
    if do_exit:
 
443
        _fail_exit()
 
444
 
 
445
def print_stdout(msg, do_force=False):
 
446
    if do_force or not quiet:
 
447
        print msg
 
448
 
 
449
def print_stderr(msg):
 
450
    logging.debug(msg)
 
451
    print >> sys.stderr, msg
 
452
 
 
453
def _fail_exit():
 
454
    sys.exit(1)
 
455
 
 
456
def nice_exit():
 
457
    print_stdout(_("Exiting at user request."))
 
458
    sys.exit(0)
 
459
 
 
460
def virsh_start_cmd(guest):
 
461
    return ("virsh --connect %s start %s" % (guest.get_uri(), guest.name))
 
462
 
 
463
def install_fail(guest):
 
464
    virshcmd = virsh_start_cmd(guest)
 
465
 
 
466
    print_stderr(
 
467
        _("Domain installation does not appear to have been successful.\n"
 
468
          "If it was, you can restart your domain by running:\n"
 
469
          "  %s\n"
 
470
          "otherwise, please restart your installation.") % virshcmd)
 
471
    sys.exit(1)
 
472
 
 
473
def build_default_pool(guest):
 
474
 
 
475
    if not virtinst.util.is_storage_capable(guest.conn):
 
476
        # VirtualDisk will raise an error for us
 
477
        return
 
478
    pool = None
 
479
    try:
 
480
        pool = guest.conn.storagePoolLookupByName(DEFAULT_POOL_NAME)
 
481
    except libvirt.libvirtError:
 
482
        pass
 
483
 
 
484
    if pool:
 
485
        return
 
486
 
 
487
    try:
 
488
        logging.debug("Attempting to build default pool with target '%s'" %
 
489
                      DEFAULT_POOL_PATH)
 
490
        defpool = virtinst.Storage.DirectoryPool(conn=guest.conn,
 
491
                                                 name=DEFAULT_POOL_NAME,
 
492
                                                 target_path=DEFAULT_POOL_PATH)
 
493
        defpool.install(build=True, create=True, autostart=True)
 
494
    except Exception, e:
 
495
        raise RuntimeError(_("Couldn't create default storage pool '%s': %s") %
 
496
                             (DEFAULT_POOL_PATH, str(e)))
 
497
 
 
498
def partition(string, sep):
 
499
    if not string:
 
500
        return (None, None, None)
 
501
 
 
502
    if string.count(sep):
 
503
        splitres = string.split(sep, 1)
 
504
        ret = (splitres[0], sep, splitres[1])
 
505
    else:
 
506
        ret = (string, None, None)
 
507
    return ret
 
508
 
 
509
 
 
510
#######################
 
511
# CLI Prompting utils #
 
512
#######################
433
513
 
434
514
def set_force(val=True):
435
515
    global force
443
523
def is_prompt():
444
524
    return doprompt
445
525
 
 
526
def yes_or_no_convert(s):
 
527
    s = s.lower()
 
528
    if s in ("y", "yes", "1", "true", "t"):
 
529
        return True
 
530
    elif s in ("n", "no", "0", "false", "f"):
 
531
        return False
 
532
    return None
 
533
 
 
534
def yes_or_no(s):
 
535
    ret = yes_or_no_convert(s)
 
536
    if ret == None:
 
537
        raise ValueError(_("A yes or no response is required"))
 
538
    return ret
 
539
 
446
540
def prompt_for_input(noprompt_err, prompt="", val=None, failed=False):
447
541
    if val is not None:
448
542
        return val
457
551
    print_stdout(prompt + " ", do_force=True)
458
552
    return sys.stdin.readline().strip()
459
553
 
460
 
def yes_or_no_convert(s):
461
 
    s = s.lower()
462
 
    if s in ("y", "yes", "1", "true", "t"):
463
 
        return True
464
 
    elif s in ("n", "no", "0", "false", "f"):
465
 
        return False
466
 
    return None
467
 
 
468
 
def yes_or_no(s):
469
 
    ret = yes_or_no_convert(s)
470
 
    if ret == None:
471
 
        raise ValueError(_("A yes or no response is required"))
472
 
    return ret
473
 
 
474
554
def prompt_for_yes_or_no(warning, question):
475
555
    """catches yes_or_no errors and ensures a valid bool return"""
476
556
    if force:
493
573
            continue
494
574
    return res
495
575
 
496
 
# Prompt the user with 'prompt_txt' for a value. Set 'obj'.'param_name'
497
 
# to the entered value. If it errors, use 'err_txt' to print a error
498
 
# message, and then re prompt.
499
576
def prompt_loop(prompt_txt, noprompt_err, passed_val, obj, param_name,
500
577
                err_txt="%s", func=None):
 
578
    """
 
579
    Prompt the user with 'prompt_txt' for a value. Set 'obj'.'param_name'
 
580
    to the entered value. If it errors, use 'err_txt' to print a error
 
581
    message, and then re prompt.
 
582
    """
 
583
 
501
584
    failed = False
502
585
    while True:
503
586
        passed_val = prompt_for_input(noprompt_err, prompt_txt, passed_val,
512
595
            passed_val = None
513
596
            failed = True
514
597
 
515
 
def vcpu_cli_options(grp):
516
 
    grp.add_option("", "--vcpus", type="string", dest="vcpus",
517
 
        help=_("Number of vcpus to configure for your guest. Ex:\n"
518
 
               "--vcpus 5\n"
519
 
               "--vcpus 5,maxcpus=10\n"
520
 
               "--vcpus sockets=2,cores=4,threads=2"))
521
 
    grp.add_option("", "--cpuset", type="string", dest="cpuset",
522
 
                   action="callback", callback=check_before_store,
523
 
                   help=_("Set which physical CPUs Domain can use."))
524
 
    grp.add_option("", "--cpu", type="string", dest="cpu",
525
 
        action="callback", callback=check_before_store,
526
 
        help=_("CPU model and features. Ex: --cpu coreduo,+x2apic"))
527
 
    grp.add_option("", "--check-cpu", action="store_true", dest="check_cpu",
528
 
                   help=optparse.SUPPRESS_HELP)
529
 
 
530
 
# Register vnc + sdl options for virt-install and virt-image
531
 
def graphics_option_group(parser):
532
 
 
533
 
    vncg = OptionGroup(parser, _("Graphics Configuration"))
534
 
    vncg.add_option("", "--graphics", type="string", dest="graphics",
535
 
                    action="callback", callback=check_before_store,
536
 
      help=_("Specify display configuration. Ex:\n"
537
 
             "--graphics vnc\n"
538
 
             "--graphics spice,port=5901,tlsport=5902\n"
539
 
             "--graphics none\n"
540
 
             "--graphics vnc,password=foobar,port=5910,keymap=ja"))
541
 
    vncg.add_option("", "--vnc", action="store_true", dest="vnc",
542
 
                    help=optparse.SUPPRESS_HELP)
543
 
    vncg.add_option("", "--vncport", type="int", dest="vncport",
544
 
                    help=optparse.SUPPRESS_HELP)
545
 
    vncg.add_option("", "--vnclisten", type="string", dest="vnclisten",
546
 
                    help=optparse.SUPPRESS_HELP)
547
 
    vncg.add_option("-k", "--keymap", type="string", dest="keymap",
548
 
                    action="callback", callback=check_before_store,
549
 
                    help=optparse.SUPPRESS_HELP)
550
 
    vncg.add_option("", "--sdl", action="store_true", dest="sdl",
551
 
                    help=optparse.SUPPRESS_HELP)
552
 
    vncg.add_option("", "--nographics", action="store_true",
553
 
                    help=optparse.SUPPRESS_HELP)
554
 
    return vncg
 
598
 
555
599
 
556
600
# Specific function for disk prompting. Returns a validated VirtualDisk
557
601
# device.
558
602
#
559
 
def disk_prompt(prompt_txt, arg_dict, warn_overwrite=False, prompt_size=True,
560
 
                path_to_clone=None):
 
603
def disk_prompt(conn, origpath, origsize, origsparse,
 
604
                prompt_txt=None,
 
605
                warn_overwrite=False, check_size=True,
 
606
                path_to_clone=None, origdev=None):
561
607
 
 
608
    askmsg = _("Do you really want to use this disk (yes or no)")
562
609
    retry_path = True
563
 
    conn = arg_dict.get("conn")
564
 
    passed_path = arg_dict.get("path")
565
 
    size = arg_dict.get("size")
566
 
 
567
 
    no_path_needed = (bool(arg_dict.get("volInstall")) or
568
 
                      bool(arg_dict.get("volName")))
569
 
 
570
 
    while 1:
571
 
        if not retry_path:
572
 
            passed_path = None
573
 
            size = None
574
 
        retry_path = False
575
 
 
 
610
 
 
611
    no_path_needed = (origdev and
 
612
                      (origdev.vol_install or
 
613
                       origdev.vol_object or
 
614
                       origdev.can_be_empty()))
 
615
 
 
616
    def prompt_path(chkpath, chksize):
 
617
        """
 
618
        Prompt for disk path if necc
 
619
        """
576
620
        msg = None
577
621
        patherr = _("A disk path must be specified.")
578
622
        if path_to_clone:
581
625
 
582
626
        if not prompt_txt:
583
627
            msg = _("What would you like to use as the disk (file path)?")
584
 
            if not size is None:
 
628
            if not chksize is None:
585
629
                msg = _("Please enter the path to the file you would like to "
586
 
                        "use for storage. It will have size %sGB.") % size
 
630
                        "use for storage. It will have size %sGB.") % chksize
587
631
 
588
632
        if not no_path_needed:
589
 
            path = prompt_for_input(patherr, prompt_txt or msg, passed_path)
 
633
            path = prompt_for_input(patherr, prompt_txt or msg, chkpath)
590
634
        else:
591
635
            path = None
592
 
        arg_dict["path"] = path
593
 
        path_exists = VirtualDisk.path_exists(conn, path)
594
 
 
 
636
 
 
637
        return path
 
638
 
 
639
    def prompt_size(chkpath, chksize, path_exists):
 
640
        """
 
641
        Prompt for disk size if necc.
 
642
        """
595
643
        sizeerr = _("A size must be specified for non-existent disks.")
596
 
        if path and not size and prompt_size:
597
 
            size_prompt = _("How large would you like the disk (%s) to "
598
 
                            "be (in gigabytes)?") % path
599
 
 
600
 
            try:
601
 
                if not path_exists:
602
 
                    size = prompt_loop(size_prompt, sizeerr, size, None, None,
603
 
                                       func=float)
604
 
            except Exception, e:
605
 
                # Path is probably bogus, raise the error
606
 
                logging.error(str(e))
607
 
                continue
608
 
        arg_dict["size"] = size
609
 
 
610
 
        # Build disk object for validation
 
644
        size_prompt = _("How large would you like the disk (%s) to "
 
645
                        "be (in gigabytes)?") % chkpath
 
646
 
 
647
        if (not chkpath or
 
648
            path_exists or
 
649
            chksize is not None or
 
650
            not check_size):
 
651
            return False, chksize
 
652
 
611
653
        try:
612
 
            dev = VirtualDisk(**arg_dict)
613
 
        except ValueError, e:
614
 
            if is_prompt():
615
 
                logging.error(e)
616
 
                continue
617
 
            else:
618
 
                fail(_("Error with storage parameters: %s" % str(e)))
619
 
 
620
 
        askmsg = _("Do you really want to use this disk (yes or no)")
621
 
 
622
 
        # Prompt if disk file already exists and preserve mode is not used
 
654
            chksize = prompt_loop(size_prompt, sizeerr, chksize, None, None,
 
655
                               func=float)
 
656
            return False, chksize
 
657
        except Exception, e:
 
658
            # Path is probably bogus, raise the error
 
659
            fail(str(e), do_exit=not is_prompt())
 
660
            return True, chksize
 
661
 
 
662
    def prompt_path_exists(dev):
 
663
        """
 
664
        Prompt if disk file already exists and preserve mode is not used
 
665
        """
623
666
        does_collide = (path_exists and
624
667
                        dev.type == dev.TYPE_FILE and
625
668
                        dev.device == dev.DEVICE_DISK)
626
 
        if (does_collide and (warn_overwrite or is_prompt())):
627
 
            msg = (_("This will overwrite the existing path '%s'" %
628
 
                   dev.path))
629
 
            if not prompt_for_yes_or_no(msg, askmsg):
630
 
                continue
631
 
 
632
 
        # Check disk conflicts
633
 
        if dev.is_conflict_disk(conn) is True:
634
 
            msg = (_("Disk %s is already in use by another guest" %
635
 
                   dev.path))
636
 
            if not prompt_for_yes_or_no(msg, askmsg):
637
 
                continue
638
 
 
 
669
        msg = (_("This will overwrite the existing path '%s'" % dev.path))
 
670
 
 
671
        if not does_collide:
 
672
            return False
 
673
 
 
674
        if warn_overwrite or is_prompt():
 
675
            return not prompt_for_yes_or_no(msg, askmsg)
 
676
        return False
 
677
 
 
678
    def prompt_inuse_conflict(dev):
 
679
        """
 
680
        Check if disk is inuse by another guest
 
681
        """
 
682
        msg = (_("Disk %s is already in use by another guest" % dev.path))
 
683
 
 
684
        if not dev.is_conflict_disk(conn):
 
685
            return False
 
686
 
 
687
        return not prompt_for_yes_or_no(msg, askmsg)
 
688
 
 
689
    def prompt_size_conflict(dev):
 
690
        """
 
691
        Check if specified size exceeds available storage
 
692
        """
639
693
        isfatal, errmsg = dev.is_size_conflict()
640
694
        if isfatal:
641
 
            fail(errmsg)
642
 
        elif errmsg:
643
 
            if not prompt_for_yes_or_no(errmsg, askmsg):
 
695
            fail(errmsg, do_exit=not is_prompt())
 
696
            return True
 
697
 
 
698
        if errmsg:
 
699
            return not prompt_for_yes_or_no(errmsg, askmsg)
 
700
 
 
701
        return False
 
702
 
 
703
    while 1:
 
704
        # If we fail within the loop, reprompt for size and path
 
705
        if not retry_path:
 
706
            origpath = None
 
707
            origsize = None
 
708
        retry_path = False
 
709
 
 
710
        # Get disk path
 
711
        path = prompt_path(origpath, origsize)
 
712
        path_exists = VirtualDisk.path_exists(conn, path)
 
713
 
 
714
        # Get storage size
 
715
        didfail, size = prompt_size(path, origsize, path_exists)
 
716
        if didfail:
 
717
            continue
 
718
 
 
719
        # Build disk object for validation
 
720
        try:
 
721
            if origdev:
 
722
                dev = origdev
 
723
                if path is not None:
 
724
                    dev.path = path
 
725
                if size is not None:
 
726
                    dev.size = size
 
727
            else:
 
728
                dev = VirtualDisk(conn=conn, path=path, size=size,
 
729
                                  sparse=origsparse)
 
730
        except ValueError, e:
 
731
            if is_prompt():
 
732
                logging.error(e)
644
733
                continue
645
 
 
646
 
        # Passed all validation, return path
 
734
            else:
 
735
                fail(_("Error with storage parameters: %s" % str(e)))
 
736
 
 
737
        # Check if path exists
 
738
        if prompt_path_exists(dev):
 
739
            continue
 
740
 
 
741
        # Check disk in use by other guests
 
742
        if prompt_inuse_conflict(dev):
 
743
            continue
 
744
 
 
745
        # Check if disk exceeds available storage
 
746
        if prompt_size_conflict(dev):
 
747
            continue
 
748
 
 
749
        # Passed all validation, return disk instance
647
750
        return dev
648
 
#
649
 
# Ask for attributes
650
 
#
 
751
 
 
752
 
 
753
#######################
 
754
# Validation wrappers #
 
755
#######################
651
756
 
652
757
name_missing    = _("--name is required")
653
758
ram_missing     = _("--ram amount in MB is required")
683
788
        except ValueError, e:
684
789
            fail(e)
685
790
 
686
 
def _build_set_param(inst, opts):
687
 
    def _set_param(paramname, keyname, val=None):
688
 
        val = get_opt_param(opts, keyname, val)
689
 
        if val == None:
690
 
            return
691
 
        setattr(inst, paramname, val)
692
 
 
693
 
    return _set_param
694
 
 
695
 
def parse_vcpu_option(guest, optstring, default_vcpus):
696
 
    """
697
 
    Helper to parse --vcpu string
698
 
 
699
 
    @param  guest: virtinst.Guest instance (object)
700
 
    @param  optstring: value of the option '--vcpus' (str)
701
 
    @param  default_vcpus: ? (it should be None at present.)
702
 
    """
703
 
    vcpus, opts = parse_optstr(optstring, remove_first=True)
704
 
    vcpus = vcpus or default_vcpus
705
 
 
706
 
    set_param = _build_set_param(guest, opts)
707
 
    set_cpu_param = _build_set_param(guest.cpu, opts)
708
 
    has_vcpus = ("vcpus" in opts or (vcpus is not None))
709
 
 
710
 
    set_param("vcpus", "vcpus", vcpus)
711
 
    set_param("maxvcpus", "maxvcpus")
712
 
 
713
 
    set_cpu_param("sockets", "sockets")
714
 
    set_cpu_param("cores", "cores")
715
 
    set_cpu_param("threads", "threads")
716
 
 
717
 
    if not has_vcpus:
718
 
        guest.vcpus = guest.cpu.vcpus_from_topology()
719
 
 
720
 
    if opts:
721
 
        raise ValueError(_("Unknown options %s") % opts.keys())
722
 
 
723
 
 
724
 
def get_vcpus(vcpus, check_cpu, guest, image_vcpus=None):
 
791
def get_vcpus(guest, vcpus, check_cpu, image_vcpus=None):
725
792
    """
726
793
    @param vcpus: value of the option '--vcpus' (str or None)
727
794
    @param check_cpu: Whether to check that the number virtual cpus requested
735
802
        else:
736
803
            vcpus = ""
737
804
 
738
 
    parse_vcpu_option(guest, vcpus, image_vcpus)
 
805
    parse_vcpu(guest, vcpus, image_vcpus)
739
806
 
740
807
    if check_cpu:
741
808
        hostinfo = guest.conn.getInfo()
750
817
            if not prompt_for_yes_or_no(msg, askmsg):
751
818
                nice_exit()
752
819
 
753
 
 
754
 
def get_cpuset(cpuset, mem, guest):
 
820
def get_cpuset(guest, cpuset):
755
821
    conn = guest.conn
756
822
    if cpuset and cpuset != "auto":
757
823
        guest.cpuset = cpuset
759
825
    elif cpuset == "auto":
760
826
        tmpset = None
761
827
        try:
762
 
            tmpset = Guest.generate_cpuset(conn, mem)
 
828
            tmpset = Guest.generate_cpuset(conn, guest.memory)
763
829
        except Exception, e:
764
830
            logging.debug("Not setting cpuset", str(e))
765
831
 
769
835
 
770
836
    return
771
837
 
 
838
def _default_network_opts(guest):
 
839
    opts = ""
 
840
    if User.current().has_priv(User.PRIV_CREATE_NETWORK, guest.get_uri()):
 
841
        net = _util.default_network(guest.conn)
 
842
        opts = "%s=%s" % (net[0], net[1])
 
843
    else:
 
844
        opts = "user"
 
845
 
 
846
    return opts
 
847
 
 
848
def digest_networks(guest, options, numnics=1):
 
849
    macs     = listify(options.mac)
 
850
    networks = listify(options.network)
 
851
    bridges  = listify(options.bridge)
 
852
 
 
853
    if bridges and networks:
 
854
        fail(_("Cannot mix both --bridge and --network arguments"))
 
855
 
 
856
    if bridges:
 
857
        # Convert old --bridges to --networks
 
858
        networks = map(lambda b: "bridge:" + b, bridges)
 
859
 
 
860
    def padlist(l, padsize):
 
861
        l = listify(l)
 
862
        l.extend((padsize - len(l)) * [None])
 
863
        return l
 
864
 
 
865
    # If a plain mac is specified, have it imply a default network
 
866
    networks = padlist(networks, max(len(macs), numnics))
 
867
    macs = padlist(macs, len(networks))
 
868
 
 
869
    for idx in range(len(networks)):
 
870
        if networks[idx] is None:
 
871
            networks[idx] = _default_network_opts(guest)
 
872
 
 
873
    return networks, macs
 
874
 
 
875
def get_networks(guest, networks, macs):
 
876
    for idx in range(len(networks)):
 
877
        mac = macs[idx]
 
878
        netstr = networks[idx]
 
879
 
 
880
        try:
 
881
            dev = parse_network(guest, netstr, mac=mac)
 
882
            guest.add_device(dev)
 
883
        except Exception, e:
 
884
            fail(_("Error in network device parameters: %s") % str(e))
 
885
 
 
886
def set_os_variant(guest, distro_type, distro_variant):
 
887
    if not distro_type and not distro_variant:
 
888
        # Default to distro autodetection
 
889
        guest.set_os_autodetect(True)
 
890
        return
 
891
 
 
892
    if (distro_type and str(distro_type).lower() != "none"):
 
893
        guest.set_os_type(distro_type)
 
894
 
 
895
    if (distro_variant and str(distro_variant).lower() != "none"):
 
896
        guest.set_os_variant(distro_variant)
 
897
 
 
898
def digest_graphics(guest, options, default_override=None):
 
899
    vnc = options.vnc
 
900
    vncport = options.vncport
 
901
    vnclisten = options.vnclisten
 
902
    nographics = options.nographics
 
903
    sdl = options.sdl
 
904
    keymap = options.keymap
 
905
    graphics = options.graphics
 
906
 
 
907
    if graphics and (vnc or sdl or keymap or vncport or vnclisten):
 
908
        fail(_("Cannot mix --graphics and old style graphical options"))
 
909
 
 
910
    optnum = sum(map(bool, [vnc, nographics, sdl, graphics]))
 
911
    if optnum > 1:
 
912
        raise ValueError(_("Can't specify more than one of VNC, SDL, "
 
913
                           "--graphics or --nographics"))
 
914
 
 
915
    if graphics:
 
916
        return graphics
 
917
 
 
918
    if optnum == 0:
 
919
        # If no graphics specified, choose a default
 
920
        if default_override is True:
 
921
            vnc = True
 
922
        elif default_override is False:
 
923
            nographics = True
 
924
        else:
 
925
            if guest.installer.is_container():
 
926
                logging.debug("Container guest, defaulting to nographics")
 
927
                nographics = True
 
928
            elif "DISPLAY" in os.environ.keys():
 
929
                logging.debug("DISPLAY is set: graphics defaulting to VNC.")
 
930
                vnc = True
 
931
            else:
 
932
                logging.debug("DISPLAY is not set: defaulting to nographics.")
 
933
                nographics = True
 
934
 
 
935
 
 
936
    # Build a --graphics command line from old style opts
 
937
    optstr = ((vnc and "vnc") or
 
938
              (sdl and "sdl") or
 
939
              (nographics and ("none")))
 
940
    if vnclisten:
 
941
        optstr += ",listen=%s" % vnclisten
 
942
    if vncport:
 
943
        optstr += ",port=%s" % vncport
 
944
    if keymap:
 
945
        optstr += ",keymap=%s" % keymap
 
946
 
 
947
    logging.debug("--graphics compat generated: %s" % optstr)
 
948
    return [optstr]
 
949
 
 
950
def get_graphics(guest, graphics):
 
951
    for optstr in graphics:
 
952
        try:
 
953
            dev = parse_graphics(guest, optstr)
 
954
        except Exception, e:
 
955
            fail(_("Error in graphics device parameters: %s") % str(e))
 
956
 
 
957
        if dev:
 
958
            guest.add_device(dev)
 
959
 
 
960
def get_video(guest, video_models=None):
 
961
    video_models = video_models or []
 
962
 
 
963
    if guest.get_devices(VirtualDevice.VIRTUAL_DEV_GRAPHICS):
 
964
        if not video_models:
 
965
            video_models.append(None)
 
966
 
 
967
    for model in video_models:
 
968
        guest.add_device(parse_video(guest, model))
 
969
 
 
970
def get_sound(old_sound_bool, sound_opts, guest):
 
971
    if not sound_opts:
 
972
        if old_sound_bool:
 
973
            # Use os default
 
974
            guest.sound_devs.append(VirtualAudio(conn=guest.conn))
 
975
        return
 
976
 
 
977
    for opts in listify(sound_opts):
 
978
        guest.add_device(parse_sound(guest, opts))
 
979
 
 
980
def get_hostdevs(hostdevs, guest):
 
981
    if not hostdevs:
 
982
        return
 
983
 
 
984
    for devname in hostdevs:
 
985
        guest.add_device(parse_hostdev(guest, devname))
 
986
 
 
987
def get_smartcard(guest, sc_opts):
 
988
    for sc in listify(sc_opts):
 
989
        try:
 
990
            dev = parse_smartcard(guest, sc)
 
991
        except Exception, e:
 
992
            fail(_("Error in smartcard device parameters: %s") % str(e))
 
993
 
 
994
        if dev:
 
995
            guest.add_device(dev)
 
996
 
 
997
#############################
 
998
# Common CLI option/group   #
 
999
#############################
 
1000
 
 
1001
def add_connect_option(parser):
 
1002
    parser.add_option("", "--connect", metavar="URI", dest="connect",
 
1003
                      help=_("Connect to hypervisor with libvirt URI"))
 
1004
 
 
1005
def vcpu_cli_options(grp, backcompat=True):
 
1006
    grp.add_option("", "--vcpus", dest="vcpus",
 
1007
        help=_("Number of vcpus to configure for your guest. Ex:\n"
 
1008
               "--vcpus 5\n"
 
1009
               "--vcpus 5,maxcpus=10\n"
 
1010
               "--vcpus sockets=2,cores=4,threads=2"))
 
1011
    grp.add_option("", "--cpuset", dest="cpuset",
 
1012
                   help=_("Set which physical CPUs domain can use."))
 
1013
    grp.add_option("", "--cpu", dest="cpu",
 
1014
        help=_("CPU model and features. Ex: --cpu coreduo,+x2apic"))
 
1015
 
 
1016
    if backcompat:
 
1017
        grp.add_option("", "--check-cpu", action="store_true",
 
1018
                       dest="check_cpu", help=optparse.SUPPRESS_HELP)
 
1019
 
 
1020
def graphics_option_group(parser):
 
1021
    """
 
1022
    Register vnc + sdl options for virt-install and virt-image
 
1023
    """
 
1024
 
 
1025
    vncg = optparse.OptionGroup(parser, _("Graphics Configuration"))
 
1026
    add_gfx_option(vncg)
 
1027
    vncg.add_option("", "--vnc", action="store_true", dest="vnc",
 
1028
                    help=optparse.SUPPRESS_HELP)
 
1029
    vncg.add_option("", "--vncport", type="int", dest="vncport",
 
1030
                    help=optparse.SUPPRESS_HELP)
 
1031
    vncg.add_option("", "--vnclisten", dest="vnclisten",
 
1032
                    help=optparse.SUPPRESS_HELP)
 
1033
    vncg.add_option("-k", "--keymap", dest="keymap",
 
1034
                    help=optparse.SUPPRESS_HELP)
 
1035
    vncg.add_option("", "--sdl", action="store_true", dest="sdl",
 
1036
                    help=optparse.SUPPRESS_HELP)
 
1037
    vncg.add_option("", "--nographics", action="store_true",
 
1038
                    help=optparse.SUPPRESS_HELP)
 
1039
    return vncg
 
1040
 
 
1041
def network_option_group(parser):
 
1042
    """
 
1043
    Register common network options for virt-install and virt-image
 
1044
    """
 
1045
    netg = optparse.OptionGroup(parser, _("Networking Configuration"))
 
1046
 
 
1047
    add_net_option(netg)
 
1048
 
 
1049
    # Deprecated net options
 
1050
    netg.add_option("-b", "--bridge", dest="bridge", action="append",
 
1051
                    help=optparse.SUPPRESS_HELP)
 
1052
    netg.add_option("-m", "--mac", dest="mac", action="append",
 
1053
                    help=optparse.SUPPRESS_HELP)
 
1054
 
 
1055
    return netg
 
1056
 
 
1057
def add_net_option(devg):
 
1058
    devg.add_option("-w", "--network", dest="network", action="append",
 
1059
      help=_("Configure a guest network interface. Ex:\n"
 
1060
             "--network bridge=mybr0\n"
 
1061
             "--network network=my_libvirt_virtual_net\n"
 
1062
             "--network network=mynet,model=virtio,mac=00:11..."))
 
1063
 
 
1064
def add_device_options(devg):
 
1065
    devg.add_option("", "--serial", dest="serials", action="append",
 
1066
                    help=_("Configure a guest serial device"))
 
1067
    devg.add_option("", "--parallel", dest="parallels", action="append",
 
1068
                    help=_("Configure a guest parallel device"))
 
1069
    devg.add_option("", "--channel", dest="channels", action="append",
 
1070
                    help=_("Configure a guest communication channel"))
 
1071
    devg.add_option("", "--console", dest="consoles", action="append",
 
1072
                    help=_("Configure a text console connection between "
 
1073
                           "the guest and host"))
 
1074
    devg.add_option("", "--host-device", dest="hostdevs", action="append",
 
1075
                    help=_("Configure physical host devices attached to the "
 
1076
                           "guest"))
 
1077
    devg.add_option("", "--soundhw", dest="soundhw", action="append",
 
1078
                    help=_("Configure guest sound device emulation"))
 
1079
    devg.add_option("", "--watchdog", dest="watchdog", action="append",
 
1080
                    help=_("Configure a guest watchdog device"))
 
1081
    devg.add_option("", "--video", dest="video", action="append",
 
1082
                    help=_("Configure guest video hardware."))
 
1083
    devg.add_option("", "--smartcard", dest="smartcard", action="append",
 
1084
                    help=_("Configure a guest smartcard device. Ex:\n"
 
1085
                           "--smartcard mode=passthrough"))
 
1086
 
 
1087
def add_gfx_option(devg):
 
1088
    devg.add_option("", "--graphics", dest="graphics", action="append",
 
1089
      help=_("Configure guest display settings. Ex:\n"
 
1090
             "--graphics vnc\n"
 
1091
             "--graphics spice,port=5901,tlsport=5902\n"
 
1092
             "--graphics none\n"
 
1093
             "--graphics vnc,password=foobar,port=5910,keymap=ja"))
 
1094
 
 
1095
def add_fs_option(devg):
 
1096
    devg.add_option("", "--filesystem", dest="filesystems", action="append",
 
1097
        help=_("Pass host directory to the guest. Ex: \n"
 
1098
               "--filesystem /my/source/dir,/dir/in/guest\n"
 
1099
               "--filesystem template_name,/,type=template"))
 
1100
 
 
1101
#############################################
 
1102
# CLI complex parsing helpers               #
 
1103
# (for options like --disk, --network, etc. #
 
1104
#############################################
 
1105
 
 
1106
def get_opt_param(opts, dictnames, val=None):
 
1107
    if type(dictnames) is not list:
 
1108
        dictnames = [dictnames]
 
1109
 
 
1110
    for key in dictnames:
 
1111
        if key in opts:
 
1112
            if val == None:
 
1113
                val = opts[key]
 
1114
            del(opts[key])
 
1115
 
 
1116
    return val
 
1117
 
 
1118
def _build_set_param(inst, opts):
 
1119
    def _set_param(paramname, keyname, val=None):
 
1120
        val = get_opt_param(opts, keyname, val)
 
1121
        if val == None:
 
1122
            return
 
1123
        setattr(inst, paramname, val)
 
1124
 
 
1125
    return _set_param
 
1126
 
 
1127
def parse_optstr_tuples(optstr, compress_first=False):
 
1128
    """
 
1129
    Parse optstr into a list of ordered tuples
 
1130
    """
 
1131
    optstr = str(optstr or "")
 
1132
    optlist = []
 
1133
 
 
1134
    if compress_first and optstr and not optstr.count("="):
 
1135
        return [(optstr, None)]
 
1136
 
 
1137
    argsplitter = shlex.shlex(optstr, posix=True)
 
1138
    argsplitter.whitespace = ","
 
1139
    argsplitter.whitespace_split = True
 
1140
 
 
1141
    for opt in list(argsplitter):
 
1142
        if not opt:
 
1143
            continue
 
1144
 
 
1145
        opt_type = None
 
1146
        opt_val = None
 
1147
        if opt.count("="):
 
1148
            opt_type, opt_val = opt.split("=", 1)
 
1149
            optlist.append((opt_type.lower(), opt_val))
 
1150
        else:
 
1151
            optlist.append((opt.lower(), None))
 
1152
 
 
1153
    return optlist
 
1154
 
 
1155
def parse_optstr(optstr, basedict=None, remove_first=None,
 
1156
                 compress_first=False):
 
1157
    """
 
1158
    Helper function for parsing opt strings of the form
 
1159
    opt1=val1,opt2=val2,...
 
1160
 
 
1161
    @param basedict: starting dictionary, so the caller can easily set
 
1162
                     default values, etc.
 
1163
    @param remove_first: List or parameters to peel off the front of
 
1164
                         option string, and store in the returned dict.
 
1165
                         remove_first=["char_type"] for --serial pty,foo=bar
 
1166
                         returns {"char_type", "pty", "foo" : "bar"}
 
1167
    @param compress_first: If there are no options of the form opt1=opt2,
 
1168
                           compress the string to a single option
 
1169
 
 
1170
    Returns a dictionary of {'opt1': 'val1', 'opt2': 'val2'}
 
1171
    """
 
1172
    optlist = parse_optstr_tuples(optstr, compress_first=compress_first)
 
1173
    optdict = basedict or {}
 
1174
 
 
1175
    paramlist = remove_first
 
1176
    if type(paramlist) is not list:
 
1177
        paramlist = paramlist and [paramlist] or []
 
1178
 
 
1179
    for idx in range(len(paramlist)):
 
1180
        if len(optlist) < len(paramlist):
 
1181
            break
 
1182
 
 
1183
        if optlist[idx][1] == None:
 
1184
            optlist[idx] = (paramlist[idx], optlist[idx][0])
 
1185
 
 
1186
    for opt, val in optlist:
 
1187
        if type(optdict.get(opt)) is list:
 
1188
            optdict[opt].append(val)
 
1189
        else:
 
1190
            optdict[opt] = val
 
1191
 
 
1192
    return optdict
 
1193
 
 
1194
 
 
1195
 
 
1196
#######################
 
1197
# Guest param parsing #
 
1198
#######################
 
1199
 
 
1200
######################
 
1201
# --numatune parsing #
 
1202
######################
 
1203
 
 
1204
def parse_numatune(guest, optstring):
 
1205
    """
 
1206
    Helper to parse --numatune string
 
1207
 
 
1208
    @param  guest: virtinst.Guest instanct (object)
 
1209
    @param  optstring: value of the option '--numatune' (str)
 
1210
    """
 
1211
    opts = parse_optstr(optstring, remove_first="nodeset", compress_first=True)
 
1212
 
 
1213
    set_param = _build_set_param(guest.numatune, opts)
 
1214
 
 
1215
    set_param("memory_nodeset", "nodeset")
 
1216
    set_param("memory_mode", "mode")
 
1217
 
 
1218
    if opts:
 
1219
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1220
 
 
1221
##################
 
1222
# --vcpu parsing #
 
1223
##################
 
1224
 
 
1225
def parse_vcpu(guest, optstring, default_vcpus=None):
 
1226
    """
 
1227
    Helper to parse --vcpu string
 
1228
 
 
1229
    @param  guest: virtinst.Guest instance (object)
 
1230
    @param  optstring: value of the option '--vcpus' (str)
 
1231
    @param  default_vcpus: ? (it should be None at present.)
 
1232
    """
 
1233
    if not optstring:
 
1234
        return
 
1235
 
 
1236
    opts = parse_optstr(optstring, remove_first="vcpus")
 
1237
    vcpus = opts.get("vcpus") or default_vcpus
 
1238
    if vcpus is not None:
 
1239
        opts["vcpus"] = vcpus
 
1240
 
 
1241
    set_param = _build_set_param(guest, opts)
 
1242
    set_cpu_param = _build_set_param(guest.cpu, opts)
 
1243
    has_vcpus = ("vcpus" in opts or (vcpus is not None))
 
1244
 
 
1245
    set_param("vcpus", "vcpus")
 
1246
    set_param("maxvcpus", "maxvcpus")
 
1247
 
 
1248
    set_cpu_param("sockets", "sockets")
 
1249
    set_cpu_param("cores", "cores")
 
1250
    set_cpu_param("threads", "threads")
 
1251
 
 
1252
    if not has_vcpus:
 
1253
        guest.vcpus = guest.cpu.vcpus_from_topology()
 
1254
 
 
1255
    if opts:
 
1256
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1257
 
 
1258
#################
 
1259
# --cpu parsing #
 
1260
#################
 
1261
 
772
1262
def parse_cpu(guest, optstring):
773
1263
    default_dict = {
774
1264
        "force": [],
777
1267
        "disable": [],
778
1268
        "forbid": [],
779
1269
    }
780
 
    model, opts = parse_optstr(optstring,
781
 
                               basedict=default_dict,
782
 
                               remove_first=True)
 
1270
    opts = parse_optstr(optstring,
 
1271
                        basedict=default_dict,
 
1272
                        remove_first="model")
783
1273
 
784
1274
    # Convert +feature, -feature into expected format
785
1275
    for key, value in opts.items():
802
1292
            guest.cpu.add_feature(name, policy)
803
1293
        del(opts[policy])
804
1294
 
805
 
    if model == "host":
 
1295
    if opts.get("model") == "host":
806
1296
        guest.cpu.copy_host_cpu()
807
 
        model = None
 
1297
        del(opts["model"])
808
1298
 
809
 
    set_param("model", "model", model)
 
1299
    set_param("model", "model")
810
1300
    set_param("match", "match")
811
1301
    set_param("vendor", "vendor")
812
1302
 
819
1309
    if opts:
820
1310
        raise ValueError(_("Unknown options %s") % opts.keys())
821
1311
 
822
 
def get_network(net_kwargs, guest):
823
 
    n = VirtualNetworkInterface(**net_kwargs)
824
 
    guest.nics.append(n)
825
 
 
826
 
def set_os_variant(guest, distro_type, distro_variant):
827
 
    if not distro_type and not distro_variant:
828
 
        # Default to distro autodetection
829
 
        guest.set_os_autodetect(True)
830
 
    else:
831
 
        if (distro_type and str(distro_type).lower() != "none"):
832
 
            guest.set_os_type(distro_type)
833
 
 
834
 
        if (distro_variant and str(distro_variant).lower() != "none"):
835
 
            guest.set_os_variant(distro_variant)
836
 
 
837
 
def parse_optstr_tuples(optstr):
838
 
    """
839
 
    Parse optstr into a list of ordered tuples
840
 
    """
841
 
    optstr = str(optstr or "")
842
 
    optlist = []
843
 
 
844
 
    args = optstr.split(",")
845
 
    for opt in args:
846
 
        if not opt:
847
 
            continue
848
 
 
849
 
        opt_type = None
850
 
        opt_val = None
851
 
        if opt.count("="):
852
 
            opt_type, opt_val = opt.split("=", 1)
853
 
            optlist.append((opt_type.lower(), opt_val))
854
 
        else:
855
 
            optlist.append((opt.lower(), None))
856
 
 
857
 
    return optlist
858
 
 
859
 
def parse_optstr(optstr, basedict=None, remove_first=False):
860
 
    """
861
 
    Helper function for parsing opt strings of the form
862
 
    opt1=val1,opt2=val2,...
863
 
 
864
 
    @param basedict: starting dictionary, so the caller can easily set
865
 
                     default values, etc.
866
 
    @param remove_first: If true, remove the first options off the string
867
 
                         and return it seperately. For example,
868
 
                         --serial pty,foo=bar returns ("pty", {"foo" : "bar"})
869
 
 
870
 
    Returns a dictionary of {'opt1': 'val1', 'opt2': 'val2'}
871
 
    """
872
 
    optlist = parse_optstr_tuples(optstr)
873
 
    optdict = basedict or {}
874
 
    first = None
875
 
 
876
 
    if remove_first and optlist:
877
 
        first_tuple = optlist[0]
878
 
        if first_tuple[1] == None:
879
 
            first = first_tuple[0]
880
 
            optlist.remove(first_tuple)
881
 
 
882
 
    for opt, val in optlist:
883
 
        if type(optdict.get(opt)) is list:
884
 
            optdict[opt].append(val)
885
 
        else:
886
 
            optdict[opt] = val
887
 
 
888
 
    return (first, optdict)
889
 
 
890
 
def parse_network_opts(conn, mac, network):
891
 
    net_type = None
892
 
    network_name = None
893
 
    bridge_name = None
894
 
    model = None
895
 
    option_whitelist = ["model", "mac"]
896
 
 
897
 
    args = network.split(",")
898
 
 
899
 
    # Determine net type and bridge vs. network
900
 
    netdata = None
901
 
    typestr = args[0]
902
 
    del(args[0])
903
 
 
904
 
    if typestr.count(":"):
905
 
        net_type, netdata = typestr.split(":", 1)
906
 
    elif typestr.count("="):
907
 
        net_type, netdata = typestr.split("=", 1)
908
 
    else:
909
 
        net_type = typestr
910
 
 
911
 
    if net_type == VirtualNetworkInterface.TYPE_VIRTUAL:
912
 
        network_name = netdata
913
 
    elif net_type == VirtualNetworkInterface.TYPE_BRIDGE:
914
 
        bridge_name = netdata
915
 
 
916
 
    # Pass the remaining arg=value pairs
917
 
    ignore, opts = parse_optstr(",".join(args))
918
 
    for opt_type, ignore_val in opts.items():
919
 
        if opt_type not in option_whitelist:
920
 
            fail(_("Unknown network option '%s'") % opt_type)
921
 
 
922
 
    model   = opts.get("model")
923
 
    mac     = opts.get("mac") or mac
924
 
 
925
 
    # The keys here correspond to parameter names for VirtualNetworkInterface
926
 
    # __init__
927
 
    if mac == "RANDOM":
928
 
        mac = None
929
 
    return {"conn" : conn, "type" : net_type, "bridge": bridge_name,
930
 
            "network" : network_name, "model" : model , "macaddr" : mac}
931
 
 
932
 
def digest_networks(conn, macs, bridges, networks, nics=0):
933
 
    macs     = listify(macs)
934
 
    bridges  = listify(bridges)
935
 
    networks = listify(networks)
936
 
 
937
 
    if bridges and networks:
938
 
        fail(_("Cannot mix both --bridge and --network arguments"))
939
 
 
940
 
    if bridges:
941
 
        networks = map(lambda b: "bridge:" + b, bridges)
942
 
 
943
 
    # With just one mac, create a default network if one is not specified.
944
 
    if len(macs) == 1 and len(networks) == 0:
945
 
        if User.current().has_priv(User.PRIV_CREATE_NETWORK, conn.getURI()):
946
 
            net = _util.default_network(conn)
947
 
            networks.append(net[0] + ":" + net[1])
948
 
        else:
949
 
            networks.append(VirtualNetworkInterface.TYPE_USER)
950
 
 
951
 
    # ensure we have less macs then networks, otherwise autofill the mac list
952
 
    if len(macs) > len(networks):
953
 
        fail(_("Cannot pass more mac addresses than networks."))
954
 
    else:
955
 
        for dummy in range(len(macs), len(networks)):
956
 
            macs.append(None)
957
 
 
958
 
    # Create extra networks up to the number of nics requested
959
 
    if len(macs) < nics:
960
 
        for dummy in range(len(macs), nics):
961
 
            if User.current().has_priv(User.PRIV_CREATE_NETWORK, conn.getURI()):
962
 
                net = _util.default_network(conn)
963
 
                networks.append(net[0] + ":" + net[1])
964
 
            else:
965
 
                networks.append(VirtualNetworkInterface.TYPE_USER)
966
 
            macs.append(None)
967
 
 
968
 
    net_init_dicts = []
969
 
    for i in range(0, len(networks)):
970
 
        mac = macs[i]
971
 
        netstr = networks[i]
972
 
        net_init_dicts.append(parse_network_opts(conn, mac, netstr))
973
 
 
974
 
    return net_init_dicts
975
 
 
976
 
def sanitize_keymap(keymap):
977
 
    if not keymap:
 
1312
##################
 
1313
# --boot parsing #
 
1314
##################
 
1315
 
 
1316
def parse_boot(guest, optstring):
 
1317
    """
 
1318
    Helper to parse --boot string
 
1319
    """
 
1320
    opts = parse_optstr(optstring)
 
1321
    optlist = map(lambda x: x[0], parse_optstr_tuples(optstring))
 
1322
    menu = None
 
1323
 
 
1324
    def set_param(paramname, dictname, val=None):
 
1325
        val = get_opt_param(opts, dictname, val)
 
1326
        if val == None:
 
1327
            return
 
1328
 
 
1329
        setattr(guest.installer.bootconfig, paramname, val)
 
1330
 
 
1331
    # Convert menu= value
 
1332
    if "menu" in opts:
 
1333
        menustr = opts["menu"]
 
1334
        menu = None
 
1335
 
 
1336
        if menustr.lower() == "on":
 
1337
            menu = True
 
1338
        elif menustr.lower() == "off":
 
1339
            menu = False
 
1340
        else:
 
1341
            menu = yes_or_no_convert(menustr)
 
1342
 
 
1343
        if menu == None:
 
1344
            fail(_("--boot menu must be 'on' or 'off'"))
 
1345
 
 
1346
    set_param("enable_bootmenu", "menu", menu)
 
1347
    set_param("kernel", "kernel")
 
1348
    set_param("initrd", "initrd")
 
1349
    set_param("kernel_args", ["kernel_args", "extra_args"])
 
1350
 
 
1351
    # Build boot order
 
1352
    if opts:
 
1353
        boot_order = []
 
1354
        for boot_dev in optlist:
 
1355
            if not boot_dev in guest.installer.bootconfig.boot_devices:
 
1356
                continue
 
1357
 
 
1358
            del(opts[boot_dev])
 
1359
            if boot_dev not in boot_order:
 
1360
                boot_order.append(boot_dev)
 
1361
 
 
1362
        guest.installer.bootconfig.bootorder = boot_order
 
1363
 
 
1364
    if opts:
 
1365
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1366
 
 
1367
######################
 
1368
# --security parsing #
 
1369
######################
 
1370
 
 
1371
def parse_security(guest, security):
 
1372
    seclist = listify(security)
 
1373
    secopts = seclist and seclist[0] or None
 
1374
    if not secopts:
 
1375
        return
 
1376
 
 
1377
    # Parse security opts
 
1378
    opts = parse_optstr(secopts)
 
1379
    arglist = secopts.split(",")
 
1380
    secmodel = guest.seclabel
 
1381
 
 
1382
    # Beware, adding boolean options here could upset label comma handling
 
1383
    mode = get_opt_param(opts, "type")
 
1384
    label = get_opt_param(opts, "label")
 
1385
 
 
1386
    # Try to fix up label if it contained commas
 
1387
    if label:
 
1388
        tmparglist = arglist[:]
 
1389
        for idx in range(len(tmparglist)):
 
1390
            arg = tmparglist[idx]
 
1391
            if not arg.split("=")[0] == "label":
 
1392
                continue
 
1393
 
 
1394
            for arg in tmparglist[idx + 1:]:
 
1395
                if arg.count("="):
 
1396
                    break
 
1397
 
 
1398
                if arg:
 
1399
                    label += "," + arg
 
1400
                    del(opts[arg])
 
1401
 
 
1402
            break
 
1403
 
 
1404
    if label:
 
1405
        secmodel.label = label
 
1406
        if not mode:
 
1407
            mode = secmodel.SECLABEL_TYPE_STATIC
 
1408
    if mode:
 
1409
        secmodel.type = mode
 
1410
 
 
1411
    if opts:
 
1412
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1413
 
 
1414
    # Run for validation purposes
 
1415
    secmodel.get_xml_config()
 
1416
 
 
1417
 
 
1418
 
 
1419
##########################
 
1420
# Guest <device> parsing #
 
1421
##########################
 
1422
 
 
1423
 
 
1424
##################
 
1425
# --disk parsing #
 
1426
##################
 
1427
 
 
1428
def _parse_disk_source(guest, path, pool, vol, size, fmt, sparse):
 
1429
    abspath = None
 
1430
    volinst = None
 
1431
    volobj = None
 
1432
 
 
1433
    # Strip media type
 
1434
    if sum(map(int, map(bool, [path, pool, vol]))) > 1:
 
1435
        fail(_("Cannot specify more than 1 storage path"))
 
1436
 
 
1437
    if path:
 
1438
        abspath = os.path.abspath(path)
 
1439
        if os.path.dirname(abspath) == DEFAULT_POOL_PATH:
 
1440
            build_default_pool(guest)
 
1441
 
 
1442
    elif pool:
 
1443
        if not size:
 
1444
            raise ValueError(_("Size must be specified with all 'pool='"))
 
1445
        if pool == DEFAULT_POOL_NAME:
 
1446
            build_default_pool(guest)
 
1447
        vc = virtinst.Storage.StorageVolume.get_volume_for_pool(pool_name=pool,
 
1448
                                                                conn=guest.conn)
 
1449
        vname = virtinst.Storage.StorageVolume.find_free_name(conn=guest.conn,
 
1450
                                                              pool_name=pool,
 
1451
                                                              name=guest.name,
 
1452
                                                              suffix=".img")
 
1453
        volinst = vc(pool_name=pool, name=vname, conn=guest.conn,
 
1454
                     allocation=0, capacity=(size and
 
1455
                                             size * 1024 * 1024 * 1024))
 
1456
        if fmt:
 
1457
            if not hasattr(volinst, "format"):
 
1458
                raise ValueError(_("Format attribute not supported for this "
 
1459
                                   "volume type"))
 
1460
            setattr(volinst, "format", fmt)
 
1461
 
 
1462
        if not sparse:
 
1463
            volinst.allocation = volinst.capacity
 
1464
 
 
1465
    elif vol:
 
1466
        if not vol.count("/"):
 
1467
            raise ValueError(_("Storage volume must be specified as "
 
1468
                               "vol=poolname/volname"))
 
1469
        vollist = vol.split("/")
 
1470
        voltuple = (vollist[0], vollist[1])
 
1471
        logging.debug("Parsed volume: as pool='%s' vol='%s'" %
 
1472
                      (voltuple[0], voltuple[1]))
 
1473
        if voltuple[0] == DEFAULT_POOL_NAME:
 
1474
            build_default_pool(guest)
 
1475
 
 
1476
        volobj = virtinst.VirtualDisk.lookup_vol_object(guest.conn, voltuple)
 
1477
 
 
1478
    return abspath, volinst, volobj
 
1479
 
 
1480
def parse_disk(guest, optstr, dev=None):
 
1481
    """
 
1482
    helper to properly parse --disk options
 
1483
    """
 
1484
    def parse_perms(val):
 
1485
        ro = False
 
1486
        shared = False
 
1487
        if val is not None:
 
1488
            if val == "ro":
 
1489
                ro = True
 
1490
            elif val == "sh":
 
1491
                shared = True
 
1492
            elif val == "rw":
 
1493
                # It's default. Nothing to do.
 
1494
                pass
 
1495
            else:
 
1496
                fail(_("Unknown '%s' value '%s'" % ("perms", val)))
 
1497
 
 
1498
        return ro, shared
 
1499
 
 
1500
    def parse_size(val):
 
1501
        newsize = None
 
1502
        if val is not None:
 
1503
            try:
 
1504
                newsize = float(val)
 
1505
            except Exception, e:
 
1506
                fail(_("Improper value for 'size': %s" % str(e)))
 
1507
 
 
1508
        return newsize
 
1509
 
 
1510
    def parse_sparse(val):
 
1511
        sparse = True
 
1512
        if val is not None:
 
1513
            val = str(val).lower()
 
1514
            if val in ["true", "yes"]:
 
1515
                sparse = True
 
1516
            elif val in ["false", "no"]:
 
1517
                sparse = False
 
1518
            else:
 
1519
                fail(_("Unknown '%s' value '%s'") % ("sparse", val))
 
1520
 
 
1521
        return sparse
 
1522
 
 
1523
    def opt_get(key):
 
1524
        val = None
 
1525
        if key in opts:
 
1526
            val = opts.get(key)
 
1527
            del(opts[key])
 
1528
 
 
1529
        return val
 
1530
 
 
1531
    # Parse out comma separated options
 
1532
    opts = parse_optstr(optstr, remove_first="path")
 
1533
 
 
1534
    # We annoyingly need these params ahead of time to deal with
 
1535
    # VirtualDisk validation
 
1536
    path = opt_get("path")
 
1537
    pool = opt_get("pool")
 
1538
    vol = opt_get("vol")
 
1539
    size = parse_size(opt_get("size"))
 
1540
    fmt = opt_get("format")
 
1541
    sparse = parse_sparse(opt_get("sparse"))
 
1542
    ro, shared = parse_perms(opt_get("perms"))
 
1543
    device = opt_get("device")
 
1544
 
 
1545
    abspath, volinst, volobj = _parse_disk_source(guest, path, pool, vol,
 
1546
                                                  size, fmt, sparse)
 
1547
 
 
1548
    if not dev:
 
1549
        # Build a stub device that should always validate cleanly
 
1550
        dev = virtinst.VirtualDisk(conn=guest.conn,
 
1551
                                   path=abspath,
 
1552
                                   volObject=volobj,
 
1553
                                   volInstall=volinst,
 
1554
                                   size=size,
 
1555
                                   readOnly=ro,
 
1556
                                   shareable=shared,
 
1557
                                   device=device,
 
1558
                                   format=fmt)
 
1559
 
 
1560
    set_param = _build_set_param(dev, opts)
 
1561
 
 
1562
    set_param("path", "path", abspath)
 
1563
    set_param("vol", "vol_object", volobj)
 
1564
    set_param("pool", "vol_install", volinst)
 
1565
    set_param("size", "size", size)
 
1566
    set_param("format", "format", fmt)
 
1567
    set_param("sparse", "sparse", sparse)
 
1568
    set_param("read_only", "perms", ro)
 
1569
    set_param("shareable", "perms", shared)
 
1570
    set_param("device", "device", device)
 
1571
 
 
1572
    set_param("bus", "bus")
 
1573
    set_param("driver_cache", "cache")
 
1574
    set_param("driver_name", "driver_name")
 
1575
    set_param("driver_type", "driver_type")
 
1576
    set_param("driver_io", "io")
 
1577
    set_param("error_policy", "error_policy")
 
1578
    set_param("serial", "serial")
 
1579
 
 
1580
    if opts:
 
1581
        fail(_("Unknown options %s") % opts.keys())
 
1582
 
 
1583
    return dev, size
 
1584
 
 
1585
#####################
 
1586
# --network parsing #
 
1587
#####################
 
1588
 
 
1589
def parse_network(guest, optstring, dev=None, mac=None):
 
1590
    # Handle old format of bridge:foo instead of bridge=foo
 
1591
    for prefix in ["network", "bridge"]:
 
1592
        if optstring.startswith(prefix + ":"):
 
1593
            optstring = optstring.replace(prefix + ":", prefix + "=")
 
1594
 
 
1595
    opts = parse_optstr(optstring, remove_first="type")
 
1596
 
 
1597
    # Determine device type
 
1598
    net_type = opts.get("type")
 
1599
    if "network" in opts:
 
1600
        net_type = VirtualNetworkInterface.TYPE_VIRTUAL
 
1601
    elif "bridge" in opts:
 
1602
        net_type = VirtualNetworkInterface.TYPE_BRIDGE
 
1603
 
 
1604
    # Build initial device
 
1605
    if not dev:
 
1606
        dev = VirtualNetworkInterface(conn=guest.conn,
 
1607
                                      type=net_type,
 
1608
                                      network=opts.get("network"),
 
1609
                                      bridge=opts.get("bridge"))
 
1610
 
 
1611
    if mac and not "mac" in opts:
 
1612
        opts["mac"] = mac
 
1613
    if "mac" in opts:
 
1614
        if opts["mac"] == "RANDOM":
 
1615
            opts["mac"] = None
 
1616
 
 
1617
    set_param = _build_set_param(dev, opts)
 
1618
 
 
1619
    set_param("type", "type", net_type)
 
1620
    set_param("network", "network")
 
1621
    set_param("bridge", "bridge")
 
1622
    set_param("model", "model")
 
1623
    set_param("macaddr", "mac")
 
1624
 
 
1625
    if opts:
 
1626
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1627
 
 
1628
    return dev
 
1629
 
 
1630
######################
 
1631
# --graphics parsing #
 
1632
######################
 
1633
 
 
1634
def parse_graphics(guest, optstring, dev=None):
 
1635
    if optstring is None:
978
1636
        return None
979
1637
 
980
 
    use_keymap = None
981
 
 
982
 
    if keymap.lower() == "local":
983
 
        use_keymap = virtinst.VirtualGraphics.KEYMAP_LOCAL
984
 
 
985
 
    elif keymap.lower() != "none":
 
1638
    def sanitize_keymap(keymap):
 
1639
        if not keymap:
 
1640
            return None
 
1641
        if keymap.lower() == "local":
 
1642
            return virtinst.VirtualGraphics.KEYMAP_LOCAL
 
1643
        if keymap.lower() == "none":
 
1644
            return None
 
1645
 
986
1646
        use_keymap = _util.check_keytable(keymap)
987
1647
        if not use_keymap:
988
 
            raise ValueError(_("Didn't match keymap '%s' in keytable!") %
989
 
                             keymap)
990
 
 
991
 
    return use_keymap
992
 
 
993
 
def parse_graphics(guest, optstring, basedict):
994
 
    if optstring is None and not basedict:
995
 
        return None
 
1648
            raise ValueError(
 
1649
                        _("Didn't match keymap '%s' in keytable!") % keymap)
 
1650
        return use_keymap
996
1651
 
997
1652
    # Peel the model type off the front
998
 
    gtype, opts = parse_optstr(optstring, basedict, remove_first=True)
999
 
    if gtype == "none" or basedict.get("type") == "none":
 
1653
    opts = parse_optstr(optstring, remove_first="type")
 
1654
    if opts.get("type") == "none":
1000
1655
        return None
1001
 
    dev = VirtualGraphics(conn=guest.conn)
 
1656
 
 
1657
    if not dev:
 
1658
        dev = VirtualGraphics(conn=guest.conn)
1002
1659
 
1003
1660
    def set_param(paramname, dictname, val=None):
1004
1661
        val = get_opt_param(opts, dictname, val)
1009
1666
            val = sanitize_keymap(val)
1010
1667
        setattr(dev, paramname, val)
1011
1668
 
1012
 
    set_param("type", "type", gtype)
 
1669
    set_param("type", "type")
1013
1670
    set_param("port", "port")
1014
1671
    set_param("tlsPort", "tlsport")
1015
1672
    set_param("listen", "listen")
1022
1679
 
1023
1680
    return dev
1024
1681
 
1025
 
def get_graphics(vnc, vncport, vnclisten, nographics, sdl, keymap,
1026
 
                 video_models, graphics, guest):
1027
 
    video_models = video_models or []
1028
 
 
1029
 
    if graphics and (vnc or sdl or keymap or vncport or vnclisten):
1030
 
        fail(_("Cannot mix --graphics and old style graphical options"))
1031
 
 
1032
 
    # If not graphics specified, choose a default
1033
 
    if not (vnc or nographics or sdl or graphics):
1034
 
        if "DISPLAY" in os.environ.keys():
1035
 
            logging.debug("DISPLAY is set: graphics defaulting to VNC.")
1036
 
            vnc = True
1037
 
        else:
1038
 
            logging.debug("DISPLAY is not set: defaulting to nographics.")
1039
 
            nographics = True
1040
 
 
1041
 
    if (sum(map(int, map(bool, [vnc, nographics, sdl, graphics])))) > 1:
1042
 
        raise ValueError(_("Can't specify more than one of VNC, SDL, "
1043
 
                           "--graphics or --nographics"))
1044
 
 
1045
 
    # Build an initial graphics argument dict
1046
 
    basedict = {
1047
 
        "type"      : ((vnc and "vnc") or
1048
 
                       (sdl and "sdl") or
1049
 
                       (nographics and "none")),
1050
 
        "listen"    : vnclisten,
1051
 
        "port"      : vncport,
1052
 
        "keymap"    : keymap,
1053
 
    }
1054
 
 
1055
 
    try:
1056
 
        dev = parse_graphics(guest, graphics, basedict)
1057
 
    except Exception, e:
1058
 
        fail(_("Error in graphics device parameters: %s") % str(e))
1059
 
 
1060
 
    if not dev:
1061
 
        return
1062
 
    guest.graphics_dev = dev
1063
 
 
1064
 
    # At this point we are definitely using graphics, so setup a default
1065
 
    # video card if necc.
1066
 
    if not video_models:
1067
 
        video_models.append(None)
1068
 
    for model in video_models:
1069
 
        vdev = virtinst.VirtualVideoDevice(guest.conn)
1070
 
        if model:
1071
 
            vdev.model_type = model
1072
 
        guest.add_device(vdev)
1073
 
 
1074
 
def get_sound(old_sound_bool, sound_opts, guest):
1075
 
    if not sound_opts:
1076
 
        if old_sound_bool:
1077
 
            # Use os default
1078
 
            guest.sound_devs.append(VirtualAudio(conn=guest.conn))
1079
 
        return
1080
 
 
1081
 
    for model in listify(sound_opts):
1082
 
        dev = VirtualAudio(conn=guest.conn)
1083
 
        dev.model = model
1084
 
        guest.add_device(dev)
1085
 
 
1086
 
 
1087
 
def get_hostdevs(hostdevs, guest):
1088
 
    if not hostdevs:
1089
 
        return
1090
 
 
1091
 
    for devname in hostdevs:
1092
 
        dev = virtinst.VirtualHostDevice.device_from_node(conn=guest.conn,
1093
 
                                                          name=devname)
1094
 
        guest.hostdevs.append(dev)
1095
 
 
1096
 
### Option parsing
1097
 
def check_before_store(option, opt_str, value, parser):
1098
 
    if len(value) == 0:
1099
 
        raise OptionValueError(_("%s option requires an argument") % opt_str)
1100
 
    setattr(parser.values, option.dest, value)
1101
 
 
1102
 
def check_before_append(option, opt_str, value, parser):
1103
 
    if len(value) == 0:
1104
 
        raise OptionValueError(_("%s option requires an argument") % opt_str)
1105
 
    parser.values.ensure_value(option.dest, []).append(value)
1106
 
 
1107
 
def get_opt_param(opts, dictnames, val=None):
1108
 
    if type(dictnames) is not list:
1109
 
        dictnames = [dictnames]
1110
 
 
1111
 
    for key in dictnames:
 
1682
#######################
 
1683
# --smartcard parsing #
 
1684
#######################
 
1685
 
 
1686
def parse_smartcard(guest, optstring, dev=None):
 
1687
    if optstring is None:
 
1688
        return None
 
1689
 
 
1690
    # Peel the mode off the front
 
1691
    opts = parse_optstr(optstring, remove_first="mode")
 
1692
    if opts.get("mode") == "none":
 
1693
        return None
 
1694
 
 
1695
    if not dev:
 
1696
        dev = virtinst.VirtualSmartCardDevice(guest.conn, opts.get("mode"))
 
1697
 
 
1698
    set_param = _build_set_param(dev, opts)
 
1699
 
 
1700
    set_param("mode", "mode")
 
1701
    set_param("type", "type")
 
1702
 
 
1703
    if opts:
 
1704
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1705
 
 
1706
    return dev
 
1707
 
 
1708
######################
 
1709
# --watchdog parsing #
 
1710
######################
 
1711
 
 
1712
def parse_watchdog(guest, optstring, dev=None):
 
1713
    # Peel the model type off the front
 
1714
    opts = parse_optstr(optstring, remove_first="model")
 
1715
 
 
1716
    if not dev:
 
1717
        dev = virtinst.VirtualWatchdog(guest.conn)
 
1718
 
 
1719
    def set_param(paramname, dictname, val=None):
 
1720
        val = get_opt_param(opts, dictname, val)
 
1721
        if val == None:
 
1722
            return
 
1723
 
 
1724
        setattr(dev, paramname, val)
 
1725
 
 
1726
    set_param("model", "model")
 
1727
    set_param("action", "action")
 
1728
 
 
1729
    if opts:
 
1730
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1731
 
 
1732
    return dev
 
1733
 
 
1734
 
 
1735
######################################################
 
1736
# --serial, --parallel, --channel, --console parsing #
 
1737
######################################################
 
1738
 
 
1739
def parse_serial(guest, optstring, dev=None):
 
1740
    return _parse_char(guest, optstring, "serial", dev)
 
1741
def parse_parallel(guest, optstring, dev=None):
 
1742
    return _parse_char(guest, optstring, "parallel", dev)
 
1743
def parse_console(guest, optstring, dev=None):
 
1744
    return _parse_char(guest, optstring, "console", dev)
 
1745
def parse_channel(guest, optstring, dev=None):
 
1746
    return _parse_char(guest, optstring, "channel", dev)
 
1747
 
 
1748
def _parse_char(guest, optstring, dev_type, dev=None):
 
1749
    """
 
1750
    Helper to parse --serial/--parallel options
 
1751
    """
 
1752
    # Peel the char type off the front
 
1753
    opts = parse_optstr(optstring, remove_first="char_type")
 
1754
    char_type = opts.get("char_type")
 
1755
 
 
1756
    if not dev:
 
1757
        dev = VirtualCharDevice.get_dev_instance(guest.conn,
 
1758
                                                 dev_type, char_type)
 
1759
 
 
1760
    def set_param(paramname, dictname, val=None):
 
1761
        val = get_opt_param(opts, dictname, val)
 
1762
        if val == None:
 
1763
            return
 
1764
 
 
1765
        if not dev.supports_property(paramname):
 
1766
            raise ValueError(_("%(devtype)s type '%(chartype)s' does not "
 
1767
                                "support '%(optname)s' option.") %
 
1768
                                {"devtype" : dev_type, "chartype": char_type,
 
1769
                                 "optname" : dictname} )
 
1770
        setattr(dev, paramname, val)
 
1771
 
 
1772
    def parse_host(key):
 
1773
        host, ignore, port = partition(opts.get(key), ":")
1112
1774
        if key in opts:
1113
 
            if val == None:
1114
 
                val = opts[key]
1115
1775
            del(opts[key])
1116
1776
 
1117
 
    return val
1118
 
 
1119
 
def partition(string, sep):
1120
 
    if not string:
1121
 
        return (None, None, None)
1122
 
 
1123
 
    if string.count(sep):
1124
 
        splitres = string.split(sep, 1)
1125
 
        ret = (splitres[0], sep, splitres[1])
1126
 
    else:
1127
 
        ret = (string, None, None)
1128
 
    return ret
 
1777
        return host or None, port or None
 
1778
 
 
1779
    host, port = parse_host("host")
 
1780
    bind_host, bind_port = parse_host("bind_host")
 
1781
    target_addr, target_port = parse_host("target_address")
 
1782
 
 
1783
    set_param("char_type", "char_type")
 
1784
    set_param("source_path", "path")
 
1785
    set_param("source_mode", "mode")
 
1786
    set_param("protocol",   "protocol")
 
1787
    set_param("source_host", "host", host)
 
1788
    set_param("source_port", "host", port)
 
1789
    set_param("bind_host", "bind_host", bind_host)
 
1790
    set_param("bind_port", "bind_host", bind_port)
 
1791
    set_param("target_type", "target_type")
 
1792
    set_param("target_name", "name")
 
1793
    set_param("target_address", "target_address", target_addr)
 
1794
    set_param("target_port", "target_address", target_port)
 
1795
 
 
1796
    if opts:
 
1797
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1798
 
 
1799
    # Try to generate dev XML to perform upfront validation
 
1800
    dev.get_xml_config()
 
1801
 
 
1802
    return dev
 
1803
 
 
1804
 
 
1805
########################
 
1806
# --filesystem parsing #
 
1807
########################
 
1808
 
 
1809
def parse_filesystem(guest, optstring, dev=None):
 
1810
    opts = parse_optstr(optstring, remove_first=["source", "target"])
 
1811
 
 
1812
    if not dev:
 
1813
        dev = virtinst.VirtualFilesystem(guest.conn)
 
1814
 
 
1815
    def set_param(paramname, dictname, val=None):
 
1816
        val = get_opt_param(opts, dictname, val)
 
1817
        if val == None:
 
1818
            return
 
1819
 
 
1820
        setattr(dev, paramname, val)
 
1821
 
 
1822
    set_param("type", "type")
 
1823
    set_param("mode", "mode")
 
1824
    set_param("source", "source")
 
1825
    set_param("target", "target")
 
1826
 
 
1827
    if opts:
 
1828
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1829
 
 
1830
    return dev
 
1831
 
 
1832
###################
 
1833
# --video parsing #
 
1834
###################
 
1835
 
 
1836
def parse_video(guest, optstr, dev=None):
 
1837
    opts = {"model" : optstr}
 
1838
 
 
1839
    if not dev:
 
1840
        dev = virtinst.VirtualVideoDevice(conn=guest.conn)
 
1841
 
 
1842
    set_param = _build_set_param(dev, opts)
 
1843
 
 
1844
    set_param("model_type", "model")
 
1845
 
 
1846
    if opts:
 
1847
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1848
    return dev
 
1849
 
 
1850
#####################
 
1851
# --soundhw parsing #
 
1852
#####################
 
1853
 
 
1854
def parse_sound(guest, optstr, dev=None):
 
1855
    opts = {"model" : optstr}
 
1856
 
 
1857
    if not dev:
 
1858
        dev = virtinst.VirtualAudio(conn=guest.conn)
 
1859
 
 
1860
    set_param = _build_set_param(dev, opts)
 
1861
 
 
1862
    set_param("model", "model")
 
1863
 
 
1864
    if opts:
 
1865
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1866
    return dev
 
1867
 
 
1868
#####################
 
1869
# --hostdev parsing #
 
1870
#####################
 
1871
 
 
1872
def parse_hostdev(guest, optstr, dev=None):
 
1873
    # XXX: Need to implement this for virt-xml
 
1874
    ignore = dev
 
1875
    return virtinst.VirtualHostDevice.device_from_node(conn=guest.conn,
 
1876
                                                       name=optstr)