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

« back to all changes in this revision

Viewing changes to virtinst/cli.py

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20
20
# MA 02110-1301 USA.
21
21
 
22
 
import os, sys
 
22
import os
 
23
import sys
23
24
import logging
24
25
import logging.handlers
25
 
import gettext, locale
 
26
import gettext
 
27
import locale
26
28
import optparse
27
 
from optparse import OptionValueError, OptionParser
 
29
from optparse import OptionValueError, OptionParser, OptionGroup
 
30
import re
 
31
import difflib
 
32
import tempfile
28
33
 
29
34
import libvirt
30
35
import _util
31
36
import virtinst
32
 
from virtinst import CapabilitiesParser, VirtualNetworkInterface, \
33
 
                     VirtualGraphics, VirtualAudio, VirtualDisk, User, \
34
 
                     VirtualVideoDevice
 
37
from _util import listify
 
38
from virtinst import VirtualNetworkInterface, Guest, \
 
39
                     VirtualGraphics, VirtualAudio, VirtualDisk, User
35
40
from virtinst import _virtinst as _
36
41
 
37
42
MIN_RAM = 64
38
43
force = False
 
44
quiet = False
39
45
doprompt = True
40
46
 
41
 
def check_if_test_uri_remote(uri):
42
 
    magic = "__virtinst_test_remote__"
43
 
    if uri and uri.startswith(magic):
44
 
        uri = uri.replace(magic, "")
 
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:
45
90
        _util.is_uri_remote = lambda uri_: True
46
 
    return uri
 
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
47
117
 
48
118
class VirtStreamHandler(logging.StreamHandler):
49
119
 
75
145
        encoding = getattr(f, "encoding", None)
76
146
        if not encoding:
77
147
            (dummy, encoding) = locale.getlocale()
 
148
        if not encoding:
 
149
            encoding = "UTF-8"
78
150
        return encoding
79
151
 
80
152
    def print_help(self, file=None):
134
206
    gettext.bindtextdomain(virtinst.gettext_app, virtinst.gettext_dir)
135
207
    gettext.install(virtinst.gettext_app, virtinst.gettext_dir)
136
208
 
137
 
def setupLogging(appname, debug=False):
138
 
    # set up logging
 
209
def earlyLogging():
 
210
    logging.basicConfig(level=logging.DEBUG, format='%(message)s')
 
211
 
 
212
def setupLogging(appname, debug=False, do_quiet=False):
 
213
    global quiet
 
214
    quiet = do_quiet
 
215
 
139
216
    vi_dir = os.path.expanduser("~/.virtinst")
140
217
    if not os.access(vi_dir, os.W_OK):
141
218
        if os.path.exists(vi_dir):
149
226
 
150
227
 
151
228
    dateFormat = "%a, %d %b %Y %H:%M:%S"
152
 
    fileFormat = "[%(asctime)s " + appname + " %(process)d] %(levelname)s (%(module)s:%(lineno)d) %(message)s"
 
229
    fileFormat = ("[%(asctime)s " + appname + " %(process)d] "
 
230
                  "%(levelname)s (%(module)s:%(lineno)d) %(message)s")
153
231
    streamDebugFormat = "%(asctime)s %(levelname)-8s %(message)s"
154
232
    streamErrorFormat = "%(levelname)-8s %(message)s"
155
233
    filename = os.path.join(vi_dir, appname + ".log")
156
234
 
157
235
    rootLogger = logging.getLogger()
 
236
 
 
237
    # Undo early logging
 
238
    for handler in rootLogger.handlers:
 
239
        rootLogger.removeHandler(handler)
 
240
 
158
241
    rootLogger.setLevel(logging.DEBUG)
159
 
    fileHandler = logging.handlers.RotatingFileHandler(filename, "a",
160
 
                                                       1024*1024, 5)
 
242
    fileHandler = logging.handlers.RotatingFileHandler(filename, "ae",
 
243
                                                       1024 * 1024, 5)
161
244
 
162
245
    fileHandler.setFormatter(logging.Formatter(fileFormat,
163
246
                                               dateFormat))
169
252
        streamHandler.setFormatter(logging.Formatter(streamDebugFormat,
170
253
                                                     dateFormat))
171
254
    else:
172
 
        streamHandler.setLevel(logging.ERROR)
 
255
        if quiet:
 
256
            level = logging.ERROR
 
257
        else:
 
258
            level = logging.WARN
 
259
        streamHandler.setLevel(level)
173
260
        streamHandler.setFormatter(logging.Formatter(streamErrorFormat))
174
261
    rootLogger.addHandler(streamHandler)
175
262
 
191
278
    # Log the app command string
192
279
    logging.debug("Launched with command line:\n%s" % " ".join(sys.argv))
193
280
 
194
 
def fail(msg):
 
281
def fail(msg, do_exit=True):
195
282
    """Convenience function when failing in cli app"""
196
283
    logging.error(msg)
197
 
    _util.log_exception()
198
 
    _fail_exit()
 
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
199
295
 
200
296
def _fail_exit():
201
297
    sys.exit(1)
202
298
 
203
299
def nice_exit():
204
 
    print _("Exiting at user request.")
 
300
    print_stdout(_("Exiting at user request."))
205
301
    sys.exit(0)
206
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
 
207
316
# Connection opening helper functions
208
 
def getConnection(connect):
209
 
    if (connect and
210
 
        not User.current().has_priv(User.PRIV_CREATE_DOMAIN, connect)):
 
317
def getConnection(uri):
 
318
    if (uri and not User.current().has_priv(User.PRIV_CREATE_DOMAIN, uri)):
211
319
        fail(_("Must be root to create Xen guests"))
212
320
 
213
 
    # Hack to facilitate remote unit testing
214
 
    connect = check_if_test_uri_remote(connect)
 
321
    # Hack to facilitate virtinst unit testing
 
322
    if _is_virtinst_test_uri(uri):
 
323
        return _open_test_uri(uri)
215
324
 
216
 
    logging.debug("Requesting libvirt URI %s" % (connect or "default"))
217
 
    conn = open_connection(connect)
 
325
    logging.debug("Requesting libvirt URI %s" % (uri or "default"))
 
326
    conn = open_connection(uri)
218
327
    logging.debug("Received libvirt URI %s" % conn.getURI())
219
328
 
220
329
    return conn
235
344
    try:
236
345
        return _do_creds(creds, cbdata)
237
346
    except:
238
 
        _util.log_exception("Error in creds callback.")
 
347
        log_exception("Error in creds callback.")
239
348
        raise
240
349
 
241
350
def _do_creds(creds, cbdata_ignore):
334
443
def is_prompt():
335
444
    return doprompt
336
445
 
337
 
def prompt_for_input(noprompt_err, prompt = "", val = None, failed=False):
 
446
def prompt_for_input(noprompt_err, prompt="", val=None, failed=False):
338
447
    if val is not None:
339
448
        return val
340
449
 
343
452
            # We already failed validation in a previous function, just exit
344
453
            _fail_exit()
345
454
 
346
 
        msg = noprompt_err
347
 
        if not force and not msg.count("--prompt"):
348
 
            # msg wasn't already appended to from yes_or_no
349
 
            msg = noprompt_err + " " + _("(use --prompt to run interactively)")
350
 
        fail(msg)
 
455
        fail(noprompt_err)
351
456
 
352
 
    print prompt + " ",
 
457
    print_stdout(prompt + " ", do_force=True)
353
458
    return sys.stdin.readline().strip()
354
459
 
355
 
def yes_or_no(s):
 
460
def yes_or_no_convert(s):
356
461
    s = s.lower()
357
462
    if s in ("y", "yes", "1", "true", "t"):
358
463
        return True
359
464
    elif s in ("n", "no", "0", "false", "f"):
360
465
        return False
361
 
    raise ValueError, "A yes or no response is required"
 
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
362
473
 
363
474
def prompt_for_yes_or_no(warning, question):
364
475
    """catches yes_or_no errors and ensures a valid bool return"""
401
512
            passed_val = None
402
513
            failed = True
403
514
 
 
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
 
404
530
# Register vnc + sdl options for virt-install and virt-image
405
531
def graphics_option_group(parser):
406
 
    from optparse import OptionGroup
407
532
 
408
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=1,tlsport=2\n"
 
539
             "--graphics none\n"
 
540
             "--graphics vnc,password=foobar,port=5910,keymap=ja"))
409
541
    vncg.add_option("", "--vnc", action="store_true", dest="vnc",
410
 
                    help=_("Use VNC for graphics support"))
 
542
                    help=optparse.SUPPRESS_HELP)
411
543
    vncg.add_option("", "--vncport", type="int", dest="vncport",
412
 
                    help=_("Port to use for VNC"))
 
544
                    help=optparse.SUPPRESS_HELP)
413
545
    vncg.add_option("", "--vnclisten", type="string", dest="vnclisten",
414
 
                    help=_("Address to listen on for VNC connections."))
 
546
                    help=optparse.SUPPRESS_HELP)
415
547
    vncg.add_option("-k", "--keymap", type="string", dest="keymap",
416
548
                    action="callback", callback=check_before_store,
417
 
                    help=_("set up keymap for the VNC console"))
 
549
                    help=optparse.SUPPRESS_HELP)
418
550
    vncg.add_option("", "--sdl", action="store_true", dest="sdl",
419
 
                    help=_("Use SDL for graphics support"))
 
551
                    help=optparse.SUPPRESS_HELP)
420
552
    vncg.add_option("", "--nographics", action="store_true",
421
 
                    help=_("Don't set up a graphical console for the guest."))
 
553
                    help=optparse.SUPPRESS_HELP)
422
554
    return vncg
423
555
 
424
556
# Specific function for disk prompting. Returns a validated VirtualDisk
451
583
            msg = _("What would you like to use as the disk (file path)?")
452
584
            if not size is None:
453
585
                msg = _("Please enter the path to the file you would like to "
454
 
                        "use for storage. It will have size %sGB.") %(size,)
 
586
                        "use for storage. It will have size %sGB.") % size
455
587
 
456
588
        if not no_path_needed:
457
589
            path = prompt_for_input(patherr, prompt_txt or msg, passed_path)
458
590
        else:
459
591
            path = None
460
592
        arg_dict["path"] = path
 
593
        path_exists = VirtualDisk.path_exists(conn, path)
461
594
 
462
595
        sizeerr = _("A size must be specified for non-existent disks.")
463
596
        if path and not size and prompt_size:
465
598
                            "be (in gigabytes)?") % path
466
599
 
467
600
            try:
468
 
                if not VirtualDisk.path_exists(conn, path):
 
601
                if not path_exists:
469
602
                    size = prompt_loop(size_prompt, sizeerr, size, None, None,
470
603
                                       func=float)
471
604
            except Exception, e:
487
620
        askmsg = _("Do you really want to use this disk (yes or no)")
488
621
 
489
622
        # Prompt if disk file already exists and preserve mode is not used
490
 
        if warn_overwrite and os.path.exists(dev.path):
491
 
            msg = (_("This will overwrite the existing path '%s'!\n" %
 
623
        does_collide = (path_exists and
 
624
                        dev.type == dev.TYPE_FILE and
 
625
                        dev.device == dev.DEVICE_DISK)
 
626
        if (does_collide and (warn_overwrite or is_prompt())):
 
627
            msg = (_("This will overwrite the existing path '%s'" %
492
628
                   dev.path))
493
629
            if not prompt_for_yes_or_no(msg, askmsg):
494
630
                continue
495
631
 
496
632
        # Check disk conflicts
497
633
        if dev.is_conflict_disk(conn) is True:
498
 
            msg = (_("Disk %s is already in use by another guest!\n" %
 
634
            msg = (_("Disk %s is already in use by another guest" %
499
635
                   dev.path))
500
636
            if not prompt_for_yes_or_no(msg, askmsg):
501
637
                continue
513
649
# Ask for attributes
514
650
#
515
651
 
516
 
def listify(l):
517
 
    if l is None:
518
 
        return []
519
 
    elif type(l) != list:
520
 
        return [ l ]
521
 
    else:
522
 
        return l
 
652
name_missing    = _("--name is required")
 
653
ram_missing     = _("--ram amount in MB is required")
523
654
 
524
655
def get_name(name, guest, image_name=None):
525
656
    prompt_txt = _("What is the name of your virtual machine?")
526
 
    err_txt = _("A name is required for the virtual machine.")
 
657
    err_txt = name_missing
527
658
 
528
659
    if name is None:
529
660
        name = image_name
531
662
 
532
663
def get_memory(memory, guest, image_memory=None):
533
664
    prompt_txt = _("How much RAM should be allocated (in megabytes)?")
534
 
    err_txt = _("Memory amount is required for the virtual machine.")
 
665
    err_txt = ram_missing
 
666
 
535
667
    def check_memory(mem):
536
668
        mem = int(mem)
537
669
        if mem < MIN_RAM:
540
672
        guest.memory = mem
541
673
 
542
674
    if memory is None and image_memory is not None:
543
 
        memory = int(image_memory)/1024
 
675
        memory = int(image_memory) / 1024
544
676
    prompt_loop(prompt_txt, err_txt, memory, guest, "memory",
545
677
                func=check_memory)
546
678
 
551
683
        except ValueError, e:
552
684
            fail(e)
553
685
 
554
 
def get_vcpus(vcpus, check_cpu, guest, conn, image_vcpus=None):
 
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
    vcpus, opts = parse_optstr(optstring, remove_first=True)
 
700
    vcpus = vcpus or default_vcpus
 
701
 
 
702
    set_param = _build_set_param(guest, opts)
 
703
    set_cpu_param = _build_set_param(guest.cpu, opts)
 
704
    has_vcpus = ("vcpus" in opts or vcpus)
 
705
 
 
706
    set_param("vcpus", "vcpus", vcpus)
 
707
    set_param("maxvcpus", "maxvcpus")
 
708
 
 
709
    set_cpu_param("sockets", "sockets")
 
710
    set_cpu_param("cores", "cores")
 
711
    set_cpu_param("threads", "threads")
 
712
 
 
713
    if not has_vcpus:
 
714
        guest.vcpus = guest.cpu.vcpus_from_topology()
 
715
 
 
716
    if opts:
 
717
        raise ValueError(_("Unknown options %s") % opts.keys())
 
718
 
 
719
 
 
720
def get_vcpus(vcpus, check_cpu, guest, image_vcpus=None):
 
721
    if vcpus is None and image_vcpus is not None:
 
722
        vcpus = int(image_vcpus)
 
723
 
 
724
    parse_vcpu_option(guest, vcpus, image_vcpus)
 
725
 
 
726
    conn = guest.conn
555
727
    if check_cpu:
556
728
        hostinfo = conn.getInfo()
557
729
        cpu_num = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
564
736
            if not prompt_for_yes_or_no(msg, askmsg):
565
737
                nice_exit()
566
738
 
567
 
    if vcpus is None and image_vcpus is not None:
568
 
        vcpus = int(image_vcpus)
569
 
    if vcpus is not None:
570
 
        try:
571
 
            guest.vcpus = vcpus
572
 
        except ValueError, e:
573
 
            fail(e)
574
739
 
575
 
def get_cpuset(cpuset, mem, guest, conn):
 
740
def get_cpuset(cpuset, mem, guest):
 
741
    conn = guest.conn
576
742
    if cpuset and cpuset != "auto":
577
743
        guest.cpuset = cpuset
 
744
 
578
745
    elif cpuset == "auto":
579
 
        caps = CapabilitiesParser.parse(conn.getCapabilities())
580
 
        if caps.host.topology is None:
581
 
            logging.debug("No topology section in caps xml. Skipping cpuset")
582
 
            return
583
 
 
584
 
        cells = caps.host.topology.cells
585
 
        if len(cells) <= 1:
586
 
            logging.debug("Capabilities only show <= 1 cell. Not NUMA capable")
587
 
            return
588
 
 
589
 
        cell_mem = conn.getCellsFreeMemory(0, len(cells))
590
 
        cell_id = -1
591
 
        mem = mem * 1024
592
 
        for i in range(len(cells)):
593
 
            if cell_mem[i] > mem and len(cells[i].cpus) != 0:
594
 
                # Find smallest cell that fits
595
 
                if cell_id < 0 or cell_mem[i] < cell_mem[cell_id]:
596
 
                    cell_id = i
597
 
        if cell_id < 0:
598
 
            logging.debug("Could not find any usable NUMA cell/cpu combinations. Not setting cpuset.")
599
 
            return
600
 
 
601
 
        # Build cpuset
602
 
        cpustr = ""
603
 
        for cpu in cells[cell_id].cpus:
604
 
            if cpustr != "":
605
 
                cpustr += ","
606
 
            cpustr += str(cpu.id)
607
 
        logging.debug("Auto cpuset is: %s" % cpustr)
608
 
        guest.cpuset = cpustr
 
746
        tmpset = None
 
747
        try:
 
748
            tmpset = Guest.generate_cpuset(conn, mem)
 
749
        except Exception, e:
 
750
            logging.debug("Not setting cpuset", str(e))
 
751
 
 
752
        if tmpset:
 
753
            logging.debug("Auto cpuset is: %s" % tmpset)
 
754
            guest.cpuset = tmpset
 
755
 
609
756
    return
610
757
 
 
758
def parse_cpu(guest, optstring):
 
759
    default_dict = {
 
760
        "force": [],
 
761
        "require": [],
 
762
        "optional": [],
 
763
        "disable": [],
 
764
        "forbid": [],
 
765
    }
 
766
    model, opts = parse_optstr(optstring,
 
767
                               basedict=default_dict,
 
768
                               remove_first=True)
 
769
 
 
770
    # Convert +feature, -feature into expected format
 
771
    for key, value in opts.items():
 
772
        policy = None
 
773
        if value or len(key) == 1:
 
774
            continue
 
775
 
 
776
        if key.startswith("+"):
 
777
            policy = "force"
 
778
        elif key.startswith("-"):
 
779
            policy = "disable"
 
780
 
 
781
        if policy:
 
782
            del(opts[key])
 
783
            opts[policy].append(key[1:])
 
784
 
 
785
    set_param = _build_set_param(guest.cpu, opts)
 
786
    def set_features(policy):
 
787
        for name in opts.get(policy):
 
788
            guest.cpu.add_feature(name, policy)
 
789
        del(opts[policy])
 
790
 
 
791
    if model == "host":
 
792
        guest.cpu.copy_host_cpu()
 
793
        model = None
 
794
 
 
795
    set_param("model", "model", model)
 
796
    set_param("match", "match")
 
797
    set_param("vendor", "vendor")
 
798
 
 
799
    set_features("force")
 
800
    set_features("require")
 
801
    set_features("optional")
 
802
    set_features("disable")
 
803
    set_features("forbid")
 
804
 
 
805
    if opts:
 
806
        raise ValueError(_("Unknown options %s") % opts.keys())
 
807
 
611
808
def get_network(net_kwargs, guest):
612
809
    n = VirtualNetworkInterface(**net_kwargs)
613
810
    guest.nics.append(n)
623
820
        if (distro_variant and str(distro_variant).lower() != "none"):
624
821
            guest.set_os_variant(distro_variant)
625
822
 
626
 
 
627
 
def parse_optstr(optstr, basedict=None):
 
823
def parse_optstr_tuples(optstr):
628
824
    """
629
 
    Helper function for parsing opt strings of the form
630
 
    opt1=val1,opt2=val2,...
631
 
    'basedict' is a starting dictionary, so the caller can easily set
632
 
    default values, etc.
633
 
 
634
 
    Returns a dictionary of {'opt1': 'val1', 'opt2': 'val2'}
 
825
    Parse optstr into a list of ordered tuples
635
826
    """
636
827
    optstr = str(optstr or "")
637
 
    optdict = basedict or {}
 
828
    optlist = []
638
829
 
639
830
    args = optstr.split(",")
640
831
    for opt in args:
645
836
        opt_val = None
646
837
        if opt.count("="):
647
838
            opt_type, opt_val = opt.split("=", 1)
648
 
            optdict[opt_type.lower()] = opt_val.lower()
649
 
        else:
650
 
            optdict[opt.lower()] = None
651
 
 
652
 
    return optdict
 
839
            optlist.append((opt_type.lower(), opt_val))
 
840
        else:
 
841
            optlist.append((opt.lower(), None))
 
842
 
 
843
    return optlist
 
844
 
 
845
def parse_optstr(optstr, basedict=None, remove_first=False):
 
846
    """
 
847
    Helper function for parsing opt strings of the form
 
848
    opt1=val1,opt2=val2,...
 
849
 
 
850
    @param basedict: starting dictionary, so the caller can easily set
 
851
                     default values, etc.
 
852
    @param remove_first: If true, remove the first options off the string
 
853
                         and return it seperately. For example,
 
854
                         --serial pty,foo=bar returns ("pty", {"foo" : "bar"})
 
855
 
 
856
    Returns a dictionary of {'opt1': 'val1', 'opt2': 'val2'}
 
857
    """
 
858
    optlist = parse_optstr_tuples(optstr)
 
859
    optdict = basedict or {}
 
860
    first = None
 
861
 
 
862
    if remove_first and optlist:
 
863
        first_tuple = optlist[0]
 
864
        if first_tuple[1] == None:
 
865
            first = first_tuple[0]
 
866
            optlist.remove(first_tuple)
 
867
 
 
868
    for opt, val in optlist:
 
869
        if type(optdict.get(opt)) is list:
 
870
            optdict[opt].append(val)
 
871
        else:
 
872
            optdict[opt] = val
 
873
 
 
874
    return (first, optdict)
653
875
 
654
876
def parse_network_opts(conn, mac, network):
655
877
    net_type = None
678
900
        bridge_name = netdata
679
901
 
680
902
    # Pass the remaining arg=value pairs
681
 
    opts = parse_optstr(",".join(args))
 
903
    ignore, opts = parse_optstr(",".join(args))
682
904
    for opt_type, ignore_val in opts.items():
683
905
        if opt_type not in option_whitelist:
684
906
            fail(_("Unknown network option '%s'") % opt_type)
690
912
    # __init__
691
913
    if mac == "RANDOM":
692
914
        mac = None
693
 
    return { "conn" : conn, "type" : net_type, "bridge": bridge_name,
694
 
             "network" : network_name, "model" : model , "macaddr" : mac }
 
915
    return {"conn" : conn, "type" : net_type, "bridge": bridge_name,
 
916
            "network" : network_name, "model" : model , "macaddr" : mac}
695
917
 
696
 
def digest_networks(conn, macs, bridges, networks, nics = 0):
 
918
def digest_networks(conn, macs, bridges, networks, nics=0):
697
919
    macs     = listify(macs)
698
920
    bridges  = listify(bridges)
699
921
    networks = listify(networks)
716
938
    if len(macs) > len(networks):
717
939
        fail(_("Cannot pass more mac addresses than networks."))
718
940
    else:
719
 
        for dummy in range (len(macs),len(networks)):
 
941
        for dummy in range(len(macs), len(networks)):
720
942
            macs.append(None)
721
943
 
722
944
    # Create extra networks up to the number of nics requested
723
945
    if len(macs) < nics:
724
 
        for dummy in range(len(macs),nics):
 
946
        for dummy in range(len(macs), nics):
725
947
            if User.current().has_priv(User.PRIV_CREATE_NETWORK, conn.getURI()):
726
948
                net = _util.default_network(conn)
727
949
                networks.append(net[0] + ":" + net[1])
737
959
 
738
960
    return net_init_dicts
739
961
 
 
962
def sanitize_keymap(keymap):
 
963
    if not keymap:
 
964
        return None
 
965
 
 
966
    use_keymap = None
 
967
 
 
968
    if keymap.lower() == "local":
 
969
        use_keymap = virtinst.VirtualGraphics.KEYMAP_LOCAL
 
970
 
 
971
    elif keymap.lower() != "none":
 
972
        use_keymap = _util.check_keytable(keymap)
 
973
        if not use_keymap:
 
974
            raise ValueError(_("Didn't match keymap '%s' in keytable!") %
 
975
                             keymap)
 
976
 
 
977
    return use_keymap
 
978
 
 
979
def parse_graphics(guest, optstring, basedict):
 
980
    if optstring is None and not basedict:
 
981
        return None
 
982
 
 
983
    # Peel the model type off the front
 
984
    gtype, opts = parse_optstr(optstring, basedict, remove_first=True)
 
985
    if gtype == "none" or basedict.get("type") == "none":
 
986
        return None
 
987
    dev = VirtualGraphics(conn=guest.conn)
 
988
 
 
989
    def set_param(paramname, dictname, val=None):
 
990
        val = get_opt_param(opts, dictname, val)
 
991
        if val == None:
 
992
            return
 
993
 
 
994
        if paramname == "keymap":
 
995
            val = sanitize_keymap(val)
 
996
        setattr(dev, paramname, val)
 
997
 
 
998
    set_param("type", "type", gtype)
 
999
    set_param("port", "port")
 
1000
    set_param("tlsPort", "tlsport")
 
1001
    set_param("listen", "listen")
 
1002
    set_param("keymap", "keymap")
 
1003
    set_param("passwd", "password")
 
1004
    set_param("passwdValidTo", "passwordvalidto")
 
1005
 
 
1006
    if opts:
 
1007
        raise ValueError(_("Unknown options %s") % opts.keys())
 
1008
 
 
1009
    return dev
 
1010
 
740
1011
def get_graphics(vnc, vncport, vnclisten, nographics, sdl, keymap,
741
 
                 video_models, guest):
 
1012
                 video_models, graphics, guest):
742
1013
    video_models = video_models or []
743
1014
 
744
 
    if ((vnc and nographics) or
745
 
        (vnc and sdl) or
746
 
        (sdl and nographics)):
747
 
        raise ValueError, _("Can't specify more than one of VNC, SDL, "
748
 
                            "or --nographics")
749
 
 
750
 
    for model in video_models:
751
 
        dev = virtinst.VirtualVideoDevice(guest.conn)
752
 
        dev.model_type = model
753
 
        guest.add_device(dev)
754
 
 
755
 
    if not (vnc or nographics or sdl):
 
1015
    if graphics and (vnc or sdl or keymap or vncport or vnclisten):
 
1016
        fail(_("Cannot mix --graphics and old style graphical options"))
 
1017
 
 
1018
    # If not graphics specified, choose a default
 
1019
    if not (vnc or nographics or sdl or graphics):
756
1020
        if "DISPLAY" in os.environ.keys():
757
1021
            logging.debug("DISPLAY is set: graphics defaulting to VNC.")
758
1022
            vnc = True
760
1024
            logging.debug("DISPLAY is not set: defaulting to nographics.")
761
1025
            nographics = True
762
1026
 
763
 
    if nographics is not None:
764
 
        guest.graphics_dev = None
 
1027
    if (sum(map(int, map(bool, [vnc, nographics, sdl, graphics])))) > 1:
 
1028
        raise ValueError(_("Can't specify more than one of VNC, SDL, "
 
1029
                           "--graphics or --nographics"))
 
1030
 
 
1031
    # Build an initial graphics argument dict
 
1032
    basedict = {
 
1033
        "type"      : ((vnc and "vnc") or
 
1034
                       (sdl and "sdl") or
 
1035
                       (nographics and "none")),
 
1036
        "listen"    : vnclisten,
 
1037
        "port"      : vncport,
 
1038
        "keymap"    : keymap,
 
1039
    }
 
1040
 
 
1041
    try:
 
1042
        dev = parse_graphics(guest, graphics, basedict)
 
1043
    except Exception, e:
 
1044
        fail(_("Error in graphics device parameters: %s") % str(e))
 
1045
 
 
1046
    if not dev:
765
1047
        return
 
1048
    guest.graphics_dev = dev
766
1049
 
767
 
    # After this point, we are using graphics, so add a video device
768
 
    # if one wasn't passed
 
1050
    # At this point we are definitely using graphics, so setup a default
 
1051
    # video card if necc.
769
1052
    if not video_models:
770
 
        guest.add_device(VirtualVideoDevice(conn=guest.conn))
771
 
 
772
 
    if sdl is not None:
773
 
        guest.graphics_dev = VirtualGraphics(type=VirtualGraphics.TYPE_SDL)
774
 
        return
775
 
 
776
 
    if vnc is not None:
777
 
        guest.graphics_dev = VirtualGraphics(type=VirtualGraphics.TYPE_VNC)
778
 
        if vncport:
779
 
            guest.graphics_dev.port = vncport
780
 
        if vnclisten:
781
 
            guest.graphics_dev.listen = vnclisten
782
 
 
783
 
    if keymap:
784
 
        use_keymap = None
785
 
 
786
 
        if keymap.lower() == "local":
787
 
            use_keymap = virtinst.VirtualGraphics.KEYMAP_LOCAL
788
 
 
789
 
        elif keymap.lower() != "none":
790
 
            use_keymap = _util.check_keytable(keymap)
791
 
            if not use_keymap:
792
 
                raise ValueError(_("Didn't match keymap '%s' in keytable!") %
793
 
                                 keymap)
794
 
 
795
 
        guest.graphics_dev.keymap = use_keymap
 
1053
        video_models.append(None)
 
1054
    for model in video_models:
 
1055
        vdev = virtinst.VirtualVideoDevice(guest.conn)
 
1056
        if model:
 
1057
            vdev.model_type = model
 
1058
        guest.add_device(vdev)
796
1059
 
797
1060
def get_sound(old_sound_bool, sound_opts, guest):
798
1061
    if not sound_opts:
819
1082
### Option parsing
820
1083
def check_before_store(option, opt_str, value, parser):
821
1084
    if len(value) == 0:
822
 
        raise OptionValueError, _("%s option requires an argument") %opt_str
 
1085
        raise OptionValueError(_("%s option requires an argument") % opt_str)
823
1086
    setattr(parser.values, option.dest, value)
824
1087
 
825
1088
def check_before_append(option, opt_str, value, parser):
826
1089
    if len(value) == 0:
827
 
        raise OptionValueError, _("%s option requires an argument") %opt_str
 
1090
        raise OptionValueError(_("%s option requires an argument") % opt_str)
828
1091
    parser.values.ensure_value(option.dest, []).append(value)
829
1092
 
 
1093
def get_opt_param(opts, dictnames, val=None):
 
1094
    if type(dictnames) is not list:
 
1095
        dictnames = [dictnames]
 
1096
 
 
1097
    for key in dictnames:
 
1098
        if key in opts:
 
1099
            if val == None:
 
1100
                val = opts[key]
 
1101
            del(opts[key])
 
1102
 
 
1103
    return val
 
1104
 
 
1105
def partition(string, sep):
 
1106
    if not string:
 
1107
        return (None, None, None)
 
1108
 
 
1109
    if string.count(sep):
 
1110
        splitres = string.split(sep, 1)
 
1111
        ret = (splitres[0], sep, splitres[1])
 
1112
    else:
 
1113
        ret = (string, None, None)
 
1114
    return ret