This example allows to use the guest agent event and metadata to track
vCPU count set via the guest agent (agent-based onlining/offlining) and
keep it persistent accross domain restarts.
The daemon listens for the agent lifecycle event, and if it's received
it looks into doman's metadata to see whether a desired count was set
and issues the guest agent command.
---
MANIFEST.in | 2 +
examples/README | 2 +
examples/guest-vcpus/guest-vcpu-daemon.py | 160 ++++++++++++++++++++++++++++++
examples/guest-vcpus/guest-vcpu.py | 74 ++++++++++++++
4 files changed, 238 insertions(+)
create mode 100755 examples/guest-vcpus/guest-vcpu-daemon.py
create mode 100755 examples/guest-vcpus/guest-vcpu.py
diff --git a/MANIFEST.in b/MANIFEST.in
index ad08207..230baea 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -11,6 +11,8 @@ include examples/domsave.py
include examples/domstart.py
include examples/esxlist.py
include examples/event-test.py
+include examples/guest-vcpus/guest-vcpu-daemon.py
+include examples/guest-vcpus/guest-vcpu.py
include examples/topology.py
include generator.py
include libvirt-lxc-override-api.xml
diff --git a/examples/README b/examples/README
index 1d4b425..0cb4513 100644
--- a/examples/README
+++ b/examples/README
@@ -12,6 +12,8 @@ esxlist.py - list active domains of an VMware ESX host and print some
info.
also demonstrates how to use the libvirt.openAuth() method
dhcpleases.py - list dhcp leases for a given virtual network
domipaddrs.py - list IP addresses for guest domains
+guest-vcpus - two helpers to make the guest agent event useful with agent based
+ vCPU state modification
The XML files in this directory are examples of the XML format that libvirt
expects, and will have to be adapted for your setup. They are only needed
diff --git a/examples/guest-vcpus/guest-vcpu-daemon.py
b/examples/guest-vcpus/guest-vcpu-daemon.py
new file mode 100755
index 0000000..c7c08a8
--- /dev/null
+++ b/examples/guest-vcpus/guest-vcpu-daemon.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+import libvirt
+import threading
+from xml.dom import minidom
+import time
+import sys
+import getopt
+import os
+
+uri = "qemu:///system"
+customXMLuri = "guest-cpu.python.libvirt.org"
+connectRetryTimeout = 5
+
+def usage():
+ print("usage: "+os.path.basename(sys.argv[0])+" [-h] [uri]")
+ print(" uri will default to qemu:///system")
+ print(" --help, -h Print(this help message")
+ print("")
+ print("This service waits for the guest agent lifecycle event and reissues
" +
+ "guest agent calls to modify the cpu count according to the metadata
" +
+ "set by guest-vcpu.py example")
+
+class workerData:
+ def __init__(self):
+ self.doms = list()
+ self.conn = None
+ self.cond = threading.Condition()
+
+ def notify(self):
+ self.cond.acquire()
+ self.cond.notify()
+ self.cond.release()
+
+ def waitNotify(self):
+ self.cond.acquire()
+ self.cond.wait()
+ self.cond.release()
+
+ def addDomainNotify(self, dom):
+ self.doms.append(dom)
+ self.notify()
+
+ def closeConnectNotify(self):
+ conn = self.conn
+ self.conn = None
+ conn.close()
+ self.notify()
+
+ def setConnect(self, conn):
+ self.conn = conn
+
+ def hasConn(self):
+ return self.conn is not None
+
+ def hasDom(self):
+ return len(self.doms) > 0
+
+ def getDom(self):
+ return self.doms.pop()
+
+ def setDoms(self, doms):
+ self.doms = doms
+
+
+def virEventLoopNativeRun():
+ while True:
+ libvirt.virEventRunDefaultImpl()
+
+def handleAgentLifecycleEvent(conn, dom, state, reason, opaque):
+ if state == libvirt.VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_CONNECTED:
+ opaque.addDomainNotify(dom)
+
+def handleConnectClose(conn, reason, opaque):
+ print('Disconnected from ' + uri)
+ opaque.closeConnectNotify()
+
+def handleLibvirtLibraryError(opaque, error):
+ pass
+
+def processAgentConnect(dom):
+ try:
+ cpus = dom.metadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, customXMLuri,
+ libvirt.VIR_DOMAIN_AFFECT_LIVE)
+ doc = minidom.parseString(cpus)
+ ncpus =
int(doc.getElementsByTagName('ncpus')[0].getAttribute('count'))
+ except:
+ return
+
+ try:
+ dom.setVcpusFlags(ncpus, libvirt.VIR_DOMAIN_AFFECT_LIVE |
libvirt.VIR_DOMAIN_VCPU_GUEST)
+ print("set vcpu count for domain " + dom.name() + " to " +
str(ncpus))
+ except:
+ print("failed to set vcpu count for domain " + dom.name())
+
+def work():
+ data = workerData()
+
+ print("Using uri: " + uri)
+
+ while True:
+ if not data.hasConn():
+ try:
+ conn = libvirt.open(uri)
+ except:
+ print('Failed to connect to ' + uri + ', retry in ' +
str(connectRetryTimeout)) + ' seconds'
+ time.sleep(connectRetryTimeout)
+ continue
+
+ print('Connected to ' + uri)
+
+ data.setConnect(conn)
+ conn.registerCloseCallback(handleConnectClose, data)
+ conn.setKeepAlive(5, 3)
+ conn.domainEventRegisterAny(None,
+ libvirt.VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE,
+ handleAgentLifecycleEvent,
+ data)
+
+ data.setDoms(conn.listAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE))
+
+ while data.hasConn() and data.hasDom():
+ processAgentConnect(data.getDom())
+
+ data.waitNotify()
+
+def main():
+ libvirt.virEventRegisterDefaultImpl()
+ libvirt.registerErrorHandler(handleLibvirtLibraryError, None)
+
+ worker = threading.Thread(target=work)
+ worker.setDaemon(True)
+ worker.start()
+
+ eventLoop = threading.Thread(target=virEventLoopNativeRun)
+ eventLoop.setDaemon(True)
+ eventLoop.start()
+
+ while True:
+ time.sleep(1)
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
+ except getopt.GetoptError as err:
+ print(str(err))
+ usage()
+ sys.exit(2)
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ usage()
+ sys.exit()
+
+ if len(args) > 1:
+ usage()
+ sys.exit(1)
+ elif len(args) == 1:
+ uri = args[0]
+
+ main()
diff --git a/examples/guest-vcpus/guest-vcpu.py b/examples/guest-vcpus/guest-vcpu.py
new file mode 100755
index 0000000..8faba87
--- /dev/null
+++ b/examples/guest-vcpus/guest-vcpu.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+
+import libvirt
+import sys
+import getopt
+import os
+
+customXMLuri = "guest-cpu.python.libvirt.org"
+
+def usage():
+ print("usage: "+os.path.basename(sys.argv[0])+" [-hcl] domain count
[uri]")
+ print(" uri will default to qemu:///system")
+ print(" --help, -h Print(this help message")
+ print(" --config, -c Modify persistent domain configuration")
+ print(" --live, -l Modify live domain configuration")
+ print("")
+ print("Sets the vCPU count via the guest agent and sets the metadata element
" +
+ "used by guest-vcpu-daemon.py example")
+
+uri = "qemu:///system"
+flags = 0
+live = False;
+config = False;
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], "hcl", ["help",
"config", "live"])
+except getopt.GetoptError as err:
+ # print help information and exit:
+ print(str(err)) # will print something like "option -a not recognized"
+ usage()
+ sys.exit(2)
+for o, a in opts:
+ if o in ("-h", "--help"):
+ usage()
+ sys.exit()
+ if o in ("-c", "--config"):
+ config = True
+ flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG
+ if o in ("-l", "--live"):
+ live = True
+ flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE
+
+if len(args) < 2:
+ usage()
+ sys.exit(1)
+elif len(args) >= 3:
+ uri = args[2]
+
+domain = args[0]
+count = int(args[1])
+
+conn = libvirt.open(uri)
+dom = conn.lookupByName(domain)
+
+if flags == 0 or config:
+ confvcpus = dom.vcpusFlags(libvirt.VIR_DOMAIN_AFFECT_CONFIG)
+
+ if confvcpus < count:
+ print("Persistent domain configuration has only " + str(confvcpus) +
" vcpus configured")
+ sys.exit(1)
+
+if flags == 0 or live:
+ livevcpus = dom.vcpusFlags(libvirt.VIR_DOMAIN_AFFECT_LIVE)
+
+ if livevcpus < count:
+ print("Live domain configuration has only " + str(livevcpus) + "
vcpus configured")
+ sys.exit(1)
+
+if flags == 0 or live:
+ dom.setVcpusFlags(count, libvirt.VIR_DOMAIN_AFFECT_LIVE |
libvirt.VIR_DOMAIN_VCPU_GUEST)
+
+meta = "<ncpus count='" + str(count) + "'/>"
+
+dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta, "guestvcpudaemon",
customXMLuri, flags)
--
2.3.5