0% found this document useful (0 votes)
2 views11 pages

DeveloperWorks Os Python KVM Scripting2 PDF

This document is the second part of a series on scripting KVM with Python, focusing on adding a GUI using wxPython. It covers the basics of wxPython, including widget hierarchy, event handling, and integrating with libvirt to manage virtual machines. The article provides practical examples and code snippets to help readers build a GUI application for managing KVM effectively.

Uploaded by

Francisco Galvez
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views11 pages

DeveloperWorks Os Python KVM Scripting2 PDF

This document is the second part of a series on scripting KVM with Python, focusing on adding a GUI using wxPython. It covers the basics of wxPython, including widget hierarchy, event handling, and integrating with libvirt to manage virtual machines. The article provides practical examples and code snippets to help readers build a GUI application for managing KVM effectively.

Uploaded by

Francisco Galvez
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Scripting KVM with Python, Part 2: Add a GUI to

manage KVM with libvirt and Python


Skill Level: Intermediate

Paul Ferrill
CTO
ATAC

17 Jan 2012

This two-part series explores how to use Python to create scripts for managing virtual
machines using KVM. In this installment, you learn how to add a GUI to expand on
the simple status and display tool.
Part 1 of this series looked at the basics of scripting Kernel-based Virtual Machine
(KVM) using libvirt and Python. This installment uses the concepts developed
there to build several utility applications and add a graphical user interface (GUI) into
the mix. There are two primary options for a GUI toolkit that has Python bindings and
is cross-platform. The first is Qt, which is now owned by Nokia; the second is
wxPython. Both have strong followings and many open source projects on their lists
of users.

For this article, I focus on wxPython more out of personal preference than anything
else. I start off with a short introduction to wxPython and the basics proper setup.
From there, I move on to a few short example programs, and then to integrating with
libvirt. This approach should introduce enough wxPython basics for you to build
a simple program, and then expand on that program to add features. Hopefully,
you'll be able to take these concepts and build on them to meet your specific needs.

wxPython basics
A good place to start is with a few basic definitions. The wxPython library is actually
a wrapper on top of the C++-based wxWidgets. In the context of creating a GUI, a
widget is essentially a building block. Five independent widgets reside at the
top-most level of the widget hierarchy:

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 1 of 11
developerWorks® [Link]/developerWorks

[Link],
[Link],
[Link],
[Link], and
[Link].

Most of the examples here are based on [Link], as it essentially implements a


single modal window.

In wxPython, Frame is a class that you instantiate as is or inherit from to add or


enhance the functionality. It's important to understand how widgets appear within a
frame so you know how to place them properly. Layout is determined either by
absolute positioning or by using sizers. A sizer is a handy tool that resizes widgets
when the user changes the size of the window by clicking and dragging a side or
corner.

The simplest form of a wxPython program must have a few lines of code to set
things up. A typical main routine might look something like Listing 1.

Listing 1. Device XML definition

if __name__ == "__main__":
app = [Link](False)
frame = MyFrame()
[Link]()
[Link]()

Every wxPython app is an instance of [Link]() and must instantiate it as shown in


Listing 1. When you pass False to [Link], it means "don't redirect stdout and
stderr to a window." The next line creates a frame by instantiating the MyFrame()
class. You then show the frame and pass control to [Link](). The
MyFrame() class typically contains an __init__ function to initialize the frame
with your widgets of choice. It is also where you would connect any widget events to
their appropriate handlers.

This is probably a good place to mention a handy debugging tool that comes with
wxPython. It's called the widget inspection tool (see Figure 1) and only requires two
lines of code to use. First, you have to import it with:

import [Link]

Then, to use it, you simply call the Show() function:

[Link]().Show()

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 2 of 11
[Link]/developerWorks developerWorks®

Clicking the Events icon on the menu toolbar dynamically shows you events as they
fire. It's a really neat way to see events as they happen if you're not sure which
events a particular widget supports. It also gives you a better appreciation of how
much is going on behind the scenes when your application is running.

Figure 1. The wxPython widget inspection tool

Add a GUI to a command-line tool


Part 1 of this series presented a simple tool to display the status of all running virtual
machines (VMs). It's simple to change that tool into a GUI tool with wxPython. The
[Link] widget provides just the functionality you need to present the
information in tabular form. To use a [Link] widget, you must add it to your
frame with the following syntax:

[Link]=[Link](frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER)

You can choose from several different styles, including the wx.LC_REPORT and
wx.SUNKEN_BORDER options previously used. The first option puts the
[Link] into Report mode, which is one of four available modes. The others
are Icon, Small Icon, and List. To add styles like wx.SUNKEN_BORDER, you simply
use the pipe character (|). Some styles are mutually exclusive, such as the different
border styles, so check the wxPython wiki if you have any doubts (see Resources).

After instantiating the [Link] widget, you can start adding things to it, like
column headers. The InsertColumn method has two mandatory parameters and
two optional ones. First is the column index, which is zero-based, followed by a
string to set the heading. The third is for formatting and should be something like
LIST_FORMAT_CENTER, _LEFT, or _RIGHT. Finally, you can set a fixed width by
passing in an integer or have the column automatically sized by using
wx.LIST_AUTOSIZE.

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 3 of 11
developerWorks® [Link]/developerWorks

Now that you have the [Link] widget configured, you can use the
InsertStringItem and SetStringItem methods to populate it with data. Each
new row in the [Link] widget must be added using the
InsertStringItem method. The two mandatory parameters specify where to
perform the insert, with a value of 0 indicating at the top of the list and the string to
insert at that location. InsertStringItem returns an integer indicating the row
number of the inserted string. You can make a call to GetItemCount() for the list
and use the return value for the index to append to the bottom, as Listing 2 shows.

Listing 2. GUI version of the command-line tool

import wx
import libvirt
conn=[Link]("qemu:///system")
class MyApp([Link]):
def OnInit(self):
frame = [Link](None, -1, "KVM Info")
id=[Link]()
[Link]=[Link](frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER)
[Link](True)
[Link](0,"ID")
[Link](1,"Name")
[Link](2,"State")
[Link](3,"Max Mem")
[Link](4,"# of vCPUs")
[Link](5,"CPU Time (ns)")
for i,id in enumerate([Link]()):
dom = [Link](id)
infos = [Link]()
pos = [Link](i,str(id))
[Link](pos,1,[Link]())
[Link](pos,2,str(infos[0]))
[Link](pos,3,str(infos[1]))
[Link](pos,4,str(infos[3]))
[Link](pos,5,str(infos[2]))
[Link](True)
[Link](frame)
return True
app = MyApp(0)
[Link]()

Figure 2 shows the results of these efforts.

Figure 2. The GUI KVM info tool

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 4 of 11
[Link]/developerWorks developerWorks®

You can enhance the appearance of this table. A noticeable improvement would be
to resize the columns. You can do so by either adding the width = parameter to
the InsertColumn call or use one line of code, like this:

[Link](column,wx.LIST_AUTOSIZE)

The other thing you could do is add a sizer so that the controls resize with the parent
window. You can do this with a wxBoxSizer in a few lines of code. First, you create
the sizer, and then you add the widgets to it that you want to resize along with the
main window. Here's what that code might look like:

[Link] = [Link]([Link])
[Link]([Link], proportion=1,flag=[Link] | [Link], border=5)
[Link]([Link], flag=[Link] | [Link], border=5)
[Link]([Link])

The last call to [Link]() instructs wxPython to set the

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 5 of 11
developerWorks® [Link]/developerWorks

initial size of the pane based on the sizer's minimum size from the embedded
widgets. This helps to give your initial screen a reasonable size based on the
content inside.

Control flow based on a user action


One of the nice things about the [Link] widget is that you can detect when a
user clicks a specific part of the widget and take some action based on that. This
functionality allows you to do things like sort a column alphabetically in forward or
reverse order based on the user clicking the column title. The technique to
accomplish this uses a callback mechanism. You must provide a function to handle
each action that you want to process by binding the widget and processing method
together. You do so with the Bind method.

Every widget has some number of events associated with it. There are also events
associated with things like the mouse. Mouse events have names like
EVT_LEFT_DOWN, EVT_LEFT_UP, and EVT_LEFT_DCLICK, along with the same
naming convention for the other buttons. You could handle all mouse events by
attaching to the EVT_MOUSE_EVENTS type. The trick is to catch the event in the
context of the application or window you're interested in.

When control passes to the event handler, it must perform the necessary steps to
handle the action, and then return control to wherever it was prior to that. This is the
event-drive programming model that every GUI must implement to handle user
actions in a timely fashion. Many modern GUI applications implement multithreading
to keep from giving the user the impression that the program isn't responding. I
briefly touch on that later in this article.

Timers represent another type of event that a program must potentially deal with.
For example, you might want to perform a periodic monitoring function at a
user-defined interval. You would need to provide a screen on which the user could
specify the interval, and then launch a timer that would in turn fire an event when it
expires. The timer expiration fires an event that you can use to activate a section of
code. You might need to set or restart the time, depending again on user preference.
You could easily use this technique to develop a VM monitoring tool.

Listing 3 provides a simple demo app with a button and static text lines. Using
[Link] is an easy way to output a string to the window. The idea is to
click the button once to start a timer and record the start time. Clicking the button
records the start time and changes the label to Stop. Clicking the button again fills in
the stop time text box and changes the button back to Start.

Listing 3. Simple app with a button and static text

import wx

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 6 of 11
[Link]/developerWorks developerWorks®

from time import gmtime, strftime


class MyForm([Link]):
def __init__(self):
[Link].__init__(self, None, wx.ID_ANY, "Buttons")
[Link] = [Link](self, wx.ID_ANY)
[Link] = [Link]([Link], id=wx.ID_ANY, label="Start")
[Link](wx.EVT_BUTTON, [Link])
def onButton(self, event):
if [Link]() == "Start":
[Link]("Stop")
strtime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
[Link](self, -1, 'Start Time = ' + strtime, (25, 75))
else:
[Link]("Start")
stptime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
[Link](self, -1, 'Stop Time = ' + stptime, (25, 100))
if __name__ == "__main__":
app = [Link](False)
frame = MyForm()
[Link]()
[Link]()

Enhanced monitoring GUI


Now, you can add functionality to the simple monitoring GUI introduced earlier.
There is one more piece of wxPython you need to understand before you have
everything you need to create your app. Adding a check box to the first column of a
[Link] widget would make it possible to take action on multiple lines based
on the status of the check box. You can do this by using what wxPython calls mixins.
In essence, a mixin is a helper class that adds some type of functionality to the
parent widget. To add the check box mixin, simply use the following code to
instantiate it:

[Link].__init__(self)

You can also take advantage of events to add the ability to select or clear all boxes
by clicking the column title. Doing so makes it simple to do things like start or stop all
VMs with just a few clicks. You need to write a few event handlers to respond to the
appropriate events in the same way you changed the label on the button previously.
Here's the line of code needed to set up a handler for the column click event:

[Link](wx.EVT_LIST_COL_CLICK, [Link], [Link])

wx.EVT_LIST_COL_CLICK fires when any column header is clicked. To determine


which column was clicked, you can use the [Link]() method. Here's a
simple handler function for the OnColClick event:

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 7 of 11
developerWorks® [Link]/developerWorks

def OnColClick(self, event):


print "column clicked %d\n" % [Link]()
[Link]()

The [Link]() call is important if you need to propagate the event to other
handlers. Although this need might not be apparent in this instance, it can be
problematic when multiple handlers need to process the same event. There's a good
discussion of event propagation on the wxPython wiki site, which has much more
detail than I have room for here.

Finally, add code to the two button handlers to start or stop all checked VMs. It's
possible to iterate over the lines in your [Link] and pull the VM ID out with
just a few lines of code, as Listing 4 shows.

Listing 4. Starting and stopping checked VMs

#!/usr/bin/env python
import wx
import [Link] as listmix
import libvirt
conn=[Link]("qemu:///system")
class CheckListCtrl([Link], [Link],
[Link]):
def __init__(self, *args, **kwargs):
[Link].__init__(self, *args, **kwargs)
[Link].__init__(self)
[Link].__init__(self)
[Link](2)
class MainWindow([Link]):
def __init__(self, *args, **kwargs):
[Link].__init__(self, *args, **kwargs)
[Link] = [Link](self)
[Link] = CheckListCtrl([Link], style=wx.LC_REPORT)
[Link](0, "Check", width = 175)
[Link](wx.EVT_LIST_COL_CLICK, [Link], [Link])
[Link](1,"Max Mem", width = 100)
[Link](2,"# of vCPUs", width = 100)
for i,id in enumerate([Link]()):
dom = [Link](id)
infos = [Link]()
pos = [Link](1,[Link]())
[Link](pos,1,str(infos[1]))
[Link](pos,2,str(infos[3]))
[Link] = [Link]([Link], label="Start")
[Link](wx.EVT_BUTTON, [Link], [Link])
[Link] = [Link]([Link])
[Link]([Link], proportion=1, flag=[Link] | [Link], border=5)
[Link]([Link], flag=[Link] | [Link], border=5)
[Link]([Link])
[Link]()

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 8 of 11
[Link]/developerWorks developerWorks®

def onStrButton(self, event):


if [Link]() == "Start":
num = [Link]()
for i in range(num):
if [Link](i):
dom = [Link]([Link](i, 0).Text)
[Link]()
print "%d started" % [Link]()
def OnColClick(self, event):
item = [Link](0)
if item is not None:
if [Link]() == "Check":
[Link]("Uncheck")
[Link](0, item)
num = [Link]()
for i in range(num):
[Link](i,True)
else:
[Link]("Check")
[Link](0, item)
num = [Link]()
for i in range(num):
[Link](i,False)
[Link]()
app = [Link](False)
win = MainWindow(None)
[Link]()

There are two things to point out here with respect to the state of VMs in KVM:
Running VMs show up when you use the listDomainsID() method from
libvirt. To see non-running machines you must use listDefinedDomains().
You just have to keep those two separate so that you know which VMs you can start
and which you can stop.

Wrapping up
This article focused mainly on the steps needed to build a GUI wrapper using
wxPython that in turn manages KVM with libvirt. The wxPython library is
extensive and provides a wide range of widgets to enable you to build
professional-looking GUI-based applications. This article just scratched the surface
of its capabilities, but you'll hopefully be motivated to investigate further. Be sure to
check more Resources to help get your application running.

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 9 of 11
developerWorks® [Link]/developerWorks

Resources
Learn
• libvirt website: Check out the entire site for more information.
• Reference Manual for libvirt: Access the complete libvirt API reference
manual.
• [Link]: Find more of the Python resources you need.
• [Link]: Get more about wxPython.
• wxPython wiki: Expand your knowledge through the many tutorials found here.
• developerWorks Open source zone: Find extensive how-to information, tools,
and project updates to help you develop with open source technologies and use
them with IBM products. Explore more Python-related articles.
• Events of interest: Check out upcoming conferences, trade shows, and
webcasts that are of interest to IBM open source developers.
• developerWorks podcasts: Tune into interesting interviews and discussions for
software developers
• developerWorks demos: Watch our no-cost demos and learn about IBM and
open source technologies and product functions.
• developerWorks on Twitter: Follow us for the latest news.
Get products and technologies
• Evaluate IBM software products: From trial downloads to cloud-hosted
products, you can innovate your next open source development project using
software especially for developers.
Discuss
• developerWorks community: Connect with other developerWorks users while
exploring the developer-driven blogs, forums, groups, and wikis. Help build the
Real world open source group in the developerWorks community.

About the author


Paul Ferrill
Paul Ferrill has been writing in the computer trade press for more than
20 years. He got his start writing networking reviews for PC Magazine
on products like LANtastic and early versions of Novell Netware. Paul
holds both BSEE and MSEE degrees and has written software for more

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 10 of 11
[Link]/developerWorks developerWorks®

computer platforms and architectures than he can remember.

Add a GUI to manage KVM with libvirt and Python Trademarks


© Copyright IBM Corporation 2012 Page 11 of 11

You might also like