#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free  Software Foundation; either version 2 of the License, or
# (at your option)  any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.

import atexit
import imp
import logging
import os
import shlex
import subprocess
import sys
import time
import traceback
import unittest
import StringIO

import virtinst.cli

from tests import utils

os.environ["VIRTCONV_TEST_NO_DISK_CONVERSION"] = "1"
os.environ["LANG"] = "en_US.UTF-8"

testuri = "test:///%s/tests/testdriver.xml" % os.getcwd()

# There is a hack in virtinst/cli.py to find this magic string and
# convince virtinst we are using a remote connection.
fakeuri     = "__virtinst_test__" + testuri + ",predictable"
capsprefix  = ",caps=%s/tests/capabilities-xml/" % os.getcwd()
remoteuri   = fakeuri + ",remote"
kvmuri      = fakeuri + capsprefix + "libvirt-0.7.6-qemu-caps.xml,qemu"
xenuri      = fakeuri + capsprefix + "rhel5.4-xen-caps-virt-enabled.xml,xen"
xenia64uri  = fakeuri + capsprefix + "xen-ia64-hvm.xml,xen"
lxcuri      = fakeuri + capsprefix + "capabilities-lxc.xml,lxc"

# Location
image_prefix = "/tmp/__virtinst_cli_"
xmldir = "tests/cli-test-xml"
treedir = "%s/faketree" % xmldir
vcdir = "%s/virtconv" % xmldir
ro_dir = image_prefix + "clitest_rodir"
ro_img = "%s/cli_exist3ro.img" % ro_dir
ro_noexist_img = "%s/idontexist.img" % ro_dir
compare_xmldir = "%s/compare" % xmldir
virtconv_out = "/tmp/__virtinst_tests__virtconv-outdir"

# Images that will be created by virt-install/virt-clone, and removed before
# each run
new_images = [
    image_prefix + "new1.img",
    image_prefix + "new2.img",
    image_prefix + "new3.img",
    image_prefix + "exist1-clone.img",
    image_prefix + "exist2-clone.img",
]

# Images that are expected to exist before a command is run
exist_images = [
    image_prefix + "exist1.img",
    image_prefix + "exist2.img",
    ro_img,
]

# Images that need to exist ahead of time for virt-image
virtimage_exist = ["/tmp/__virtinst__cli_root.raw"]

# Images created by virt-image
virtimage_new = ["/tmp/__virtinst__cli_scratch.raw"]

# virt-convert output dirs
virtconv_dirs = [virtconv_out]

exist_files = exist_images + virtimage_exist
new_files   = new_images + virtimage_new + virtconv_dirs
clean_files = (new_images + exist_images +
               virtimage_exist + virtimage_new + virtconv_dirs + [ro_dir])

promptlist = []

test_files = {
    'TESTURI'           : testuri,
    'DEFAULTURI'        : "__virtinst_test__test:///default,predictable",
    'REMOTEURI'         : remoteuri,
    'KVMURI'            : kvmuri,
    'XENURI'            : xenuri,
    'XENIA64URI'        : xenia64uri,
    'LXCURI'            : lxcuri,
    'CLONE_DISK_XML'    : "%s/clone-disk.xml" % xmldir,
    'CLONE_STORAGE_XML' : "%s/clone-disk-managed.xml" % xmldir,
    'CLONE_NOEXIST_XML' : "%s/clone-disk-noexist.xml" % xmldir,
    'IMAGE_XML'         : "%s/image.xml" % xmldir,
    'IMAGE_NOGFX_XML'   : "%s/image-nogfx.xml" % xmldir,
    'NEWIMG1'           : new_images[0],
    'NEWIMG2'           : new_images[1],
    'NEWIMG3'           : new_images[2],
    'EXISTIMG1'         : exist_images[0],
    'EXISTIMG2'         : exist_images[1],
    'ROIMG'             : ro_img,
    'ROIMGNOEXIST'      : ro_noexist_img,
    'POOL'              : "default-pool",
    'VOL'               : "testvol1.img",
    'DIR'               : os.getcwd(),
    'TREEDIR'           : treedir,
    'MANAGEDEXIST1'     : "/default-pool/testvol1.img",
    'MANAGEDEXIST2'     : "/default-pool/testvol2.img",
    'MANAGEDEXISTUPPER' : "/default-pool/UPPER",
    'MANAGEDNEW1'       : "/default-pool/clonevol",
    'MANAGEDNEW2'       : "/default-pool/clonevol",
    'MANAGEDDISKNEW1'   : "/disk-pool/newvol1.img",
    'COLLIDE'           : "/default-pool/collidevol1.img",
    'SHARE'             : "/default-pool/sharevol.img",

    'VIRTCONV_OUT'      : "%s/test.out" % virtconv_out,
    'VC_IMG1'           : "%s/virtimage/test1.virt-image" % vcdir,
    'VC_IMG2'           : "tests/image-xml/image-format.xml",
    'VMX_IMG1'          : "%s/vmx/test1.vmx" % vcdir,
}


_cleanup_imports = []


def _import(name, path):
    _cleanup_imports.append(path + "c")
    return imp.load_source(name, path)


def _cleanup_imports_cb():
    for f in _cleanup_imports:
        if os.path.exists(f):
            os.unlink(f)


atexit.register(_cleanup_imports_cb)
virtinstall = _import("virtinstall", "virt-install")
virtimage = _import("virtimage", "virt-image")
virtclone = _import("virtclone", "virt-clone")
virtconvert = _import("virtconvert", "virt-convert")


######################
# Test class helpers #
######################


class Command(object):
    """
    Instance of a single cli command to test
    """
    def __init__(self, cmd):
        self.cmdstr = cmd % test_files
        self.check_success = True
        self.compare_file = None

        app, opts = self.cmdstr.split(" ", 1)
        self.argv = [os.path.abspath(app)] + shlex.split(opts)

    def _launch_command(self):
        logging.debug(self.cmdstr)

        uri = None
        conn = None
        app = self.argv[0]

        for idx in reversed(range(len(self.argv))):
            if self.argv[idx] == "--connect":
                uri = self.argv[idx + 1]
                break

        if uri:
            conn = open_conn(uri)

        oldstdout = sys.stdout
        oldstderr = sys.stderr
        oldargv = sys.argv
        try:
            out = StringIO.StringIO()
            sys.stdout = out
            sys.stderr = out
            sys.argv = self.argv

            try:
                if app.count("virt-install"):
                    ret = virtinstall.main(conn=conn)
                elif app.count("virt-clone"):
                    ret = virtclone.main(conn=conn)
                elif app.count("virt-image"):
                    ret = virtimage.main(conn=conn)
                elif app.count("virt-convert"):
                    ret = virtconvert.main()
            except SystemExit, sys_e:
                ret = sys_e.code

            if ret != 0:
                ret = -1
            outt = out.getvalue()
            if outt.endswith("\n"):
                outt = outt[:-1]
            return (ret, outt)
        finally:
            sys.stdout = oldstdout
            sys.stderr = oldstderr
            sys.argv = oldargv


    def _get_output(self):
        try:
            for i in new_files:
                os.system("rm %s > /dev/null 2>&1" % i)

            code, output = self._launch_command()

            logging.debug(output + "\n")
            return code, output
        except Exception, e:
            return (-1, "".join(traceback.format_exc()) + str(e))

    def run(self):
        filename = self.compare_file
        err = None

        try:
            code, output = self._get_output()

            if bool(code) == self.check_success:
                raise AssertionError(
                    ("Expected command to %s, but failed.\n" %
                     (self.check_success and "pass" or "fail")) +
                     ("Command was: %s\n" % self.cmdstr) +
                     ("Error code : %d\n" % code) +
                     ("Output was:\n%s" % output))

            if filename:
                # Generate test files that don't exist yet
                if not os.path.exists(filename):
                    file(filename, "w").write(output)

                utils.diff_compare(output, filename)

        except AssertionError, e:
            err = self.cmdstr + "\n" + str(e)

        return err


class PromptCheck(object):
    """
    Individual question/response pair for automated --prompt tests
    """
    def __init__(self, prompt, response=None):
        self.prompt = prompt
        self.response = response
        if self.response:
            self.response = self.response % test_files

    def check(self, proc):
        out = proc.stdout.readline()

        if not out.count(self.prompt):
            out += "\nContent didn't contain prompt '%s'" % (self.prompt)
            return False, out

        if self.response:
            proc.stdin.write(self.response + "\n")

        return True, out


class PromptTest(Command):
    """
    Fully automated --prompt test
    """
    def __init__(self, cmdstr):
        Command.__init__(self, cmdstr)

        self.prompt_list = []

    def add(self, *args, **kwargs):
        self.prompt_list.append(PromptCheck(*args, **kwargs))

    def _launch_command(self):
        proc = subprocess.Popen(self.argv,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT)

        out = "Running %s\n" % self.cmdstr

        for p in self.prompt_list:
            ret, content = p.check(proc)
            out += content
            if not ret:
                # Since we didn't match output, process might be hung
                proc.kill()
                break

        exited = False
        for ignore in range(30):
            if proc.poll() is not None:
                exited = True
                break
            time.sleep(.1)

        if not exited:
            proc.kill()
            out += "\nProcess was killed by test harness"

        return proc.wait(), out



class App(object):
    def __init__(self, appname):
        self.appname = appname
        self.categories = {}
        self.cmds = []

    def _default_args(self, cli, iscompare):
        args = ""
        if not iscompare:
            args = "--debug"

        if self.appname != "virt-convert" and not iscompare:
            if "--connect " not in cli:
                args += " --connect %(TESTURI)s"

        if self.appname in ["virt-install"]:
            if "--name " not in cli:
                args += " --name foobar"
            if "--ram " not in cli:
                args += " --ram 64"

        if iscompare:
            if self.appname == "virt-install":
                if (not cli.count("--print-xml") and
                    not cli.count("--print-step") and
                    not cli.count("--quiet")):
                    args += " --print-step all"

            elif self.appname == "virt-image":
                if not cli.count("--print"):
                    args += " --print"

            elif self.appname == "virt-clone":
                if not cli.count("--print-xml"):
                    args += " --print-xml"

            if self.appname != "virt-convert" and not "--connect " in cli:
                args += " --connect %s" % fakeuri

        return args


    def add_category(self, catname, default_args):
        self.categories[catname] = default_args

    def _add(self, catname, testargs, valid, compfile):
        args = self.categories[catname] + " " + testargs
        args = self._default_args(args, bool(compfile)) + " " + args
        cmdstr = "./%s %s" % (self.appname, args)

        cmd = Command(cmdstr)
        cmd.check_success = valid
        if compfile:
            cmd.compare_file = "%s/%s.xml" % (compare_xmldir, compfile)
        self.cmds.append(cmd)

    def add_valid(self, cat, args):
        self._add(cat, args, True, None)
    def add_invalid(self, cat, args):
        self._add(cat, args, False, None)
    def add_compare(self, cat, args, compfile):
        self._add(cat, args, not compfile.endswith("-fail"), compfile)



#
# The test matrix
#
# add_valid: A test that should pass
# add_invalid: A test that should fail
# add_compare: Get the generated XML, and compare against the passed filename
#              in tests/clitest-xml/compare/
#

vinst = App("virt-install")
vinst.add_category("cpuram", "--hvm --nographics --noautoconsole --nodisks --pxe")
vinst.add_valid("cpuram", "--vcpus 32")  # Max VCPUS
vinst.add_valid("cpuram", "--vcpus 4 --cpuset=1,3-5")  # Cpuset
vinst.add_valid("cpuram", "--vcpus 4 --cpuset=1,3-5,")  # Cpuset with trailing comma
vinst.add_valid("cpuram", "--vcpus 4 --cpuset=auto")  # Cpuset with trailing comma
vinst.add_valid("cpuram", "--ram 100000000000")  # Ram overcommit
vinst.add_valid("cpuram", "--vcpus 5,maxvcpus=10 --check-cpu")  # maxvcpus, --check-cpu shouldn't error
vinst.add_valid("cpuram", "--vcpus 4,cores=2,threads=2,sockets=2")  # Topology
vinst.add_valid("cpuram", "--vcpus 4,cores=1")  # Topology auto-fill
vinst.add_valid("cpuram", "--vcpus sockets=2,threads=2")  # Topology only
vinst.add_valid("cpuram", "--cpu somemodel")  # Simple --cpu
vinst.add_valid("cpuram", "--cpu foobar,+x2apic,+x2apicagain,-distest,forbid=foo,forbid=bar,disable=distest2,optional=opttest,require=reqtest,match=strict,vendor=meee")  # Crazy --cpu
vinst.add_valid("cpuram", "--numatune 1,2,3,5-7,^6")  # Simple --numatune
vinst.add_invalid("cpuram", "--vcpus 32 --cpuset=969-1000")  # Bogus cpuset
vinst.add_invalid("cpuram", "--vcpus 32 --cpuset=autofoo")  # Bogus cpuset
vinst.add_invalid("cpuram", "--vcpus 10000")  # Over max vcpus
vinst.add_invalid("cpuram", "--vcpus 20 --check-cpu")  # Over host vcpus w/ --check-cpu
vinst.add_invalid("cpuram", "--vcpus 5,maxvcpus=1")  # maxvcpus less than cpus
vinst.add_invalid("cpuram", "--vcpus foo=bar")  # vcpus unknown option
vinst.add_invalid("cpuram", "--cpu host")  # --cpu host, but no host CPU in caps
vinst.add_invalid("cpuram", "--numatune 1-3,4,mode=strict")  # Non-escaped numatune


vinst.add_category("smartcard", "--noautoconsole --nodisks --pxe")
vinst.add_valid("smartcard", "--smartcard host")  # --smartcard host
vinst.add_valid("smartcard", "--smartcard none")  # --smartcard none,
vinst.add_valid("smartcard", "--smartcard passthrough,type=spicevmc")  # --smartcard mode with type
vinst.add_invalid("smartcard", "--smartcard")  # Missing argument
vinst.add_invalid("smartcard", "--smartcard foo")  # Invalid argument
vinst.add_invalid("smartcard", "--smartcard passthrough,type=foo")  # Invalid type
vinst.add_invalid("smartcard", "--smartcard host,foobar=baz")  # --smartcard bogus


vinst.add_category("xen", "--connect %(XENURI)s --noautoconsole")
vinst.add_compare("xen", "--disk %(EXISTIMG1)s --import", "xen-default")  # Xen default
vinst.add_compare("xen", "--disk %(EXISTIMG1)s --location %(TREEDIR)s --paravirt", "xen-pv")  # Xen PV
vinst.add_compare("xen", "--disk %(EXISTIMG1)s --cdrom %(EXISTIMG1)s --livecd --hvm", "xen-hvm")  # Xen HVM
vinst.add_compare("xen", "--connect %(XENIA64URI)s --disk %(EXISTIMG1)s --import", "xen-ia64-default")  # ia64 default
vinst.add_compare("xen", "--connect %(XENIA64URI)s --disk %(EXISTIMG1)s --location %(TREEDIR)s --paravirt", "xen-ia64-pv")  # ia64 pv
vinst.add_compare("xen", "--connect %(XENIA64URI)s --disk %(EXISTIMG1)s --location %(TREEDIR)s --hvm", "xen-ia64-hvm")  # ia64 hvm
vinst.add_valid("xen", "--nodisks --cdrom %(EXISTIMG1)s --livecd --hvm")  # HVM
vinst.add_valid("xen", "--nodisks --boot hd --paravirt")  # PV
vinst.add_valid("xen", "--nodisks --boot hd --paravirt --arch i686")  # 32 on 64 xen


vinst.add_category("kvm", "--connect %(KVMURI)s --noautoconsole")
vinst.add_compare("kvm", "--os-variant fedora14 --file %(EXISTIMG1)s --location %(TREEDIR)s --extra-args console=ttyS0 --cpu host", "kvm-f14-url")  # F14 Directory tree URL install with extra-args
vinst.add_compare("kvm", "--os-variant fedora14 --disk %(NEWIMG1)s,size=.01 --location %(TREEDIR)s --extra-args console=ttyS0 --quiet", "quiet-url")  # Quiet URL install should make no noise
vinst.add_compare("kvm", "--cdrom %(EXISTIMG2)s --file %(EXISTIMG1)s --os-variant win2k3 --wait 0 --sound", "kvm-win2k3-cdrom")  # HVM windows install with disk
vinst.add_compare("kvm", "--os-variant fedora14 --nodisks --boot hd --paravirt", "kvm-xenner")  # xenner
vinst.add_compare("kvm", "--os-variant fedora14 --nodisks --boot cdrom --virt-type qemu --cpu Penryn", "qemu-plain")  # plain qemu
vinst.add_compare("kvm", "--os-variant fedora14 --nodisks --boot network --nographics --arch i686", "qemu-32-on-64")  # 32 on 64
vinst.add_compare("kvm", "--os-variant fedora14 --nodisks --boot fd --graphics spice --machine pc", "kvm-machine")  # kvm machine type 'pc'
vinst.add_compare("kvm", "--os-variant fedora14 --nodisks --boot fd --graphics sdl --arch sparc --machine SS-20", "qemu-sparc")  # exotic arch + machine type
vinst.add_valid("kvm", "--cdrom %(EXISTIMG2)s --file %(EXISTIMG1)s --os-variant win2k3 --wait 0 --sound")  # HVM windows install with disk
vinst.add_valid("kvm", "--os-variant fedora14 --file %(EXISTIMG1)s --location %(TREEDIR)s --extra-args console=ttyS0 --sound")  # F14 Directory tree URL install with extra-args
vinst.add_invalid("kvm", "--nodisks --boot network --machine foobar")  # Unknown machine type
vinst.add_invalid("kvm", "--nodisks --boot network --arch mips --virt-type kvm")  # Invalid domain type for arch
vinst.add_invalid("kvm", "--nodisks --boot network --paravirt --arch mips")  # Invalid arch/virt combo


vinst.add_category("misc", "--nographics --noautoconsole")
vinst.add_compare("misc", "", "noargs-fail")  # No arguments
vinst.add_compare("misc", "--hvm --nodisks --pxe --print-step all", "simple-pxe")  # Diskless PXE install
vinst.add_compare("misc", "--hvm --cdrom %(EXISTIMG2)s --file %(EXISTIMG1)s --os-variant win2k3 --wait 0 --vcpus cores=4", "w2k3-cdrom")  # HVM windows install with disk
vinst.add_compare("misc", """--hvm --pxe --controller usb,model=ich9-ehci1,address=0:0:4.7,index=0 --controller usb,model=ich9-uhci1,address=0:0:4.0,index=0,master=0 --controller usb,model=ich9-uhci2,address=0:0:4.1,index=0,master=2 --controller usb,model=ich9-uhci3,address=0:0:4.2,index=0,master=4 --disk %(MANAGEDEXISTUPPER)s,cache=writeback,io=threads,perms=sh,serial=WD-WMAP9A966149 --disk %(NEWIMG1)s,sparse=false,size=.001,perms=ro,error_policy=enospace --disk device=cdrom,bus=sata --serial tcp,host=:2222,mode=bind,protocol=telnet --filesystem /source,/target,mode=squash --network user,mac=12:34:56:78:11:22 --network bridge=foobar,model=virtio --channel spicevmc --smartcard passthrough,type=spicevmc --security type=static,label='system_u:object_r:svirt_image_t:s0:c100,c200',relabel=yes  --numatune \\"1-3,5\\",mode=preferred --boot loader=/foo/bar """, "many-devices")  # Lot's of devices
vinst.add_compare("misc", "--connect %(DEFAULTURI)s --hvm --nodisks --pxe --cpuset auto --vcpus 2", "cpuset-auto")  # --cpuset=auto actually works
vinst.add_valid("misc", "--hvm --disk path=virt-install,device=cdrom")  # Specifying cdrom media via --disk
vinst.add_valid("misc", "--hvm --import --disk path=virt-install")  # FV Import install
vinst.add_valid("misc", "--hvm --import --disk path=virt-install --prompt --force")  # Working scenario w/ prompt shouldn't ask anything
vinst.add_valid("misc", "--paravirt --import --disk path=virt-install")  # PV Import install
vinst.add_valid("misc", "--paravirt --import --disk path=virt-install --print-xml")  # PV Import install, print single XML
vinst.add_valid("misc", "--hvm --import --disk path=virt-install,device=floppy")  # Import a floppy disk
vinst.add_valid("misc", "--hvm --nodisks --pxe --autostart")  # --autostart flag
vinst.add_valid("misc", "--hvm --nodisks --pxe --description \"foobar & baz\"")  # --description
vinst.add_valid("misc", "--hvm --cdrom %(EXISTIMG2)s --file %(EXISTIMG1)s --os-variant win2k3 --wait 0")  # HVM windows install with disk
vinst.add_valid("misc", "--hvm --cdrom %(EXISTIMG2)s --file %(EXISTIMG1)s --os-variant win2k3 --wait 0 --print-step 3")  # HVM windows install, print 3rd stage XML
vinst.add_valid("misc", "--hvm --nodisks --pxe --watchdog default")  # --watchdog dev default
vinst.add_valid("misc", "--hvm --nodisks --pxe --watchdog ib700,action=pause")  # --watchdog opts
vinst.add_valid("misc", "--hvm --nodisks --pxe --sound")  # --sound option
vinst.add_valid("misc", "--hvm --nodisks --pxe --soundhw default --soundhw ac97")  # --soundhw option
vinst.add_valid("misc", "--hvm --nodisks --pxe --security type=dynamic")  # --security dynamic
vinst.add_valid("misc", "--hvm --nodisks --pxe --security label=foobar.label,relabel=yes")  # --security implicit static
vinst.add_valid("misc", "--hvm --nodisks --pxe --security label=foobar.label,a1,z2,b3,type=static,relabel=no")  # --security static with commas 1
vinst.add_valid("misc", "--hvm --nodisks --pxe --security label=foobar.label,a1,z2,b3")  # --security static with commas 2
vinst.add_valid("misc", "--hvm --pxe --filesystem /foo/source,/bar/target")  # --filesystem simple
vinst.add_valid("misc", "--hvm --pxe --filesystem template_name,/,type=template")  # --filesystem template
vinst.add_valid("misc", "--hvm --nodisks --nonetworks --cdrom %(EXISTIMG1)s")  # no networks
vinst.add_valid("misc", "--hvm --nodisks --pxe --memballoon virtio")  # --memballoon use virtio
vinst.add_valid("misc", "--hvm --nodisks --pxe --memballoon none")  # --memballoon disabled
vinst.add_invalid("misc", "--hvm --nodisks --pxe foobar")  # Positional arguments error
vinst.add_invalid("misc", "--nodisks --pxe --nonetworks")  # pxe and nonetworks
vinst.add_invalid("misc", "--nodisks --pxe --name test")  # Colliding name
vinst.add_invalid("misc", "--hvm --nodisks --pxe --watchdog default,action=foobar")  # Busted --watchdog
vinst.add_invalid("misc", "--hvm --nodisks --pxe --soundhw default --soundhw foobar")  # Busted --soundhw
vinst.add_invalid("misc", "--hvm --nodisks --pxe --security type=foobar")  # Busted --security
vinst.add_invalid("misc", "--paravirt --import --disk path=virt-install --print-step 2")  # PV Import install, no second XML step
vinst.add_invalid("misc", "--hvm --nodisks --pxe --print-xml")  # 2 stage install with --print-xml
vinst.add_invalid("misc", "--hvm --nodisks --pxe --memballoon foobar")  # Busted --memballoon


vinst.add_category("char", "--hvm --nographics --noautoconsole --nodisks --pxe")
vinst.add_valid("char", "--serial pty --parallel null")  # Simple devs
vinst.add_valid("char", "--serial file,path=/tmp/foo --parallel unix,path=/tmp/foo --parallel null")  # Some with options
vinst.add_valid("char", "--parallel udp,host=0.0.0.0:1234,bind_host=127.0.0.1:1234")  # UDP
vinst.add_valid("char", "--serial tcp,mode=bind,host=0.0.0.0:1234")  # TCP
vinst.add_valid("char", "--parallel unix,path=/tmp/foo-socket")  # Unix
vinst.add_valid("char", "--serial tcp,host=:1234,protocol=telnet")  # TCP w/ telnet
vinst.add_valid("char", "--channel pty,target_type=guestfwd,target_address=127.0.0.1:10000")  # --channel guestfwd
vinst.add_valid("char", "--channel pty,target_type=virtio,name=org.linux-kvm.port1")  # --channel virtio
vinst.add_valid("char", "--channel pty,target_type=virtio")  # --channel virtio without name=
vinst.add_valid("char", "--console pty,target_type=virtio")  # --console virtio
vinst.add_valid("char", "--console pty,target_type=xen")  # --console xen
vinst.add_invalid("char", "--parallel foobah")  # Bogus device type
vinst.add_invalid("char", "--serial unix")  # Unix with no path
vinst.add_invalid("char", "--serial null,path=/tmp/foo")  # Path where it doesn't belong
vinst.add_invalid("char", "--serial udp,host=:1234,frob=baz")  # Nonexistent argument
vinst.add_invalid("char", "--channel pty,target_type=guestfwd")  # --channel guestfwd without target_address
vinst.add_invalid("char", "--console pty,target_type=abcd")  # --console unknown type


vinst.add_category("controller", "--noautoconsole --nodisks --pxe")
vinst.add_valid("controller", "--controller usb,model=ich9-ehci1,address=0:0:4.7")
vinst.add_valid("controller", "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=0")
vinst.add_valid("controller", "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=1")
vinst.add_valid("controller", "--controller usb2")
vinst.add_invalid("controller", "--controller")  # Missing argument
vinst.add_invalid("controller", "--controller foo")  # Invalid argument
vinst.add_invalid("controller", "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=bar,master=foo")  # Invalid values
vinst.add_invalid("controller", "--controller host,foobar=baz")  # --bogus


vinst.add_category("lxc", "--connect %(LXCURI)s --noautoconsole --name foolxc --ram 64")
vinst.add_compare("lxc", "", "default")
vinst.add_compare("lxc", "--filesystem /source,/", "fs-default")
vinst.add_compare("lxc", "--init /usr/bin/httpd", "manual-init")


vinst.add_category("graphics", "--noautoconsole --nodisks --pxe")
vinst.add_valid("graphics", "--sdl")  # SDL
vinst.add_valid("graphics", "--graphics sdl")  # --graphics SDL
vinst.add_valid("graphics", "--graphics none")  # --graphics none,
vinst.add_valid("graphics", "--vnc --keymap ja --vncport 5950 --vnclisten 1.2.3.4")  # VNC w/ lots of options
vinst.add_valid("graphics", "--graphics vnc,port=5950,listen=1.2.3.4,keymap=ja,password=foo")  # VNC w/ lots of options, new way
vinst.add_valid("graphics", "--graphics spice,port=5950,tlsport=5950,listen=1.2.3.4,keymap=ja")  # SPICE w/ lots of options
vinst.add_valid("graphics", "--vnc --video vga")  # --video option
vinst.add_valid("graphics", "--graphics spice --video qxl")  # --video option
vinst.add_valid("graphics", "--vnc --keymap local")  # --keymap local,
vinst.add_valid("graphics", "--vnc --keymap none")  # --keymap none
vinst.add_invalid("graphics", "--vnc --keymap ZZZ")  # Invalid keymap
vinst.add_invalid("graphics", "--vnc --vncport -50")  # Invalid port
vinst.add_invalid("graphics", "--graphics spice,tlsport=-50")  # Invalid port
vinst.add_invalid("graphics", "--vnc --video foobar")  # Invalid --video
vinst.add_invalid("graphics", "--graphics vnc,foobar=baz")  # --graphics bogus
vinst.add_invalid("graphics", "--graphics vnc --vnclisten 1.2.3.4")  # mixing old and new


vinst.add_category("remote", "--connect %(REMOTEURI)s --nographics --noautoconsole")
vinst.add_valid("remote", "--nodisks --pxe")  # Simple pxe nodisks
vinst.add_valid("remote", "--nodisks --cdrom %(MANAGEDEXIST1)s")  # Managed CDROM install
vinst.add_valid("remote", "--pxe --file %(MANAGEDEXIST1)s")  # Using existing managed storage
vinst.add_valid("remote", "--pxe --disk vol=%(POOL)s/%(VOL)s")  # Using existing managed storage 2
vinst.add_valid("remote", "--pxe --disk pool=%(POOL)s,size=.04")  # Creating storage on managed pool
vinst.add_invalid("remote", "--nodisks --location /tmp")  # Use of --location
vinst.add_invalid("remote", "--file %(EXISTIMG1)s --pxe")  # Trying to use unmanaged storage


vinst.add_category("network", "--pxe --nographics --noautoconsole --nodisks")
vinst.add_valid("network", "--mac 22:22:33:44:55:AF")  # Just a macaddr
vinst.add_valid("network", "--network=user")  # user networking
vinst.add_valid("network", "--bridge mybr0")  # Old bridge option
vinst.add_valid("network", "--bridge mybr0 --mac 22:22:33:44:55:AF")  # Old bridge w/ mac
vinst.add_valid("network", "--network bridge:mybr0,model=e1000")  # --network bridge:
vinst.add_valid("network", "--network network:default --mac RANDOM")  # VirtualNetwork with a random macaddr
vinst.add_valid("network", "--network network:default --mac 00:11:22:33:44:55")  # VirtualNetwork with a random macaddr
vinst.add_valid("network", "--network network=default,mac=22:00:11:00:11:00")  # Using '=' as the net type delimiter
vinst.add_valid("network", "--network=user,model=e1000")  # with NIC model
vinst.add_valid("network", "--network=network:default,model=e1000 --network=user,model=virtio,mac=22:22:33:44:55:AF")  # several networks
vinst.add_invalid("network", "--network=FOO")  # Nonexistent network
vinst.add_invalid("network", "--network=network:default --mac 1234")  # Invalid mac
vinst.add_invalid("network", "--network user --bridge foo0")  # Mixing bridge and network
vinst.add_invalid("network", "--mac 22:22:33:12:34:AB")  # Colliding macaddr


vinst.add_category("storage", "--pxe --nographics --noautoconsole --hvm")
vinst.add_valid("storage", "--file %(EXISTIMG1)s --nonsparse --file-size 4")  # Existing file, other opts
vinst.add_valid("storage", "--file %(EXISTIMG1)s")  # Existing file, no opts
vinst.add_valid("storage", "--file %(EXISTIMG1)s --file virt-image --file virt-clone")  # Multiple existing files
vinst.add_valid("storage", "--file %(NEWIMG1)s --file-size .00001 --nonsparse")  # Nonexistent file
vinst.add_valid("storage", "--disk path=%(EXISTIMG1)s,perms=ro,size=.0001,cache=writethrough,io=threads")  # Existing disk, lots of opts
vinst.add_valid("storage", "--disk path=%(EXISTIMG1)s,perms=rw")  # Existing disk, rw perms
vinst.add_valid("storage", "--disk path=%(EXISTIMG1)s,device=floppy")  # Existing floppy
vinst.add_valid("storage", "--disk path=%(EXISTIMG1)s")  # Existing disk, no extra options
vinst.add_valid("storage", "--disk pool=%(POOL)s,size=.0001 --disk pool=%(POOL)s,size=.0001")  # Create 2 volumes in a pool
vinst.add_valid("storage", "--disk vol=%(POOL)s/%(VOL)s")  # Existing volume
vinst.add_valid("storage", "--disk path=%(EXISTIMG1)s --disk path=%(EXISTIMG1)s --disk path=%(EXISTIMG1)s --disk path=%(EXISTIMG1)s,device=cdrom")  # 3 IDE and CD
vinst.add_valid("storage", " --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi --disk path=%(EXISTIMG1)s,bus=scsi")  # > 16 scsi disks
vinst.add_valid("storage", "--disk path=%(NEWIMG1)s,format=raw,size=.0000001")  # Unmanaged file using format 'raw'
vinst.add_valid("storage", "--disk path=%(MANAGEDNEW1)s,format=raw,size=.0000001")  # Managed file using format raw
vinst.add_valid("storage", "--disk path=%(MANAGEDNEW1)s,format=qcow2,size=.0000001")  # Managed file using format qcow2
vinst.add_valid("storage", "--disk path=%(ROIMG)s,perms=ro")  # Using ro path as a disk with readonly flag
vinst.add_valid("storage", "--disk path=%(ROIMG)s,device=cdrom")  # Using RO path with cdrom dev
vinst.add_valid("storage", "--disk %(EXISTIMG1)s")  # Not specifying path=
vinst.add_valid("storage", "--disk %(NEWIMG1)s,format=raw,size=.0000001")  # Not specifying path= but creating storage
vinst.add_valid("storage", "--disk %(COLLIDE)s --force")  # Colliding storage with --force
vinst.add_valid("storage", "--disk %(SHARE)s,perms=sh")  # Colliding shareable storage
vinst.add_valid("storage", "--disk path=%(EXISTIMG1)s,device=cdrom --disk path=%(EXISTIMG1)s,device=cdrom")  # Two IDE cds
vinst.add_valid("storage", "--disk %(DIR)s,device=floppy")  # Dir with a floppy dev
vinst.add_valid("storage", "--disk %(EXISTIMG1)s,driver_name=qemu,driver_type=qcow2")  # Driver name and type options
vinst.add_valid("storage", "--disk /dev/hda")  # Using a storage pool source as a disk
vinst.add_valid("storage", "--disk pool=default,size=.00001")  # Building 'default' pool
vinst.add_invalid("storage", "--file %(NEWIMG1)s --file-size 100000 --nonsparse")  # Nonexisting file, size too big
vinst.add_invalid("storage", "--file %(NEWIMG1)s --file-size 100000")  # Huge file, sparse, but no prompting
vinst.add_invalid("storage", "--file %(NEWIMG1)s")  # Nonexisting file, no size
vinst.add_invalid("storage", "--file %(EXISTIMG1)s --file %(EXISTIMG1)s --file %(EXISTIMG1)s --file %(EXISTIMG1)s --file %(EXISTIMG1)s")  # Too many IDE
vinst.add_invalid("storage", "--file-size .0001")  # Size, no file
vinst.add_invalid("storage", "--disk pool=foopool,size=.0001")  # Specify a nonexistent pool
vinst.add_invalid("storage", "--disk vol=%(POOL)s/foovol")  # Specify a nonexistent volume
vinst.add_invalid("storage", "--disk pool=%(POOL)s")  # Specify a pool with no size
vinst.add_invalid("storage", "--disk path=%(EXISTIMG1)s,perms=ro,size=.0001,cache=FOOBAR")  # Unknown cache type
vinst.add_invalid("storage", "--disk path=%(NEWIMG1)s,format=qcow2,size=.0000001")  # Unmanaged file using non-raw format
vinst.add_invalid("storage", "--disk path=%(MANAGEDNEW1)s,format=frob,size=.0000001")  # Managed file using unknown format
vinst.add_invalid("storage", "--disk path=%(MANAGEDDISKNEW1)s,format=raw,size=.0000001")  # Managed disk using any format
vinst.add_invalid("storage", "--disk %(NEWIMG1)s")  # Not specifying path= and non existent storage w/ no size
vinst.add_invalid("storage", "--disk %(COLLIDE)s")  # Colliding storage without --force
vinst.add_invalid("storage", "--disk %(DIR)s,device=cdrom")  # Dir without floppy
vinst.add_invalid("storage", "--disk %(EXISTIMG1)s,driver_name=foobar,driver_type=foobaz")  # Unknown driver name and type options (as of 1.0.0)


vinst.add_category("redirdev", "--noautoconsole --nographics --nodisks --pxe")
vinst.add_valid("redirdev", "--redirdev usb,type=spicevmc")
vinst.add_valid("redirdev", "--redirdev usb,type=tcp,server=localhost:4000")
vinst.add_valid("redirdev", "--redirdev usb,type=tcp,server=127.0.0.1:4002")  # Different host server
vinst.add_invalid("redirdev", "--redirdev")  # Missing argument
vinst.add_invalid("redirdev", "--redirdev pci")  # Unsupported bus
vinst.add_invalid("redirdev", "--redirdev usb,type=spicevmc,server=foo:12")  # Invalid argument
vinst.add_invalid("redirdev", "--redirdev usb,type=tcp,server=")  # Missing argument
vinst.add_invalid("redirdev", "--redirdev usb,type=tcp,server=localhost:p4000")  # Invalid address
vinst.add_invalid("redirdev", "--redirdev usb,type=tcp,server=localhost:")  # Missing address
vinst.add_invalid("redirdev", "--redirdev usb,type=tcp,server=:399")  # Missing host


vinst.add_category("hostdev", "--noautoconsole --nographics --nodisks --pxe")
vinst.add_valid("hostdev", "--host-device usb_device_781_5151_2004453082054CA1BEEE")  # Host dev by libvirt name
vinst.add_valid("hostdev", "--host-device 001.003 --host-device 15:0.1 --host-device 2:15:0.2 --host-device 0:15:0.3 --host-device 0x0781:0x5151")  # Many hostdev parsing types
vinst.add_invalid("hostdev", "--host-device 1d6b:2")  # multiple USB devices with identical vendorId and productId
vinst.add_invalid("hostdev", "--host-device pci_8086_2850_scsi_host_scsi_host")  # Unsupported hostdev type
vinst.add_invalid("hostdev", "--host-device foobarhostdev")  # Unknown hostdev
vinst.add_invalid("hostdev", "--host-device 300:400")  # Parseable hostdev, but unknown digits


vinst.add_category("install", "--nographics --noautoconsole --nodisks")
vinst.add_valid("install", "--hvm --cdrom %(EXISTIMG1)s")  # Simple cdrom install
vinst.add_valid("install", "--hvm --cdrom %(MANAGEDEXIST1)s")  # Cdrom install with managed storage
vinst.add_valid("install", "--hvm --wait 0 --os-variant winxp --cdrom %(EXISTIMG1)s")  # Windows (2 stage) install
vinst.add_valid("install", "--hvm --pxe --virt-type test")  # Explicit virt-type
vinst.add_valid("install", "--arch i686 --pxe")  # Explicity fullvirt + arch
vinst.add_valid("install", "--arch i486 --pxe")  # Convert i*86 -> i686
vinst.add_valid("install", "--hvm --location %(TREEDIR)s")  # Directory tree URL install
vinst.add_valid("install", "--hvm --location %(TREEDIR)s --initrd-inject virt-install --extra-args ks=file:/virt-install")  # initrd-inject
vinst.add_valid("install", "--hvm --location %(TREEDIR)s --extra-args console=ttyS0")  # Directory tree URL install with extra-args
vinst.add_valid("install", "--hvm --cdrom %(TREEDIR)s")  # Directory tree CDROM install
vinst.add_valid("install", "--paravirt --location %(TREEDIR)s")  # Paravirt location
vinst.add_valid("install", "--hvm --cdrom %(ROIMG)s")  # Using ro path as a cd media
vinst.add_valid("install", "--paravirt --location %(TREEDIR)s --os-variant none")  # Paravirt location with --os-variant none
vinst.add_valid("install", "--hvm --location %(TREEDIR)s --os-variant fedora12")  # URL install with manual os-variant
vinst.add_valid("install", "--hvm --pxe --boot menu=on")  # Boot menu
vinst.add_valid("install", "--hvm --pxe --boot kernel=/tmp/foo1.img,initrd=/tmp/foo2.img,kernel_args='ro quiet console=/dev/ttyS0' ")  # Kernel params
vinst.add_valid("install", "--hvm --pxe --boot cdrom,fd,hd,network,menu=off")  # Boot order
vinst.add_valid("install", "--hvm --boot network,hd,menu=on")  # Boot w/o other install option
vinst.add_invalid("install", "--hvm --pxe --virt-type bogus")  # Bogus virt-type
vinst.add_invalid("install", "--hvm --pxe --arch bogus")  # Bogus arch
vinst.add_invalid("install", "--paravirt --pxe")  # PXE w/ paravirt
vinst.add_invalid("install", "--import")  # Import with no disks
vinst.add_invalid("install", "--livecd")  # LiveCD with no media
vinst.add_invalid("install", "--hvm --pxe --os-variant farrrrrrrge# Boot menu w/ bogus value ")  # Bogus --os-variant
vinst.add_invalid("install", "--hvm --pxe --boot menu=foobar")
vinst.add_invalid("install", "--hvm --cdrom %(EXISTIMG1)s --extra-args console=ttyS0")  # cdrom fail w/ extra-args
vinst.add_invalid("install", "--hvm --boot kernel=%(TREEDIR)s/pxeboot/vmlinuz,initrd=%(TREEDIR)s/pxeboot/initrd.img --initrd-inject virt-install")  # initrd-inject with manual kernel/initrd




vimag = App("virt-image")
vimag.add_category("graphics", "--name test-image --boot 0 %(IMAGE_XML)s")
vimag.add_valid("graphics", "--sdl")  # SDL
vimag.add_valid("graphics", "--vnc --keymap ja --vncport 5950 --vnclisten 1.2.3.4")  # VNC w/ lots of options


vimag.add_category("misc", "")
vimag.add_compare("misc", "--name foobar --ram 64 --os-variant winxp --boot 0 %(IMAGE_XML)s", "image-boot0")
vimag.add_compare("misc", "--name foobar --ram 64 --network user,model=e1000 --boot 1 %(IMAGE_XML)s", "image-boot1")
vimag.add_compare("misc", "--name foobar --ram 64 --boot 0 %(IMAGE_NOGFX_XML)s", "image-nogfx")
vimag.add_valid("misc", "--name test --replace %(IMAGE_XML)s")  # Colliding VM name w/ --replace
vimag.add_invalid("misc", "%(IMAGE_XML)s")  # No name specified, and no prompt flag
vimag.add_invalid("misc", "--name test %(IMAGE_XML)s")  # Colliding VM name without --replace


vimag.add_category("network", "--name test-image --boot 0 --nographics %(IMAGE_XML)s")
vimag.add_valid("network", "--network=user")  # user networking
vimag.add_valid("network", "--network network:default --mac RANDOM")  # VirtualNetwork with a random macaddr
vimag.add_valid("network", "--network network:default --mac 00:11:22:33:44:55")  # VirtualNetwork with a random macaddr
vimag.add_valid("network", "--network=user,model=e1000")  # with NIC model
vimag.add_valid("network", "--network=network:default,model=e1000 --network=user,model=virtio")  # several networks
vimag.add_invalid("network", "--network=FOO")  # Nonexistent network
vimag.add_invalid("network", "--network=network:default --mac 1234")  # Invalid mac


vimag.add_category("general", "--name test-image %(IMAGE_XML)s")
vimag.add_valid("general", "")  # All default values
vimag.add_valid("general", "--print")  # Print default
vimag.add_valid("general", "--boot 0")  # Manual boot idx 0
vimag.add_valid("general", "--boot 1")  # Manual boot idx 1
vimag.add_valid("general", "--name foobar --ram 64 --os-variant winxp")  # Lots of options
vimag.add_valid("general", "--name foobar --ram 64 --os-variant none")  # OS variant 'none'
vimag.add_invalid("general", "--boot 10")  # Out of bounds index




vconv = App("virt-convert")
vconv.add_category("misc", "")
vconv.add_compare("misc", "%(VC_IMG1)s %(VIRTCONV_OUT)s", "convert-default")  # virt-image to default (virt-image) w/ no convert
vconv.add_valid("misc", "%(VC_IMG1)s -D none %(VIRTCONV_OUT)s")  # virt-image to default (virt-image) w/ no convert
vconv.add_valid("misc", "%(VC_IMG1)s -o virt-image -D none %(VIRTCONV_OUT)s")  # virt-image to virt-image w/ no convert
vconv.add_valid("misc", "%(VC_IMG1)s -o vmx -D none %(VIRTCONV_OUT)s")  # virt-image to vmx w/ no convert
vconv.add_valid("misc", "%(VC_IMG1)s -o vmx -D raw %(VIRTCONV_OUT)s")  # virt-image to vmx w/ raw
vconv.add_valid("misc", "%(VC_IMG1)s -o vmx -D vmdk %(VIRTCONV_OUT)s")  # virt-image to vmx w/ vmdk
vconv.add_valid("misc", "%(VC_IMG1)s -o vmx -D qcow2 %(VIRTCONV_OUT)s")  # virt-image to vmx w/ qcow2
vconv.add_valid("misc", "%(VMX_IMG1)s -o vmx -D none %(VIRTCONV_OUT)s")  # vmx to vmx no convert
vconv.add_valid("misc", "%(VC_IMG2)s -o vmx -D vmdk %(VIRTCONV_OUT)s")  # virt-image with exotic formats specified
vconv.add_invalid("misc", "%(VC_IMG1)s -o virt-image -D foobarfmt %(VIRTCONV_OUT)s")  # virt-image to virt-image with invalid format
vconv.add_invalid("misc", "%(VC_IMG1)s -o ovf %(VIRTCONV_OUT)s")  # virt-image to ovf (has no output formatter)




vclon = App("virt-clone")
vclon.add_category("remote", "--connect %(REMOTEURI)s")
vclon.add_valid("remote", "-o test --auto-clone")  # Auto flag, no storage
vclon.add_valid("remote", "--original-xml %(CLONE_STORAGE_XML)s --auto-clone")  # Auto flag w/ managed storage,
vclon.add_invalid("remote", "--original-xml %(CLONE_DISK_XML)s --auto-clone")  # Auto flag w/ storage,


vclon.add_category("misc", "")
vclon.add_compare("misc", "--connect %(KVMURI)s -o test-for-clone --auto-clone --clone-running", "clone-auto1")
vclon.add_compare("misc", "-o test-clone-simple --name newvm --auto-clone --clone-running", "clone-auto2")
vclon.add_valid("misc", "-o test --auto-clone")  # Auto flag, no storage
vclon.add_valid("misc", "--original-xml %(CLONE_DISK_XML)s --auto-clone")  # Auto flag w/ storage,
vclon.add_valid("misc", "--original-xml %(CLONE_STORAGE_XML)s --auto-clone")  # Auto flag w/ managed storage,
vclon.add_valid("misc", "-o test-for-clone --auto-clone --clone-running")  # Auto flag, actual VM, skip state check
vclon.add_valid("misc", "-o test-clone-simple -n newvm --preserve-data --file /default-pool/default-vol --clone-running --force")  # Preserve data shouldn't complain about existing volume
vclon.add_invalid("misc", "--auto-clone# Auto flag, actual VM, without state skip ")  # Just the auto flag
vclon.add_invalid("misc", "-o test-for-clone --auto-clone")


vclon.add_category("general", "-n clonetest")
vclon.add_valid("general", "-o test")  # Nodisk guest
vclon.add_valid("general", "-o test --file %(NEWIMG1)s --file %(NEWIMG2)s")  # Nodisk, but with spurious files passed
vclon.add_valid("general", "-o test --file %(NEWIMG1)s --file %(NEWIMG2)s --prompt")  # Working scenario w/ prompt shouldn't ask anything
vclon.add_valid("general", "--original-xml %(CLONE_DISK_XML)s --file %(NEWIMG1)s --file %(NEWIMG2)s")  # XML File with 2 disks
vclon.add_valid("general", "--original-xml %(CLONE_DISK_XML)s --file virt-install --file %(EXISTIMG1)s --preserve")  # XML w/ disks, overwriting existing files with --preserve
vclon.add_valid("general", "--original-xml %(CLONE_DISK_XML)s --file %(NEWIMG1)s --file %(NEWIMG2)s --file %(NEWIMG3)s --force-copy=hdc")  # XML w/ disks, force copy a readonly target
vclon.add_valid("general", "--original-xml %(CLONE_DISK_XML)s --file %(NEWIMG1)s --file %(NEWIMG2)s --force-copy=fda")  # XML w/ disks, force copy a target with no media
vclon.add_valid("general", "--original-xml %(CLONE_STORAGE_XML)s --file %(MANAGEDNEW1)s")  # XML w/ managed storage, specify managed path
vclon.add_valid("general", "--original-xml %(CLONE_NOEXIST_XML)s --file %(EXISTIMG1)s --preserve")  # XML w/ managed storage, specify managed path across pools# Libvirt test driver doesn't support cloning across pools# XML w/ non-existent storage, with --preserve
vclon.add_valid("general", "-o test -n test-many-devices --replace")  # Overwriting existing VM
vclon.add_invalid("general", "-o test foobar")  # Positional arguments error
vclon.add_invalid("general", "-o idontexist")  # Non-existent vm name
vclon.add_invalid("general", "-o idontexist --auto-clone")  # Non-existent vm name with auto flag,
vclon.add_invalid("general", "-o test -n test")  # Colliding new name
vclon.add_invalid("general", "--original-xml %(CLONE_DISK_XML)s")  # XML file with several disks, but non specified
vclon.add_invalid("general", "--original-xml %(CLONE_DISK_XML)s --file virt-install --file %(EXISTIMG1)s")  # XML w/ disks, overwriting existing files with no --preserve
vclon.add_invalid("general", "--original-xml %(CLONE_DISK_XML)s --file %(NEWIMG1)s --file %(NEWIMG2)s --force-copy=hdc")  # XML w/ disks, force copy but not enough disks passed
vclon.add_invalid("general", "--original-xml %(CLONE_STORAGE_XML)s --file /tmp/clonevol")  # XML w/ managed storage, specify unmanaged path (should fail)
vclon.add_invalid("general", "--original-xml %(CLONE_NOEXIST_XML)s --file %(EXISTIMG1)s")  # XML w/ non-existent storage, WITHOUT --preserve
vclon.add_invalid("general", "--original-xml %(CLONE_DISK_XML)s --file %(ROIMG)s --file %(ROIMG)s --force")  # XML w/ managed storage, specify RO image without preserve
vclon.add_invalid("general", "--original-xml %(CLONE_DISK_XML)s --file %(ROIMG)s --file %(ROIMGNOEXIST)s --force")  # XML w/ managed storage, specify RO non existent





##########################
# Automated prompt tests #
##########################

# Basic virt-install prompting
p1 = PromptTest("virt-install --connect %(TESTURI)s --prompt --quiet "
               "--noautoconsole")
p1.add("fully virtualized", "yes")
p1.add("What is the name", "foo")
p1.add("How much RAM", "64")
p1.add("use as the disk", "%(NEWIMG1)s")
p1.add("large would you like the disk", ".00001")
p1.add("CD-ROM/ISO or URL", "%(EXISTIMG1)s")
promptlist.append(p1)

# Basic virt-install kvm prompting, existing disk
p2 = PromptTest("virt-install --connect %(KVMURI)s --prompt --quiet "
               "--noautoconsole --name foo --ram 64 --pxe --hvm")
p2.add("use as the disk", "%(EXISTIMG1)s")
p2.add("overwrite the existing path")
p2.add("want to use this disk", "yes")
promptlist.append(p2)

# virt-install with install and --file-size and --hvm specified
p3 = PromptTest("virt-install --connect %(TESTURI)s --prompt --quiet "
               "--noautoconsole --pxe --file-size .00001 --hvm")
p3.add("What is the name", "foo")
p3.add("How much RAM", "64")
p3.add("enter the path to the file", "%(NEWIMG1)s")
promptlist.append(p3)

# Basic virt-image prompting
p4 = PromptTest("virt-image --connect %(TESTURI)s %(IMAGE_XML)s "
               "--prompt --quiet --noautoconsole")
# prompting for virt-image currently disabled
#promptlist.append(p4)

# Basic virt-clone prompting
p5 = PromptTest("virt-clone --connect %(TESTURI)s --prompt --quiet "
               "--clone-running")
p5.add("original virtual machine", "test-clone-simple")
p5.add("cloned virtual machine", "test-clone-new")
p5.add("use as the cloned disk", "%(MANAGEDNEW1)s")
promptlist.append(p5)

# virt-clone prompt with input XML
p6 = PromptTest("virt-clone --connect %(TESTURI)s --prompt --quiet "
               "--original-xml %(CLONE_DISK_XML)s --clone-running")
p6.add("cloned virtual machine", "test-clone-new")
p6.add("use as the cloned disk", "%(NEWIMG1)s")
p6.add("use as the cloned disk", "%(NEWIMG2)s")
promptlist.append(p6)

# Basic virt-clone prompting with disk failure handling
p7 = PromptTest("virt-clone --connect %(TESTURI)s --prompt --quiet "
               "--clone-running -o test-clone-simple -n test-clone-new")
p7.add("use as the cloned disk", "/root")
p7.add("'/root' must be a file or a device")
p7.add("use as the cloned disk", "%(MANAGEDNEW1)s")
promptlist.append(p7)



#########################
# Test runner functions #
#########################


def open_conn(uri):
    #if uri not in _conns:
    #    _conns[uri] = virtinst.cli.getConnection(uri)
    #return _conns[uri]
    return virtinst.cli.getConnection(uri)


newidx = 0
curtest = 0
old_bridge = virtinst.util.default_bridge


def setup():
    """
    Create initial test files/dirs
    """
    os.system("mkdir %s" % ro_dir)

    for i in exist_files:
        os.system("touch %s" % i)

    # Set ro_img to readonly
    os.system("chmod 444 %s" % ro_img)
    os.system("chmod 555 %s" % ro_dir)

    virtinst.util.default_bridge = lambda ignore: None


def cleanup():
    """
    Cleanup temporary files used for testing
    """
    for i in clean_files:
        os.system("chmod 777 %s > /dev/null 2>&1" % i)
        os.system("rm -rf %s > /dev/null 2>&1" % i)

    virtinst.util.default_bridge = old_bridge


class CLITests(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        unittest.TestCase.__init__(self, *args, **kwargs)

    def setUp(self):
        global curtest
        curtest += 1
        # Only run this for first test
        if curtest == 1:
            setup()

    def tearDown(self):
        # Only run this on the last test
        if curtest == newidx:
            cleanup()


def maketest(cmd):
    def cmdtemplate(self, c):
        err = c.run()
        if err:
            self.fail(err)
    return lambda s: cmdtemplate(s, cmd)

_cmdlist = promptlist
_cmdlist += vinst.cmds
_cmdlist += vclon.cmds
_cmdlist += vimag.cmds
_cmdlist += vconv.cmds

for _cmd in _cmdlist:
    newidx += 1
    setattr(CLITests, "testCLI%d" % newidx, maketest(_cmd))

atexit.register(cleanup)
