From: Lucas Meneghel Rodrigues <lmr@redhat.com>
To: autotest@test.kernel.org
Cc: kvm@vger.kernel.org
Subject: [PATCH 06/11] virt: Introducing libvirt monitor
Date: Tue, 11 Oct 2011 18:07:12 -0300 [thread overview]
Message-ID: <1318367237-26081-7-git-send-email-lmr@redhat.com> (raw)
In-Reply-To: <1318367237-26081-1-git-send-email-lmr@redhat.com>
This is an initial implementation for a libvirt monitor.
With it, we plan on making the libvirt test use all the
monitor features, making most of the tests available for
kvm available for libvirt.
As of implementation details, it uses aexpect to get a
virsh shell, and then the monitor methods are implemented
by executing commands on that virsh shell.
As of now, the libvirt vm class is still not using the
monitor code, but we plan on making the move soon enough.
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
---
client/virt/libvirt_monitor.py | 322 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 322 insertions(+), 0 deletions(-)
create mode 100644 client/virt/libvirt_monitor.py
diff --git a/client/virt/libvirt_monitor.py b/client/virt/libvirt_monitor.py
new file mode 100644
index 0000000..05b838c
--- /dev/null
+++ b/client/virt/libvirt_monitor.py
@@ -0,0 +1,322 @@
+import re, tempfile, xml.dom.minidom, logging
+import virt_utils, aexpect
+from autotest_lib.client.bin import utils
+
+
+class VirshMonitor:
+ """
+ Wraps "Virsh monitor" commands.
+ """
+
+ def __init__(self, virsh_exec='virsh', name, vmname, password=None,
+ prompt=None, hostname='localhost', driver=None, username=None,
+ linesep="\\n"):
+ """
+ Connect to the hypervisor and get virsh prompt.
+
+ @param virsh_exec: Virsh executable
+ @param name: Monitor identifier (a string)
+ @param vmname: VM name
+ @param password: Hypervisor user password
+ @param prompt: Virsh prompt
+ @param hostname: Hypervisor IP
+ @param driver: Hypervisor driver type
+ @param username: Hypervisor username
+ @param linesep: The line separator to use when sending lines
+ (e.g. '\\n' or '\\r\\n')
+ """
+ self.virsh_exec = virsh_exec
+ self.name = name
+ self.vmname = vmname
+ self.password = password
+ self.prompt = prompt
+ self.hostname = hostname
+ self.driver = driver
+ self.username = username
+ self.session = self.login()
+ self.virsh_cmd = {"help":"help", "quit":"destroy " + self.vmname,
+ "stop":"suspend", "cont":"resume"}
+ self.drive_map = {}
+ self.network_info = []
+ self.disk_info = []
+ self._parse_domxml()
+
+
+ def __del__(self):
+ self.session.sendline("quit")
+
+
+ def __getstate__(self):
+ pass
+
+
+ def __setstate__(self, state):
+ pass
+
+
+ def __getinitargs__(self):
+ # Save some information when pickling -- will be passed to the
+ # constructor upon unpickling
+ return (self.name, self.vmname, self.password, self.prompt,
+ self.hostname, self.driver, self.username)
+
+
+ def login(self, timeout=10):
+ """
+ Log into the hypervisor using required URIs .
+
+ @timeout: Time in seconds that we will wait before giving up on logging
+ into the host.
+ @return: A ShellSession object.
+ """
+ if self.driver is None:
+ uri = utils.system_output('%s uri' % self.virsh_exec)
+ else:
+ uri = "%s+ssh://%s@%s/system" % (self.driver, self.username,
+ self.host)
+
+ command = "%s --connect %s" % (self.virsh_exec, uri)
+
+ session = aexpect.ShellSession(command, linesep=self.linesep,
+ prompt=self.prompt)
+
+ if self.username is not None:
+ try:
+ virt_utils._remote_login(session, self.username, self.password,
+ self.prompt, self.timeout)
+ except aexpect.ShellError:
+ session.close()
+ session = None
+
+ return session
+
+
+ def _parse_domxml(self):
+ self.session.cmd_output("\n")
+ domxml = self.session.cmd_output("dumpxml %s \n" % self.vmname)
+ self._parse_dev(domxml)
+
+
+ def _parse_dev(self, domxml):
+ dom = xml.dom.minidom.parseString(domxml)
+ self.network_info = []
+ for elems in dom.getElementsByTagName('interface'):
+ self.network_info.append(elems.toxml())
+
+ self.disk_info = []
+ for elems in dom.getElementsByTagName('disk'):
+ self.disk_info.append(elems.toxml())
+
+
+ def verify_responsive(self):
+ """
+ Make sure the monitor is responsive by sending a command.
+ """
+ self.cmd("help")
+
+
+ def cmd(self, command):
+ """
+ Send command to the monitor.
+
+ @param command: Command to send to the monitor
+ @return: Output received from the monitor
+ """
+ def dev_add(command):
+ """
+ Create the xml file for the device to be attached
+ """
+ parm_dic = dict(re.findall("(id|driver|mac|drive)=([^,\s]+)",
+ command))
+ self.session.cmd_output("\n")
+ if parm_dic.has_key("drive"):
+ self.drive_map[parm_dic["drive"]]["driver"] = parm_dic\
+ ["driver"]
+ self.drive_map[parm_dic["drive"]]["device_id"] = parm_dic["id"]
+ self.drive_map[parm_dic["drive"]]["dev"] = "vd" + str(len(
+ self.drive_map))
+ doc = xml.dom.minidom.Document()
+ topelem = doc.createElement("disk")
+ topelem.setAttribute('device', 'disk')
+ topelem.setAttribute('type', 'file')
+ doc.appendChild(topelem)
+ chld_elem = doc.createElement("driver")
+ chld_elem.setAttribute('name', "qemu")
+ topelem.appendChild(chld_elem)
+ chld_elem = doc.createElement("source")
+ chld_elem.setAttribute('file', self.drive_map[parm_dic["drive"]
+ ]["file"])
+ topelem.appendChild(chld_elem)
+ chld_elem = doc.createElement("target")
+ if "virtio-blk-pci" in command:
+ self.drive_map[parm_dic["drive"]]["driver"] = "virtio"
+ chld_elem.setAttribute('bus', self.drive_map[parm_dic["drive"]
+ ]["driver"])
+ chld_elem.setAttribute('dev', self.drive_map[parm_dic["drive"]
+ ]["dev"])
+ topelem.appendChild(chld_elem)
+ else:
+ netpool_lst = self.session.cmd_output("net-list"
+ ).split('\n')
+ netpool = []
+ for r in range(2, len(netpool_lst) - 2):
+ netpool.append(netpool_lst[r].split()[0])
+
+ doc = xml.dom.minidom.Document()
+ topelem = doc.createElement("interface")
+ doc.appendChild(topelem)
+
+ chld_elem = doc.createElement("source")
+ chld_elem.setAttribute('network', netpool[-1])
+ topelem.appendChild(chld_elem)
+ if parm_dic.has_key("mac"):
+ mac = parm_dic.get("mac")
+ chld_elem = doc.createElement("mac")
+ chld_elem.setAttribute('address', mac)
+ topelem.appendChild(chld_elem)
+ if not parm_dic.has_key("driver"):
+ model = re.findall("device_add ([^,\s]+)", command)[0]
+ else:
+ model = parm_dic.get("driver")
+ if model == "virtio-net-pci":
+ model = "virtio"
+ chld_elem = doc.createElement("model")
+ chld_elem.setAttribute('type', model)
+ topelem.appendChild(chld_elem)
+ if parm_dic.has_key("id"):
+ id = parm_dic.get("id")
+ chld_elem = doc.createElement("target")
+ chld_elem.setAttribute('dev', id)
+ topelem.appendChild(chld_elem)
+
+ tmp_xml = doc.toxml()
+ devfl = tempfile.mktemp(prefix='dev')
+ devfd = open(devfl, 'w')
+ devfd.write(tmp_xml)
+ devfd.close()
+ return devfl
+
+ def net_del(command):
+ """
+ Create the xml file for the device to be detached
+ """
+ xml_str = ""
+ id = re.findall("device_del ([^,\s]+)", command)[0]
+ for i in range(len(self.network_info)):
+ if id in str(self.network_info[i]):
+ xml_str = str(self.network_info[i])
+ for i in self.drive_map:
+ if id in str(self.drive_map[i]):
+ map_key = self.drive_map[i]["dev"]
+ for i in range(len(self.disk_info)):
+ if map_key in str(self.disk_info[i]):
+ xml_str = str(self.disk_info[i])
+ devfl = tempfile.mktemp(prefix='dev')
+ devfd = open(devfl, 'w')
+ devfd.write(xml_str)
+ devfd.close()
+ return devfl
+
+ if "device_add ?" in command:
+ return "virtio-net-pci virtio-blk-pci e1000 rtl8139"
+
+ if "?" in command:
+ output = "\nhelp|? [cmd]\ndevice_add\ndevice_del\ndrive_add"\
+ "\n__com.redhat_drive_add"
+ return output
+
+ if "redhat_drive" in command:
+ drive_dic = dict(re.findall("(file|id)=([^,\s]+)", command))
+ self.drive_map[drive_dic["id"]] = drive_dic
+ return
+
+ if "netdev_add" in command:
+ id = re.findall("id=(.*?),", command)[0]
+ net_str = " peer=%s" % id
+ self.network_info[id] = net_str
+ return
+
+ if "device_add" in command:
+ devfile = dev_add(command)
+ xml_handle = open(devfile)
+ xml_pars = xml.dom.minidom.parse(xml_handle)
+ self.session.cmd_output("attach-device %s %s" %
+ (self.vmname, devfile))
+ domxml = self.session.cmd_output("dumpxml %s \n" % self.vmname)
+ self._parse_dev(domxml)
+ return
+
+ if "device_del" in command:
+ devfile = net_del(command)
+ xml_handle = open(devfile)
+ xml_pars = xml.dom.minidom.parse(xml_handle)
+ self.session.cmd_output("detach-device %s %s" %
+ (self.vmname, devfile))
+ domxml = self.session.cmd_output("dumpxml %s \n" % self.vmname)
+ self._parse_dev(domxml)
+ return
+
+ if "balloon" in command:
+ new_mem = re.findall("balloon\s+(\d+)", command)[0]
+ new_mem = str(int(new_mem) * 1024)
+ output = self.session.cmd_output("setmem %s %s" %
+ (self.vmname, new_mem))
+ return
+
+ if "system_reset" in command:
+ self.session.cmd_output("destroy %s" % self.vmname)
+ self.session.cmd_output("start %s" % self.vmname)
+ return
+
+ data = self.session.cmd_output(" %s \n" % self.virsh_cmd.get(
+ command, command))
+ return data
+
+
+ def is_responsive(self):
+ """
+ Return True if the monitor is responsive.
+ """
+ return True
+
+
+ def quit(self):
+ """
+ Send "quit" without waiting for output.
+ """
+ self.cmd("quit")
+
+
+ def screendump(self, filename, debug=True):
+ """
+ Request a screendump.
+
+ @param filename: Location for the screendump
+ @return: The command's output
+ """
+ if debug:
+ logging.debug("Requesting screendump %s" % filename)
+ self.cmd("screenshot %s" % filename)
+
+
+ def info(self, what):
+ """
+ Request info about something and return the output.
+ """
+ if "network" in what:
+ return self.network_info
+
+ if "pci" in what:
+ domxml = self.session.cmd_output("dumpxml %s \n" %
+ self.vmname)
+ self._parse_dev(domxml)
+ return str(self.network_info) + str(self.drive_map)
+
+ if "balloon" in what:
+ self.session.cmd_output("\n")
+ netpool_lst = self.session.cmd_output("dominfo %s" %
+ self.vmname)
+ return str(int(re.findall("Used memory:\s+(\d+)", netpool_lst)
+ [0]) / 1024)
+
+ return self.cmd("info %s" % what)
--
1.7.6.4
next prev parent reply other threads:[~2011-10-11 21:07 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-10-11 21:07 [PATCH 00/11] [RFC] Libvirt test v2 Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 02/11] virt: Introducing virt_test.virt_test class Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 03/11] Moving unattended_install test from kvm test to common virt location Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 04/11] Moving get_started code to client.virt.virt_utils Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 05/11] virt: Introducing libvirt VM class Lucas Meneghel Rodrigues
2011-10-12 6:51 ` [Autotest] " Amos Kong
2011-10-12 8:14 ` Daniel P. Berrange
2011-10-13 17:26 ` Lucas Meneghel Rodrigues
2011-10-11 21:07 ` Lucas Meneghel Rodrigues [this message]
2011-10-12 7:48 ` [Autotest] [PATCH 06/11] virt: Introducing libvirt monitor Amos Kong
2011-10-13 17:12 ` Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 07/11] virt.virt_env_process: Add libvirt vm handling Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 08/11] client.tests: Introducing libvirt test Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 09/11] Virt: builtin HTTP server for unattended installs Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 10/11] Virt: support XEN via libvirt and auto url installer Lucas Meneghel Rodrigues
2011-10-11 21:07 ` [PATCH 11/11] Virt: add support for XEN via libvirt installs and auto url Lucas Meneghel Rodrigues
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1318367237-26081-7-git-send-email-lmr@redhat.com \
--to=lmr@redhat.com \
--cc=autotest@test.kernel.org \
--cc=kvm@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).