Hello Chris, Please find the improved patch for adding to libvirt - support for running tests with multiple VMs without interfering with other VM resources. Please let me know if there are any issues- my security patches are based on this -
>From 5cff67ca3f004381ddcbe862ce20ae6894bb742e Mon Sep 17 00:00:00 2001 From: Onkar N Mahajan <onkar.n.maha...@linux.vnet.ibm.com> Date: Tue, 26 Jun 2012 22:31:11 +0530 Subject: [PATCH] Libvirt-Autotest: Support for running multiple guest (Improved) Signed-off-by: Onkar N Mahajan <onkar.n.maha...@linux.vnet.ibm.com> --- client/tests/libvirt/tests-shared.cfg.sample | 12 +- client/virt/libvirt_vm.py | 159 +++++++++++++++++++++++++- client/virt/virt_utils.py | 38 +++++-- client/virt/virt_vm.py | 54 +++++++++- 4 files changed, 246 insertions(+), 17 deletions(-) diff --git a/client/tests/libvirt/tests-shared.cfg.sample b/client/tests/libvirt/tests-shared.cfg.sample index 1fe0894..64cf516 100644 --- a/client/tests/libvirt/tests-shared.cfg.sample +++ b/client/tests/libvirt/tests-shared.cfg.sample @@ -28,13 +28,13 @@ include virtio-win.cfg # * The parameters cdrom_unattended, floppy, kernel and initrd are generated # by LIBVIRT autotest, so remember to put them under a writable location # (for example, the cdrom share can be read only) -image_name(_.*)? ?<= /tmp/libvirt_autotest_root/images/ -cdrom(_.*)? ?<= /tmp/libvirt_autotest_root/ -floppy ?<= /tmp/libvirt_autotest_root/ -image_dir = /tmp/libvirt_autotest_root/ +image_name(_.*)? ?<= /tmp/libvirt_autotest_root/$main_vm/ +cdrom(_.*)? ?<= /tmp/libvirt_autotest_root/$main_vm/ +floppy ?<= /tmp/libvirt_autotest_root/$main_vm/ +image_dir = /tmp/libvirt_autotest_root/$main_vm/ Linux..unattended_install: - kernel ?<= /tmp/libvirt_autotest_root/ - initrd ?<= /tmp/libvirt_autotest_root/ + kernel ?<= /tmp/libvirt_autotest_root/$main_vm/ + initrd ?<= /tmp/libvirt_autotest_root/$main_vm/ # Uncomment the following lines to enable abort-on-error mode: #abort_on_error = yes diff --git a/client/virt/libvirt_vm.py b/client/virt/libvirt_vm.py index d97ac0c..df9fd9b 100644 --- a/client/virt/libvirt_vm.py +++ b/client/virt/libvirt_vm.py @@ -461,6 +461,156 @@ def virsh_detach_device(name, xml_file, extra="", uri=""): logging.error("Detaching device from VM %s failed." % name) return False +def virsh_vncdisplay(name, uri=""): + """ + Returns the VNC display information for the domain + Returns : [ <IP>, <port> ] + """ + + cmd = "vncdisplay %s" % name + try: + cmdresult = virsh_cmd(cmd, uri).stdout.strip() + # Match IP|'':<port> + m = re.search('^((?:25[0-]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){3}' + '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))?\:(\d+)', + cmdresult) + ip = m.group(1) + if ip == None: + ip = "0.0.0.0" + vncinfo = [ ip, m.group(2)] + return vncinfo + except error.CmdError, details: + out = str(details) + if out.strip() == "": + domxml = virsh_dumpxml(self.name, self.connect_uri) + dom = minidom.parseString(domxml) + if dom.getElementsByTagName('graphics'): + display = node.childNodes[0] + ap = display.attributes["autoport"] + if ap.value =="yes": + # VNC display configured with autoport='yes' + return ["0.0.0.0", -1] + else: + raise virt_vm.UnsupportedDisplayError(self.name, "vnc") + raise virt_vm.LibvirtVirshCmdError(name, cmd, uri, details) + + +def virsh_list(option="--all", uri = ""): + """ + - list domains - + @param option: Can take various values see 'virsh help list' + for more details. + @param uri : connect uri + + Returns a list : + Returns dictionary with each entry + as : <'dom-name'>: [ <dom-id> , <dom-status> ] + + """ + cmd = "list %s" % option + try: + cmd_res = virsh_cmd(cmd, uri).stdout.strip() + d = {} + for line in cmd_res.split("\n"): + l = [] + m = re.search('^\s([0-9]*\-?)\s+([0-9a-zA-Z\.-_]+)\s+(.*)', line) + if m: + l.append(m.group(1)) + l.append(m.group(3)) + d[m.group(2)] = l + return d + except error.CmdError, details: + raise virt_vm.LibvirtVirshCmdError(virsh_cmd=cmd, connuri=uri, + details=details) + + +def list_all_domains(uri, option="--all"): + """ + Returns list of domains for the uri + """ + try: + val = virsh_list(option, uri) + return val + except virt_vm.LibvirtVirshCmdError: + raise virt_vm.LibvirtFailedGettingInfoError(connuri=uri) + + +def get_graphics_info(name, uri, displaytype): + """ + Returns Graphics display Information configured for a given + domain. + ** Currently supports only VNC display ** + """ + if displaytype == "vnc": + try: + """ + Add virsh functions to get graphics information + here. + """ + vncinfo = virsh_vncdisplay(name, uri) + if vncinfo: + ip = vncinfo[0] + port = vncinfo[1] + return [ip, port] + except virt_vm.UnsupportedDisplayError: + raise NotImplementedError("Only VMs with VNC" + "displays are supported") + except virt_vm.LibvirtVirshCmdError: + raise virt_vm.LibvirtFailedGettingInfoError(name, uri) + + +def get_free_port(name, uri, type, startport, endport): + """ + Get first free port available for a given application. + ** Currently supports only VNC ** + Returns : port (>0) : success + -1 : Failure + """ + if type == "vnc": + val={} + assigned_ports=[] + maxport = 0 + port = 0 + try: + val = list_all_domains(uri) + except virt_vm.LibvirtFailedGettingInfoError: + logging.error("Failed to get domains information for " + "%s" % (self.connect_uri)) + return -1 + for key in val.keys(): + try: + vncinfo = get_graphics_info(key, uri, type) + p = int(vncinfo[1]) + if p >= startport: + p = p-int(startport) + if p >= maxport: + maxport = p + assigned_ports.append(p) + except (virt_vm.LibvirtFailedGettingInfoError, + NotImplementedError): + logging.error("Failed to get information for VM %s " + "at URI %s" % (name, uri)) + return -1 + cmd = "vncserver -list | grep ^:[0-9]* | cut -c1-4" + cmd_result = str(utils.run(cmd)) + for line in cmd_result.split("\n"): + m = re.search('^:(\d)\s*$', line) + if m: + vncsp = m.group(1) + vncserverport = int(vncsp) + if vncserverport >= maxport: + maxport = vncserverport + assigned_ports.append(vncserverport) + for p in range(0,endport-startport): + if not p in assigned_ports: + port = p + if virt_utils.find_free_port(port,port+1): + break + else: + continue + port += startport + return port + class VM(virt_vm.BaseVM): """ @@ -485,6 +635,7 @@ class VM(virt_vm.BaseVM): self.process = None self.serial_console = None self.redirs = {} + self.displaytype= None self.vnc_port = 5900 self.vnclisten = "0.0.0.0" self.pci_assignable = None @@ -934,6 +1085,7 @@ class VM(virt_vm.BaseVM): virt_install_cmd += add_location(help, location) if params.get("display") == "vnc": + self.displaytype = "vnc" if params.get("vnc_port"): vm.vnc_port = int(params.get("vnc_port")) virt_install_cmd += add_vnc(help, vm.vnc_port) @@ -941,8 +1093,10 @@ class VM(virt_vm.BaseVM): vm.vnclisten = params.get("vnclisten") virt_install_cmd += add_vnclisten(help, vm.vnclisten) elif params.get("display") == "sdl": + self.displaytype = "sdl" virt_install_cmd += add_sdl(help) elif params.get("display") == "nographic": + self.displaytype = "nographic" virt_install_cmd += add_nographic(help) video_device = params.get("video_device") @@ -1188,7 +1342,10 @@ class VM(virt_vm.BaseVM): # Find available VNC port, if needed if params.get("display") == "vnc": - self.vnc_port = virt_utils.find_free_port(5900, 6100) + self.displaytype = "vnc" + port = get_free_port(self.name, self.connect_uri, self.displaytype, + 5900, 6100) + self.vnc_port = port # Find available spice port, if needed if params.get("spice"): diff --git a/client/virt/virt_utils.py b/client/virt/virt_utils.py index 1c4d90d..5079cbb 100644 --- a/client/virt/virt_utils.py +++ b/client/virt/virt_utils.py @@ -3889,14 +3889,36 @@ def virt_test_assistant(test_name, test_dir, base_dir, default_userspace_paths, logging.info("%d - Verifying directories (check if the directory structure " "expected by the default test config is there)", step) sub_dir_list = ["images", "isos", "steps_data"] - for sub_dir in sub_dir_list: - sub_dir_path = os.path.join(base_dir, sub_dir) - if not os.path.isdir(sub_dir_path): - logging.debug("Creating %s", sub_dir_path) - os.makedirs(sub_dir_path) - else: - logging.debug("Dir %s exists, not creating" % - sub_dir_path) + + if test_name == 'libvirt': + base_fl = "%s/%s" % (test_dir, "base.cfg") + if not os.path.isfile(base_fl): + base_fl = "%s/%s" % (common_dir, "base.cfg.sample") + cmd = "cat %s | grep \"^main_vm\s=\s.*\" | cut -d \"=\" -f 2|sed 's/[^\w]//'" % base_fl + try: + vmdir = utils.run("%s" % cmd).stdout + except error.CmdError, detail: + logging.error("Failed to fetch 'main_vm' parameter from base.cfg :%s", detail) + sys.exit(1) + base_dir = "%s/%s" % (base_dir, vmdir.strip()) + for sub_dir in sub_dir_list: + sub_dir_path = os.path.join(base_dir, sub_dir) + if not os.path.isdir(sub_dir_path): + logging.debug("Creating %s", sub_dir_path) + os.makedirs(sub_dir_path) + else: + logging.debug("Dir %s exists, not creating" % + sub_dir_path) + else: + for sub_dir in sub_dir_list: + sub_dir_path = os.path.join(base_dir, sub_dir) + if not os.path.isdir(sub_dir_path): + logging.debug("Creating %s", sub_dir_path) + os.makedirs(sub_dir_path) + else: + logging.debug("Dir %s exists, not creating" % + sub_dir_path) + logging.info("") step += 1 logging.info("%d - Creating config files from samples (copy the default " diff --git a/client/virt/virt_vm.py b/client/virt/virt_vm.py index 534f8e3..201992d 100644 --- a/client/virt/virt_vm.py +++ b/client/virt/virt_vm.py @@ -1,8 +1,8 @@ -import logging, time, glob, re +import os, logging, time, glob, re, shutil, string from autotest.client.shared import error +from autotest.client import utils import virt_utils, virt_remote - class VMError(Exception): pass @@ -312,6 +312,56 @@ class VMUSBControllerPortFullError(VMUSBControllerError): def __str__(self): return ("No available USB Controller port left for VM %s." % self.name) +class VMDisplayError(VMError): + pass + +class UnsupportedDisplayError(VMDisplayError): + def __init__(self, name, display): + VMDisplayError.__init__(self, name, displaytype) + self.name = name + self.display = displaytype + + def __str__(self): + return ("%s display not supported for this VM '%s' on this host." % + (string.upper(self.display), self.name)) + +""" +Libvirt error handling starts below. +""" + +class LibvirtError(Exception): + pass + +class LibvirtFailedGettingInfoError(LibvirtError): + def __init__(self, name, connuri): + LibvirtError.__init__(self, name, connuri) + self.name = name + self.uri = connuri + + def __str__(self, name=None, uri =""): + if self.name: + return ("Libvirt failed to get information for VM '%s' at" + " connection URI '%s'" % (self.name, self.uri)) + else: + return ("Libvirt failed to get information for " + "connection '%s' : %s" % (self.uri)) + +class LibvirtVirshCmdError(LibvirtError): + def __init__(self, name, virsh_cmd, connuri, details=""): + LibvirtError.__init__(self, name, virsh_cmd, connuri, details) + self.virshcmd = virsh_cmd + self.name = name + self.uri = connuri + self.details = details + + def __str__(self): + if self.name : + return ("Error executing command '%s' for VM '%s' at" + " connection URI '%s' : " + "%s" % (self.virshcmd, self.name, self.uri, self.details)) + else: + return ("Error in executing commmand '%s' for connect URI" + " %s : %s" % (self.virshcmd, self.uri, self.details)) class BaseVM(object): """ -- 1.7.4.4 On Mon, 2012-06-11 at 11:10 -0400, Chris Evich wrote: > On 06/11/2012 09:30 AM, Pradeep Kumar Surisetty wrote: > > * Chris Evich<cev...@redhat.com> [2012-06-08 10:45:29]: > > > >> On 06/08/2012 02:06 AM, Onkar N Mahajan wrote: > >>> On Thu, 2012-06-07 at 10:52 -0400, Chris Evich wrote: > >>>> On 06/07/2012 04:46 AM, Onkar N Mahajan wrote: > >>> See, I have tried making nxt_free_vnc_port() available for general usage > >>> (vnc/spice/etc) as you mentioned , but this requires virsh to have > >>> vncdisplay counterparts to spice/etc - which AFAIK are currently not > >>> available. > >> > >> Annoyingly, it must be extracted from the xml :( However, you're right > >> in that virsh only has a vncdisplay command. Along those lines, how > >> about adding a lower-level and more generic virsh_vncdisplay() function > >> that returns the port and ip address? > >> > >> I'd like the the vm class methods to support a wider usage, even if all > >> the capabilities aren't there immediately. When possible, methods > >> should not be tied closely to underlying implementation details. > >> > >> For example with graphics, I'd prefer a vm.graphics_info() (or similar > >> name) method provides details independent of the underlying graphics > >> device type. But it's perfectly acceptable if support for other types > >> gets added at a later date, and it just throws exceptions for the stubs. > >> > >> With this deign, we can easily extend the tests (later) by adding > >> support for additional graphics. It also allows for extending the > >> underlying support w/o disturbing the callers. Make sense? > > Code example of the above: > > virsh_vncdisplay(name, uri=""): > ... > if cmdresult.exit_status: > ... # other checks > raise virt_vm.UnsupportedDisplayError("VM %s doesn't use vnc" > "graphics" % name) > > class VM(virt_vm.BaseVM): > ... > def graphics_info(self): > d={} > # only VNC graphics supported for now > try: > vncinfo=virsh_vncdisplay(...) > d["ip"] = vncinfo[0] > d["port"] = vncinfo[1] > except virt_vm.UnsupportedDisplayError: > raise virt_vm.NotImplementedError("Only VMs with VNC > displays are supported") > return d > > In this way, we can easily add spice or whatever support to > vm.graphics_info (or whatever name) later w/o affecting callers. If a > test needs to specifically only deal with VM's using VNC, they can just > call virst_vncdisplay directly. It also supports error testing in a > generic way by calling vm.graphics_info() and checking making sure it > does NOT raise UnsupportedDisplayError or NotImplementedError (presuming > bad guest XML raises a different exception). > -- Onkar N Mahajan System Software Engineer, IBM Linux Technology Center, Bangalore,India _______________________________________________ Autotest mailing list Autotest@test.kernel.org http://test.kernel.org/cgi-bin/mailman/listinfo/autotest