libvirt kvm_添加GUI以使用libvirt和Python管理KVM

本文介绍了如何使用Python和wxPython库为基于内核的虚拟机(KVM)工具添加图形用户界面(GUI)。从wxPython的基础知识开始,通过简单的示例展示如何将GUI集成到命令行工具中,实现对KVM状态的可视化管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

libvirt kvm

本系列的第1部分介绍了使用libvirt和Python编写基于内核的虚拟机(KVM)脚本的基础知识。 本部分使用在那里开发的概念来构建几个实用程序,并在组合中添加图形用户界面(GUI)。 具有Python绑定并且是跨平台的GUI工具箱有两个主要选项。 第一个是Qt,现在归诺基亚所有。 第二个是wxPython。 两者都有很强的追随者,并且在他们的用户列表中有许多开源项目。

对于本文,我主要出于个人喜好而将wxPython放在首位。 我首先简要介绍了wxPython和基本的正确设置。 从那里开始,我进入一些简短的示例程序,然后与libvirt集成。 这种方法应该为您引入足够的wxPython基础知识,以构建一个简单的程序,然后对该程序进行扩展以添加功能。 希望您能够采用这些概念并在其基础上满足您的特定需求。

wxPython基础

一个好的起点是一些基本定义。 wxPython库实际上是基于C++的wxWidgets的包装。 在创建GUI的上下文中, 小部件本质上是一个构建块。 五个独立的小部件位于小部件层次结构的最顶层:

wx.Frame
wx.Dialog
wx.PopupWindow
wx.MDIParentFrame ,和
wx.MDIChildFrame

这里的大多数示例都基于wx.Frame ,因为它实际上实现了一个模态窗口。

在wxPython中, Frame是一个类,您可以直接对其进行实例化或从其继承以添加或增强功能。 了解小部件在框架中的显示方式非常重要,这样您才能知道如何正确放置它们。 布局可以通过绝对定位或使用大小调整器来确定。 分级机是一个方便的工具,当用户通过点击并拖动一个边或角改变窗口的大小调整大小部件。

wxPython程序的最简单形式必须包含几行代码来进行设置。 一个典型的主例程可能类似于清单1

清单1.设备XML定义
if __name__ == "__main__":
    app = wx.App(False)
	frame = MyFrame()
	frame.Show()
	app.MainLoop()

每个wxPython应用程序都是wx.App()的实例,并且必须实例化它,如清单1所示。当将False传递给wx.App ,这意味着“不要将stdout和stderr重定向到窗口”。 下一行通过实例化MyFrame()类创建一个框架。 然后,显示框架并将控制权传递给app.MainLoop()MyFrame()类通常包含__init__函数,以使用您选择的小部件来初始化框架。 在这里,您也可以将任何小部件事件连接到其相应的处理程序。

这可能是提及wxPython随附的便捷调试工具的好地方。 它被称为小部件检查工具 (参见图1 ),只需要使用两行代码即可。 首先,您必须使用以下命令导入它:

import wx.lib.inspection

然后,要使用它,只需调用Show()函数:

wx.lib.inspectin.InspectionTool().Show()

单击菜单工具栏上的“ 事件”图标可动态显示事件。 如果不确定特定窗口小部件支持哪些事件,这是一种观察事件发生的真正好方法。 它还使您可以更好地了解应用程序运行时幕后发生的事情。

图1. wxPython小部件检查工具
屏幕快照显示了wxPython窗口小部件检查工具,其中来自窗口小部件树和对象信息的帧并排在PyCrust输出的帧上。

将GUI添加到命令行工具

本系列的第1部分介绍了一个简单的工具来显示所有正在运行的虚拟机(VM)的状态。 使用wxPython将该工具更改为GUI工具很简单。 wx.ListCtrl小部件仅提供您需要以表格形式显示信息的功能。 要使用wx.ListCtrl小部件,必须使用以下语法将其添加到框架中:

self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER)

您可以从几种不同的样式中进行选择,包括先前使用的wx.LC_REPORTwx.SUNKEN_BORDER选项。 第一个选项将wx.ListCtrl置于“报告”模式,这是四个可用模式之一。 其他的是图标,小图标和列表。 要添加wx.SUNKEN_BORDER类的wx.SUNKEN_BORDER ,您只需使用竖线字符( | )。 某些样式是互斥的,例如不同的边框样式,因此如果有任何疑问,请查阅wxPython Wiki(请参阅参考资料 )。

实例化wx.ListCtrl小部件之后,您可以开始向其中添加内容,例如列标题。 InsertColumn方法具有两个必需参数和两个可选参数。 首先是列索引,从零开始,然后是设置标题的字符串。 第三个用于格式化,并且应该类似于LIST_FORMAT_CENTER_LEFT_RIGHT 。 最后,您可以通过传入整数来设置固定宽度,或者使用wx.LIST_AUTOSIZE自动调整列的大小。

现在,您已经配置了wx.ListCtrl小部件,可以使用InsertStringItemSetStringItem方法向其中填充数据。 必须使用InsertStringItem方法添加wx.ListCtrl小部件中的每个新行。 这两个强制性参数指定执行插入的位置,值为0指示列表顶部,并在该位置插入字符串。 InsertStringItem返回一个整数,指示所插入字符串的行号。 您可以调用列表的GetItemCount() ,并使用索引的返回值追加到底部,如清单2所示。

清单2.命令行工具的GUI版本
import wx
import libvirt

conn=libvirt.open("qemu:///system")

class MyApp(wx.App):
    def OnInit(self):
        frame = wx.Frame(None, -1, "KVM Info")
        id=wx.NewId()
        self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.list.Show(True)
        self.list.InsertColumn(0,"ID")
        self.list.InsertColumn(1,"Name")
        self.list.InsertColumn(2,"State")
        self.list.InsertColumn(3,"Max Mem")
        self.list.InsertColumn(4,"# of vCPUs")
        self.list.InsertColumn(5,"CPU Time (ns)")

        for i,id in enumerate(conn.listDomainsID()):
            dom = conn.lookupByID(id)
            infos = dom.info()
            pos = self.list.InsertStringItem(i,str(id)) 
            self.list.SetStringItem(pos,1,dom.name())
            self.list.SetStringItem(pos,2,str(infos[0]))
            self.list.SetStringItem(pos,3,str(infos[1]))
            self.list.SetStringItem(pos,4,str(infos[3]))
            self.list.SetStringItem(pos,5,str(infos[2]))
            
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

app = MyApp(0)
app.MainLoop()

图2显示了这些努力的结果。

图2. GUI KVM信息工具
屏幕快照显示KVM信息,其中包含ID,名称,状态,最大内存,vCPU数量和CPU时间列

您可以增强此表的外观。 一个显着的改进是调整列的大小。 您可以通过将width =参数添加到InsertColumn调用中或使用一行代码来做到这一点,如下所示:

self.ListCtrl.SetColumnWidth(column,wx.LIST_AUTOSIZE)

您可以做的另一件事是添加一个大小调整器,以使控件随父窗口调整大小。 您可以使用wxBoxSizer在几行代码中完成此操作。 首先,创建sizer,然后向其中添加要调整大小的小部件以及主窗口。 该代码可能如下所示:

self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.list, proportion=1,flag=wx.EXPAND | wx.ALL, border=5)
self.sizer.Add(self.button, flag=wx.EXPAND | wx.ALL, border=5)
self.panel.SetSizerAndFit(self.sizer)

self.panel.SetSizerAndFit()的最后一次调用指示wxPython根据嵌入式窗口小部件中sizer的最小大小来设置窗格的初始大小。 这有助于根据内部内容为您的初始屏幕提供合理的尺寸。

根据用户动作控制流程

关于wx.ListCtrl小部件的一件好事是,您可以检测到用户何时单击小部件的特定部分,并据此采取一些措施。 使用此功能,您可以执行以下操作,例如,根据用户单击列标题,按向前或向后按字母顺序对列进行排序。 实现此目的的技术使用回调机制。 您必须通过将小部件和处理方法绑定在一起来提供处理您要处理的每个动作的功能。 您可以使用Bind方法进行操作。

每个小部件都有一些与之关联的事件。 也有与鼠标相关的事件。 鼠标事件的名称类似EVT_LEFT_DOWNEVT_LEFT_UPEVT_LEFT_DCLICK ,以及其他按钮的相同命名约定。 您可以通过附加到EVT_MOUSE_EVENTS类型来处理所有鼠标事件。 技巧是在您感兴趣的应用程序或窗口的上下文中捕获事件。

当控制权传递给事件处理程序时,它必须执行必要的步骤来处理该动作,然后将控制权返回到之前的任何位置。 这是每个GUI必须实现的事件驱动程序编程模型,以便及时处理用户操作。 许多现代的GUI应用程序都实现了多线程,以防止给用户留下程序没有响应的印象。 我将在本文稍后简要介绍。

计时器代表程序必须处理的另一种事件。 例如,您可能希望以用户定义的时间间隔执行定期监视功能。 您需要提供一个屏幕,用户可以在该屏幕上指定间隔,然后启动计时器,该计时器将在事件到期时触发事件。 计时器到期会触发一个事件,您可以使用该事件来激活一段代码。 您可能需要设置或重新启动时间,具体取决于用户偏好。 您可以轻松地使用此技术来开发VM监视工具。

清单3提供了一个带有按钮和静态文本行的简单演示应用程序。 使用wx.StaticText是将字符串输出到窗口的简便方法。 想法是单击一次按钮以启动计时器并记录开始时间。 单击按钮将记录开始时间,并将标签更改为Stop 。 再次单击该按钮将填写“停止时间”文本框,然后将按钮更改回“ 开始”

清单3.带有按钮和静态文本的简单应用
import wx
from time import gmtime, strftime

class MyForm(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Buttons")
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        self.button = wx.Button(self.panel, id=wx.ID_ANY, label="Start")
        self.button.Bind(wx.EVT_BUTTON, self.onButton)

    def onButton(self, event):
        if self.button.GetLabel() == "Start":
            self.button.SetLabel("Stop")
            strtime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
            wx.StaticText(self, -1, 'Start Time = ' + strtime, (25, 75))
        else:
            self.button.SetLabel("Start")
            stptime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
            wx.StaticText(self, -1, 'Stop Time = ' + stptime, (25, 100))
 
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()

增强的监控GUI

现在,您可以向前面介绍的简单监视GUI添加功能。 在拥有创建应用程序所需的一切之前,还需要了解wxPython的另一部分。 在wx.ListCtrl小部件的第一列中添加一个复选框将使得可以根据复选框的状态对多行进行操作。 您可以使用wxPython所谓的mixins来实现。 本质上, mixin是一个帮助程序类,它向父窗口小部件添加了某种类型的功能。 要添加复选框mixin,只需使用以下代码对其进行实例化:

listmix.CheckListCtrlMixin.__init__(self)

您还可以利用事件,通过单击列标题来添加选择或清除所有框的功能。 这样一来,只需单击几下即可轻松启动或停止所有VM等操作。 您需要编写一些事件处理程序,以与之前更改按钮上的标签相同的方式来响应适当的事件。 这是为列click事件设置处理程序所需的代码行:

self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)

单击任何列标题时, wx.EVT_LIST_COL_CLICK触发wx.EVT_LIST_COL_CLICK 。 要确定单击了哪一列,可以使用event.GetColumn()方法。 这是OnColClick事件的简单处理函数:

def OnColClick(self, event):
    print "column clicked %d\n" % event.GetColumn()
	event.Skip()

如果您需要将事件传播到其他处理程序,则event.Skip()调用很重要。 尽管这种需求在这种情况下可能并不明显,但是当多个处理程序需要处理同一事件时,这可能会成为问题。 在wxPython Wiki站点上对事件传播进行了很好的讨论,其细节远远超出我在此留出的余地。

最后,将代码添加到两个按钮处理程序中,以启动或停止所有检查的VM。 可以遍历wx.ListCtrl各行,仅用几行代码wx.ListCtrl提取VM ID,如清单4所示。

清单4.启动和停止检查的VM
#!/usr/bin/env python

import wx
import wx.lib.mixins.listctrl as listmix
import libvirt

conn=libvirt.open("qemu:///system")

class CheckListCtrl(wx.ListCtrl, listmix.CheckListCtrlMixin, 
                                 listmix.ListCtrlAutoWidthMixin):
    def __init__(self, *args, **kwargs):
        wx.ListCtrl.__init__(self, *args, **kwargs)
        listmix.CheckListCtrlMixin.__init__(self)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        self.setResizeColumn(2)

class MainWindow(wx.Frame):

    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self)
        self.list = CheckListCtrl(self.panel, style=wx.LC_REPORT)
        self.list.InsertColumn(0, "Check", width = 175)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)

        self.list.InsertColumn(1,"Max Mem", width = 100)
        self.list.InsertColumn(2,"# of vCPUs", width = 100)

        for i,id in enumerate(conn.listDefinedDomains()):
            dom = conn.lookupByName(id)
            infos = dom.info()
            pos = self.list.InsertStringItem(1,dom.name()) 
            self.list.SetStringItem(pos,1,str(infos[1]))
            self.list.SetStringItem(pos,2,str(infos[3]))

        self.StrButton = wx.Button(self.panel, label="Start")
        self.Bind(wx.EVT_BUTTON, self.onStrButton, self.StrButton)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.list, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        self.sizer.Add(self.StrButton, flag=wx.EXPAND | wx.ALL, border=5)
        self.panel.SetSizerAndFit(self.sizer)
        self.Show()

    def onStrButton(self, event):
        if self.StrButton.GetLabel() == "Start":
	    num = self.list.GetItemCount()
            for i in range(num):
                if self.list.IsChecked(i):
                    dom = conn.lookupByName(self.list.GetItem(i, 0).Text)
                    dom.create()
                    print "%d started" % dom.ID()
 
    def OnColClick(self, event):
         item = self.list.GetColumn(0)
         if item is not None:
             if item.GetText() == "Check":
                 item.SetText("Uncheck")
                 self.list.SetColumn(0, item)
                 num = self.list.GetItemCount()
                 for i in range(num):
                     self.list.CheckItem(i,True)
             else:
                 item.SetText("Check")
                 self.list.SetColumn(0, item)
                 num = self.list.GetItemCount()
                 for i in range(num):
                     self.list.CheckItem(i,False)

         event.Skip()
 
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()

关于KVM中VM的状态,这里有两点需要指出:当您使用libvirtlistDomainsID()方法时,将显示正在运行的VM。 要查看未运行的计算机,必须使用listDefinedDomains() 。 您只需要将两者分开,就可以知道可以启动和停止哪些VM。

结语

本文主要关注使用wxPython构建GUI包装器所需的步骤,而wxPython则使用libvirt管理KVM。 wxPython库功能丰富,并提供了各种小部件,使您能够构建具有专业外观的基于GUI的应用程序。 本文只是简单介绍了其功能,但希望您有动力进一步进行研究。 确保检查更多参考资料,以帮助您的应用程序运行。


翻译自: https://www.ibm.com/developerworks/opensource/library/os-python-kvm-scripting2/index.html

libvirt kvm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值