Release of libvirt-python-4.5.0
[libvirt-python/ericb.git] / examples / event-test.py
blob281e6614452a86872578765a108e0e6a5a51238d
1 #!/usr/bin/env python
5 ##############################################################################
6 # Start off by implementing a general purpose event loop for anyone's use
7 ##############################################################################
9 import sys
10 import getopt
11 import os
12 import libvirt
13 import select
14 import errno
15 import time
16 import threading
18 # This example can use three different event loop impls. It defaults
19 # to a portable pure-python impl based on poll that is implemented
20 # in this file.
22 # When Python >= 3.4, it can optionally use an impl based on the
23 # new asyncio module.
25 # Finally, it can also use the libvirt native event loop impl
27 # This setting thus allows 'poll', 'native' or 'asyncio' as valid
28 # choices
30 event_impl = "poll"
32 do_debug = False
33 def debug(msg):
34 global do_debug
35 if do_debug:
36 print(msg)
39 # This general purpose event loop will support waiting for file handle
40 # I/O and errors events, as well as scheduling repeatable timers with
41 # a fixed interval.
43 # It is a pure python implementation based around the poll() API
45 class virEventLoopPoll:
46 # This class contains the data we need to track for a
47 # single file handle
48 class virEventLoopPollHandle:
49 def __init__(self, handle, fd, events, cb, opaque):
50 self.handle = handle
51 self.fd = fd
52 self.events = events
53 self.cb = cb
54 self.opaque = opaque
56 def get_id(self):
57 return self.handle
59 def get_fd(self):
60 return self.fd
62 def get_events(self):
63 return self.events
65 def set_events(self, events):
66 self.events = events
68 def dispatch(self, events):
69 self.cb(self.handle,
70 self.fd,
71 events,
72 self.opaque)
74 # This class contains the data we need to track for a
75 # single periodic timer
76 class virEventLoopPollTimer:
77 def __init__(self, timer, interval, cb, opaque):
78 self.timer = timer
79 self.interval = interval
80 self.cb = cb
81 self.opaque = opaque
82 self.lastfired = 0
84 def get_id(self):
85 return self.timer
87 def get_interval(self):
88 return self.interval
90 def set_interval(self, interval):
91 self.interval = interval
93 def get_last_fired(self):
94 return self.lastfired
96 def set_last_fired(self, now):
97 self.lastfired = now
99 def dispatch(self):
100 self.cb(self.timer,
101 self.opaque)
104 def __init__(self):
105 self.poll = select.poll()
106 self.pipetrick = os.pipe()
107 self.pendingWakeup = False
108 self.runningPoll = False
109 self.nextHandleID = 1
110 self.nextTimerID = 1
111 self.handles = []
112 self.timers = []
113 self.cleanup = []
114 self.quit = False
116 # The event loop can be used from multiple threads at once.
117 # Specifically while the main thread is sleeping in poll()
118 # waiting for events to occur, another thread may come along
119 # and add/update/remove a file handle, or timer. When this
120 # happens we need to interrupt the poll() sleep in the other
121 # thread, so that it'll see the file handle / timer changes.
123 # Using OS level signals for this is very unreliable and
124 # hard to implement correctly. Thus we use the real classic
125 # "self pipe" trick. A anonymous pipe, with one end registered
126 # with the event loop for input events. When we need to force
127 # the main thread out of a poll() sleep, we simple write a
128 # single byte of data to the other end of the pipe.
129 debug("Self pipe watch %d write %d" %(self.pipetrick[0], self.pipetrick[1]))
130 self.poll.register(self.pipetrick[0], select.POLLIN)
133 # Calculate when the next timeout is due to occur, returning
134 # the absolute timestamp for the next timeout, or 0 if there is
135 # no timeout due
136 def next_timeout(self):
137 next = 0
138 for t in self.timers:
139 last = t.get_last_fired()
140 interval = t.get_interval()
141 if interval < 0:
142 continue
143 if next == 0 or (last + interval) < next:
144 next = last + interval
146 return next
148 # Lookup a virEventLoopPollHandle object based on file descriptor
149 def get_handle_by_fd(self, fd):
150 for h in self.handles:
151 if h.get_fd() == fd:
152 return h
153 return None
155 # Lookup a virEventLoopPollHandle object based on its event loop ID
156 def get_handle_by_id(self, handleID):
157 for h in self.handles:
158 if h.get_id() == handleID:
159 return h
160 return None
163 # This is the heart of the event loop, performing one single
164 # iteration. It asks when the next timeout is due, and then
165 # calculates the maximum amount of time it is able to sleep
166 # for in poll() pending file handle events.
168 # It then goes into the poll() sleep.
170 # When poll() returns, there will zero or more file handle
171 # events which need to be dispatched to registered callbacks
172 # It may also be time to fire some periodic timers.
174 # Due to the coarse granularity of scheduler timeslices, if
175 # we ask for a sleep of 500ms in order to satisfy a timer, we
176 # may return up to 1 scheduler timeslice early. So even though
177 # our sleep timeout was reached, the registered timer may not
178 # technically be at its expiry point. This leads to us going
179 # back around the loop with a crazy 5ms sleep. So when checking
180 # if timeouts are due, we allow a margin of 20ms, to avoid
181 # these pointless repeated tiny sleeps.
182 def run_once(self):
183 sleep = -1
184 self.runningPoll = True
186 for opaque in self.cleanup:
187 libvirt.virEventInvokeFreeCallback(opaque)
188 self.cleanup = []
190 try:
191 next = self.next_timeout()
192 debug("Next timeout due at %d" % next)
193 if next > 0:
194 now = int(time.time() * 1000)
195 if now >= next:
196 sleep = 0
197 else:
198 sleep = (next - now) / 1000.0
200 debug("Poll with a sleep of %d" % sleep)
201 events = self.poll.poll(sleep)
203 # Dispatch any file handle events that occurred
204 for (fd, revents) in events:
205 # See if the events was from the self-pipe
206 # telling us to wakup. if so, then discard
207 # the data just continue
208 if fd == self.pipetrick[0]:
209 self.pendingWakeup = False
210 data = os.read(fd, 1)
211 continue
213 h = self.get_handle_by_fd(fd)
214 if h:
215 debug("Dispatch fd %d handle %d events %d" % (fd, h.get_id(), revents))
216 h.dispatch(self.events_from_poll(revents))
218 now = int(time.time() * 1000)
219 for t in self.timers:
220 interval = t.get_interval()
221 if interval < 0:
222 continue
224 want = t.get_last_fired() + interval
225 # Deduct 20ms, since scheduler timeslice
226 # means we could be ever so slightly early
227 if now >= (want-20):
228 debug("Dispatch timer %d now %s want %s" % (t.get_id(), str(now), str(want)))
229 t.set_last_fired(now)
230 t.dispatch()
232 except (os.error, select.error) as e:
233 if e.args[0] != errno.EINTR:
234 raise
235 finally:
236 self.runningPoll = False
239 # Actually run the event loop forever
240 def run_loop(self):
241 self.quit = False
242 while not self.quit:
243 self.run_once()
245 def interrupt(self):
246 if self.runningPoll and not self.pendingWakeup:
247 self.pendingWakeup = True
248 os.write(self.pipetrick[1], 'c'.encode("UTF-8"))
251 # Registers a new file handle 'fd', monitoring for 'events' (libvirt
252 # event constants), firing the callback cb() when an event occurs.
253 # Returns a unique integer identier for this handle, that should be
254 # used to later update/remove it
255 def add_handle(self, fd, events, cb, opaque):
256 handleID = self.nextHandleID + 1
257 self.nextHandleID = self.nextHandleID + 1
259 h = self.virEventLoopPollHandle(handleID, fd, events, cb, opaque)
260 self.handles.append(h)
262 self.poll.register(fd, self.events_to_poll(events))
263 self.interrupt()
265 debug("Add handle %d fd %d events %d" % (handleID, fd, events))
267 return handleID
269 # Registers a new timer with periodic expiry at 'interval' ms,
270 # firing cb() each time the timer expires. If 'interval' is -1,
271 # then the timer is registered, but not enabled
272 # Returns a unique integer identier for this handle, that should be
273 # used to later update/remove it
274 def add_timer(self, interval, cb, opaque):
275 timerID = self.nextTimerID + 1
276 self.nextTimerID = self.nextTimerID + 1
278 h = self.virEventLoopPollTimer(timerID, interval, cb, opaque)
279 self.timers.append(h)
280 self.interrupt()
282 debug("Add timer %d interval %d" % (timerID, interval))
284 return timerID
286 # Change the set of events to be monitored on the file handle
287 def update_handle(self, handleID, events):
288 h = self.get_handle_by_id(handleID)
289 if h:
290 h.set_events(events)
291 self.poll.unregister(h.get_fd())
292 self.poll.register(h.get_fd(), self.events_to_poll(events))
293 self.interrupt()
295 debug("Update handle %d fd %d events %d" % (handleID, h.get_fd(), events))
297 # Change the periodic frequency of the timer
298 def update_timer(self, timerID, interval):
299 for h in self.timers:
300 if h.get_id() == timerID:
301 h.set_interval(interval)
302 self.interrupt()
304 debug("Update timer %d interval %d" % (timerID, interval))
305 break
307 # Stop monitoring for events on the file handle
308 def remove_handle(self, handleID):
309 handles = []
310 for h in self.handles:
311 if h.get_id() == handleID:
312 debug("Remove handle %d fd %d" % (handleID, h.get_fd()))
313 self.poll.unregister(h.get_fd())
314 self.cleanup.append(h.opaque)
315 else:
316 handles.append(h)
317 self.handles = handles
318 self.interrupt()
320 # Stop firing the periodic timer
321 def remove_timer(self, timerID):
322 timers = []
323 for h in self.timers:
324 if h.get_id() != timerID:
325 timers.append(h)
326 else:
327 debug("Remove timer %d" % timerID)
328 self.cleanup.append(h.opaque)
329 self.timers = timers
330 self.interrupt()
332 # Convert from libvirt event constants, to poll() events constants
333 def events_to_poll(self, events):
334 ret = 0
335 if events & libvirt.VIR_EVENT_HANDLE_READABLE:
336 ret |= select.POLLIN
337 if events & libvirt.VIR_EVENT_HANDLE_WRITABLE:
338 ret |= select.POLLOUT
339 if events & libvirt.VIR_EVENT_HANDLE_ERROR:
340 ret |= select.POLLERR
341 if events & libvirt.VIR_EVENT_HANDLE_HANGUP:
342 ret |= select.POLLHUP
343 return ret
345 # Convert from poll() event constants, to libvirt events constants
346 def events_from_poll(self, events):
347 ret = 0
348 if events & select.POLLIN:
349 ret |= libvirt.VIR_EVENT_HANDLE_READABLE
350 if events & select.POLLOUT:
351 ret |= libvirt.VIR_EVENT_HANDLE_WRITABLE
352 if events & select.POLLNVAL:
353 ret |= libvirt.VIR_EVENT_HANDLE_ERROR
354 if events & select.POLLERR:
355 ret |= libvirt.VIR_EVENT_HANDLE_ERROR
356 if events & select.POLLHUP:
357 ret |= libvirt.VIR_EVENT_HANDLE_HANGUP
358 return ret
361 ###########################################################################
362 # Now glue an instance of the general event loop into libvirt's event loop
363 ###########################################################################
365 # This single global instance of the event loop wil be used for
366 # monitoring libvirt events
367 eventLoop = virEventLoopPoll()
369 # This keeps track of what thread is running the event loop,
370 # (if it is run in a background thread)
371 eventLoopThread = None
374 # These next set of 6 methods are the glue between the official
375 # libvirt events API, and our particular impl of the event loop
377 # There is no reason why the 'virEventLoopPoll' has to be used.
378 # An application could easily may these 6 glue methods hook into
379 # another event loop such as GLib's, or something like the python
380 # Twisted event framework.
382 def virEventAddHandleImpl(fd, events, cb, opaque):
383 global eventLoop
384 return eventLoop.add_handle(fd, events, cb, opaque)
386 def virEventUpdateHandleImpl(handleID, events):
387 global eventLoop
388 return eventLoop.update_handle(handleID, events)
390 def virEventRemoveHandleImpl(handleID):
391 global eventLoop
392 return eventLoop.remove_handle(handleID)
394 def virEventAddTimerImpl(interval, cb, opaque):
395 global eventLoop
396 return eventLoop.add_timer(interval, cb, opaque)
398 def virEventUpdateTimerImpl(timerID, interval):
399 global eventLoop
400 return eventLoop.update_timer(timerID, interval)
402 def virEventRemoveTimerImpl(timerID):
403 global eventLoop
404 return eventLoop.remove_timer(timerID)
406 # This tells libvirt what event loop implementation it
407 # should use
408 def virEventLoopPollRegister():
409 libvirt.virEventRegisterImpl(virEventAddHandleImpl,
410 virEventUpdateHandleImpl,
411 virEventRemoveHandleImpl,
412 virEventAddTimerImpl,
413 virEventUpdateTimerImpl,
414 virEventRemoveTimerImpl)
416 # Directly run the event loop in the current thread
417 def virEventLoopPollRun():
418 global eventLoop
419 eventLoop.run_loop()
421 def virEventLoopAIORun(loop):
422 import asyncio
423 asyncio.set_event_loop(loop)
424 loop.run_forever()
426 def virEventLoopNativeRun():
427 while True:
428 libvirt.virEventRunDefaultImpl()
430 # Spawn a background thread to run the event loop
431 def virEventLoopPollStart():
432 global eventLoopThread
433 virEventLoopPollRegister()
434 eventLoopThread = threading.Thread(target=virEventLoopPollRun, name="libvirtEventLoop")
435 eventLoopThread.setDaemon(True)
436 eventLoopThread.start()
438 def virEventLoopAIOStart():
439 global eventLoopThread
440 import libvirtaio
441 import asyncio
442 loop = asyncio.new_event_loop()
443 libvirtaio.virEventRegisterAsyncIOImpl(loop=loop)
444 eventLoopThread = threading.Thread(target=virEventLoopAIORun, args=(loop,), name="libvirtEventLoop")
445 eventLoopThread.setDaemon(True)
446 eventLoopThread.start()
448 def virEventLoopNativeStart():
449 global eventLoopThread
450 libvirt.virEventRegisterDefaultImpl()
451 eventLoopThread = threading.Thread(target=virEventLoopNativeRun, name="libvirtEventLoop")
452 eventLoopThread.setDaemon(True)
453 eventLoopThread.start()
456 ##########################################################################
457 # Everything that now follows is a simple demo of domain lifecycle events
458 ##########################################################################
459 def domEventToString(event):
460 domEventStrings = ( "Defined",
461 "Undefined",
462 "Started",
463 "Suspended",
464 "Resumed",
465 "Stopped",
466 "Shutdown",
467 "PMSuspended",
468 "Crashed",
470 return domEventStrings[event]
472 def domDetailToString(event, detail):
473 domEventStrings = (
474 ( "Added", "Updated" ),
475 ( "Removed", ),
476 ( "Booted", "Migrated", "Restored", "Snapshot", "Wakeup" ),
477 ( "Paused", "Migrated", "IOError", "Watchdog", "Restored", "Snapshot", "API error" ),
478 ( "Unpaused", "Migrated", "Snapshot" ),
479 ( "Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", "Failed", "Snapshot"),
480 ( "Finished", "On guest request", "On host request"),
481 ( "Memory", "Disk" ),
482 ( "Panicked", ),
484 return domEventStrings[event][detail]
486 def blockJobTypeToString(type):
487 blockJobTypes = ( "unknown", "Pull", "Copy", "Commit", "ActiveCommit", )
488 return blockJobTypes[type]
490 def blockJobStatusToString(status):
491 blockJobStatus = ( "Completed", "Failed", "Canceled", "Ready", )
492 return blockJobStatus[status]
494 def agentLifecycleStateToString(state):
495 agentStates = ( "unknown", "connected", "disconnected", )
496 return agentStates[state]
498 def agentLifecycleReasonToString(reason):
499 agentReasons = ( "unknown", "domain started", "channel event", )
500 return agentReasons[reason]
502 def myDomainEventCallback1 (conn, dom, event, detail, opaque):
503 print("myDomainEventCallback1 EVENT: Domain %s(%s) %s %s" % (dom.name(), dom.ID(),
504 domEventToString(event),
505 domDetailToString(event, detail)))
507 def myDomainEventCallback2 (conn, dom, event, detail, opaque):
508 print("myDomainEventCallback2 EVENT: Domain %s(%s) %s %s" % (dom.name(), dom.ID(),
509 domEventToString(event),
510 domDetailToString(event, detail)))
512 def myDomainEventRebootCallback(conn, dom, opaque):
513 print("myDomainEventRebootCallback: Domain %s(%s)" % (dom.name(), dom.ID()))
515 def myDomainEventRTCChangeCallback(conn, dom, utcoffset, opaque):
516 print("myDomainEventRTCChangeCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), utcoffset))
518 def myDomainEventWatchdogCallback(conn, dom, action, opaque):
519 print("myDomainEventWatchdogCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), action))
521 def myDomainEventIOErrorCallback(conn, dom, srcpath, devalias, action, opaque):
522 print("myDomainEventIOErrorCallback: Domain %s(%s) %s %s %d" % (dom.name(), dom.ID(), srcpath, devalias, action))
523 def myDomainEventIOErrorReasonCallback(conn, dom, srcpath, devalias, action, reason, opaque):
524 print("myDomainEventIOErrorReasonCallback: Domain %s(%s) %s %s %d %s" % (dom.name(), dom.ID(), srcpath, devalias, action, reason))
525 def myDomainEventGraphicsCallback(conn, dom, phase, localAddr, remoteAddr, authScheme, subject, opaque):
526 print("myDomainEventGraphicsCallback: Domain %s(%s) %d %s" % (dom.name(), dom.ID(), phase, authScheme))
527 def myDomainEventControlErrorCallback(conn, dom, opaque):
528 print("myDomainEventControlErrorCallback: Domain %s(%s)" % (dom.name(), dom.ID()))
529 def myDomainEventBlockJobCallback(conn, dom, disk, type, status, opaque):
530 print("myDomainEventBlockJobCallback: Domain %s(%s) %s on disk %s %s" % (dom.name(), dom.ID(), blockJobTypeToString(type), disk, blockJobStatusToString(status)))
531 def myDomainEventDiskChangeCallback(conn, dom, oldSrcPath, newSrcPath, devAlias, reason, opaque):
532 print("myDomainEventDiskChangeCallback: Domain %s(%s) disk change oldSrcPath: %s newSrcPath: %s devAlias: %s reason: %s" % (
533 dom.name(), dom.ID(), oldSrcPath, newSrcPath, devAlias, reason))
534 def myDomainEventTrayChangeCallback(conn, dom, devAlias, reason, opaque):
535 print("myDomainEventTrayChangeCallback: Domain %s(%s) tray change devAlias: %s reason: %s" % (
536 dom.name(), dom.ID(), devAlias, reason))
537 def myDomainEventPMWakeupCallback(conn, dom, reason, opaque):
538 print("myDomainEventPMWakeupCallback: Domain %s(%s) system pmwakeup" % (
539 dom.name(), dom.ID()))
540 def myDomainEventPMSuspendCallback(conn, dom, reason, opaque):
541 print("myDomainEventPMSuspendCallback: Domain %s(%s) system pmsuspend" % (
542 dom.name(), dom.ID()))
543 def myDomainEventBalloonChangeCallback(conn, dom, actual, opaque):
544 print("myDomainEventBalloonChangeCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), actual))
545 def myDomainEventPMSuspendDiskCallback(conn, dom, reason, opaque):
546 print("myDomainEventPMSuspendDiskCallback: Domain %s(%s) system pmsuspend_disk" % (
547 dom.name(), dom.ID()))
548 def myDomainEventDeviceRemovedCallback(conn, dom, dev, opaque):
549 print("myDomainEventDeviceRemovedCallback: Domain %s(%s) device removed: %s" % (
550 dom.name(), dom.ID(), dev))
551 def myDomainEventBlockJob2Callback(conn, dom, disk, type, status, opaque):
552 print("myDomainEventBlockJob2Callback: Domain %s(%s) %s on disk %s %s" % (dom.name(), dom.ID(), blockJobTypeToString(type), disk, blockJobStatusToString(status)))
553 def myDomainEventTunableCallback(conn, dom, params, opaque):
554 print("myDomainEventTunableCallback: Domain %s(%s) %s" % (dom.name(), dom.ID(), params))
555 def myDomainEventAgentLifecycleCallback(conn, dom, state, reason, opaque):
556 print("myDomainEventAgentLifecycleCallback: Domain %s(%s) %s %s" % (dom.name(), dom.ID(), agentLifecycleStateToString(state), agentLifecycleReasonToString(reason)))
557 def myDomainEventDeviceAddedCallback(conn, dom, dev, opaque):
558 print("myDomainEventDeviceAddedCallback: Domain %s(%s) device added: %s" % (
559 dom.name(), dom.ID(), dev))
560 def myDomainEventMigrationIteration(conn, dom, iteration, opaque):
561 print("myDomainEventMigrationIteration: Domain %s(%s) started migration iteration %d" % (
562 dom.name(), dom.ID(), iteration))
563 def myDomainEventJobCompletedCallback(conn, dom, params, opaque):
564 print("myDomainEventJobCompletedCallback: Domain %s(%s) %s" % (dom.name(), dom.ID(), params))
565 def myDomainEventDeviceRemovalFailedCallback(conn, dom, dev, opaque):
566 print("myDomainEventDeviceRemovalFailedCallback: Domain %s(%s) failed to remove device: %s" % (
567 dom.name(), dom.ID(), dev))
568 def myDomainEventMetadataChangeCallback(conn, dom, mtype, nsuri, opaque):
569 print("myDomainEventMetadataChangeCallback: Domain %s(%s) changed metadata mtype=%d nsuri=%s" % (
570 dom.name(), dom.ID(), mtype, nsuri))
571 def myDomainEventBlockThresholdCallback(conn, dom, dev, path, threshold, excess, opaque):
572 print("myDomainEventBlockThresholdCallback: Domain %s(%s) block device %s(%s) threshold %d exceeded by %d" % (
573 dom.name(), dom.ID(), dev, path, threshold, excess))
575 ##########################################################################
576 # Network events
577 ##########################################################################
578 def netEventToString(event):
579 netEventStrings = ( "Defined",
580 "Undefined",
581 "Started",
582 "Stopped",
584 return netEventStrings[event]
586 def netDetailToString(event, detail):
587 netEventStrings = (
588 ( "Added", ),
589 ( "Removed", ),
590 ( "Started", ),
591 ( "Stopped", ),
593 return netEventStrings[event][detail]
595 def myNetworkEventLifecycleCallback(conn, net, event, detail, opaque):
596 print("myNetworkEventLifecycleCallback: Network %s %s %s" % (net.name(),
597 netEventToString(event),
598 netDetailToString(event, detail)))
600 ##########################################################################
601 # Storage pool events
602 ##########################################################################
603 def storageEventToString(event):
604 storageEventStrings = ( "Defined",
605 "Undefined",
606 "Started",
607 "Stopped",
608 "Created",
609 "Deleted",
611 return storageEventStrings[event]
613 def myStoragePoolEventLifecycleCallback(conn, pool, event, detail, opaque):
614 print("myStoragePoolEventLifecycleCallback: Storage pool %s %s %d" % (pool.name(),
615 storageEventToString(event),
616 detail))
618 def myStoragePoolEventRefreshCallback(conn, pool, opaque):
619 print("myStoragePoolEventRefreshCallback: Storage pool %s" % pool.name())
621 ##########################################################################
622 # Node device events
623 ##########################################################################
624 def nodeDeviceEventToString(event):
625 nodeDeviceEventStrings = ( "Created",
626 "Deleted",
628 return nodeDeviceEventStrings[event]
630 def myNodeDeviceEventLifecycleCallback(conn, dev, event, detail, opaque):
631 print("myNodeDeviceEventLifecycleCallback: Node device %s %s %d" % (dev.name(),
632 nodeDeviceEventToString(event),
633 detail))
635 def myNodeDeviceEventUpdateCallback(conn, dev, opaque):
636 print("myNodeDeviceEventUpdateCallback: Node device %s" % dev.name())
638 ##########################################################################
639 # Secret events
640 ##########################################################################
641 def secretEventToString(event):
642 secretEventStrings = ( "Defined",
643 "Undefined",
645 return secretEventStrings[event]
647 def mySecretEventLifecycleCallback(conn, secret, event, detail, opaque):
648 print("mySecretEventLifecycleCallback: Secret %s %s %d" % (secret.UUIDString(),
649 secretEventToString(event),
650 detail))
652 def mySecretEventValueChanged(conn, secret, opaque):
653 print("mySecretEventValueChanged: Secret %s" % secret.UUIDString())
655 ##########################################################################
656 # Set up and run the program
657 ##########################################################################
659 run = True
661 def myConnectionCloseCallback(conn, reason, opaque):
662 reasonStrings = (
663 "Error", "End-of-file", "Keepalive", "Client",
665 print("myConnectionCloseCallback: %s: %s" % (conn.getURI(), reasonStrings[reason]))
666 run = False
668 def usage():
669 print("usage: "+os.path.basename(sys.argv[0])+" [-hdl] [uri]")
670 print(" uri will default to qemu:///system")
671 print(" --help, -h Print this help message")
672 print(" --debug, -d Print debug output")
673 print(" --loop=TYPE, -l Choose event-loop-implementation (native, poll, asyncio)")
674 print(" --timeout=SECS Quit after SECS seconds running")
676 def main():
677 try:
678 opts, args = getopt.getopt(sys.argv[1:], "hdl:", ["help", "debug", "loop=", "timeout="])
679 except getopt.GetoptError as err:
680 # print help information and exit:
681 print(str(err)) # will print something like "option -a not recognized"
682 usage()
683 sys.exit(2)
684 timeout = None
685 for o, a in opts:
686 if o in ("-h", "--help"):
687 usage()
688 sys.exit()
689 if o in ("-d", "--debug"):
690 global do_debug
691 do_debug = True
692 if o in ("-l", "--loop"):
693 global event_impl
694 event_impl = a
695 if o in ("--timeout"):
696 timeout = int(a)
698 if len(args) >= 1:
699 uri = args[0]
700 else:
701 uri = "qemu:///system"
703 print("Using uri '%s' and event loop '%s'" % (uri, event_impl))
705 # Run a background thread with the event loop
706 if event_impl == "poll":
707 virEventLoopPollStart()
708 elif event_impl == "asyncio":
709 virEventLoopAIOStart()
710 else:
711 virEventLoopNativeStart()
713 vc = libvirt.openReadOnly(uri)
715 # Close connection on exit (to test cleanup paths)
716 old_exitfunc = getattr(sys, 'exitfunc', None)
717 def exit():
718 print("Closing " + vc.getURI())
719 vc.close()
720 if (old_exitfunc): old_exitfunc()
721 sys.exitfunc = exit
723 vc.registerCloseCallback(myConnectionCloseCallback, None)
725 #Add 2 lifecycle callbacks to prove this works with more than just one
726 vc.domainEventRegister(myDomainEventCallback1,None)
727 domcallbacks = []
728 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, myDomainEventCallback2, None))
729 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, myDomainEventRebootCallback, None))
730 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE, myDomainEventRTCChangeCallback, None))
731 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, myDomainEventWatchdogCallback, None))
732 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR, myDomainEventIOErrorCallback, None))
733 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, myDomainEventGraphicsCallback, None))
734 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON, myDomainEventIOErrorReasonCallback, None))
735 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, myDomainEventControlErrorCallback, None))
736 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_BLOCK_JOB, myDomainEventBlockJobCallback, None))
737 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_DISK_CHANGE, myDomainEventDiskChangeCallback, None))
738 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_TRAY_CHANGE, myDomainEventTrayChangeCallback, None))
739 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_PMWAKEUP, myDomainEventPMWakeupCallback, None))
740 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_PMSUSPEND, myDomainEventPMSuspendCallback, None))
741 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, myDomainEventBalloonChangeCallback, None))
742 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_PMSUSPEND_DISK, myDomainEventPMSuspendDiskCallback, None))
743 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED, myDomainEventDeviceRemovedCallback, None))
744 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2, myDomainEventBlockJob2Callback, None))
745 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_TUNABLE, myDomainEventTunableCallback, None))
746 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE, myDomainEventAgentLifecycleCallback, None))
747 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_ADDED, myDomainEventDeviceAddedCallback, None))
748 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION, myDomainEventMigrationIteration, None))
749 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_JOB_COMPLETED, myDomainEventJobCompletedCallback, None))
750 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback, None))
751 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, myDomainEventMetadataChangeCallback, None))
752 domcallbacks.append(vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback, None))
754 netcallbacks = []
755 netcallbacks.append(vc.networkEventRegisterAny(None, libvirt.VIR_NETWORK_EVENT_ID_LIFECYCLE, myNetworkEventLifecycleCallback, None))
757 poolcallbacks = []
758 poolcallbacks.append(vc.storagePoolEventRegisterAny(None, libvirt.VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, myStoragePoolEventLifecycleCallback, None))
759 poolcallbacks.append(vc.storagePoolEventRegisterAny(None, libvirt.VIR_STORAGE_POOL_EVENT_ID_REFRESH, myStoragePoolEventRefreshCallback, None))
761 devcallbacks = []
762 devcallbacks.append(vc.nodeDeviceEventRegisterAny(None, libvirt.VIR_NODE_DEVICE_EVENT_ID_LIFECYCLE, myNodeDeviceEventLifecycleCallback, None))
763 devcallbacks.append(vc.nodeDeviceEventRegisterAny(None, libvirt.VIR_NODE_DEVICE_EVENT_ID_UPDATE, myNodeDeviceEventUpdateCallback, None))
765 seccallbacks = []
766 seccallbacks.append(vc.secretEventRegisterAny(None, libvirt.VIR_SECRET_EVENT_ID_LIFECYCLE, mySecretEventLifecycleCallback, None))
767 seccallbacks.append(vc.secretEventRegisterAny(None, libvirt.VIR_SECRET_EVENT_ID_VALUE_CHANGED, mySecretEventValueChanged, None))
769 vc.setKeepAlive(5, 3)
771 # The rest of your app would go here normally, but for sake
772 # of demo we'll just go to sleep. The other option is to
773 # run the event loop in your main thread if your app is
774 # totally event based.
775 count = 0
776 while run and (timeout is None or count < timeout):
777 count = count + 1
778 time.sleep(1)
780 vc.domainEventDeregister(myDomainEventCallback1)
782 for id in seccallbacks:
783 vc.secretEventDeregisterAny(id)
784 for id in devcallbacks:
785 vc.nodeDeviceEventDeregisterAny(id)
786 for id in poolcallbacks:
787 vc.storagePoolEventDeregisterAny(id)
788 for id in netcallbacks:
789 vc.networkEventDeregisterAny(id)
790 for id in domcallbacks:
791 vc.domainEventDeregisterAny(id)
793 vc.unregisterCloseCallback()
794 vc.close()
796 # Allow delayed event loop cleanup to run, just for sake of testing
797 time.sleep(2)
799 if __name__ == "__main__":
800 main()