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 <[email protected]>
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
<[email protected]>
---
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<[email protected]> [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
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest