5 ##############################################################################
6 # Start off by implementing a general purpose event loop for anyone's use
7 ##############################################################################
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
22 # When Python >= 3.4, it can optionally use an impl based on the
25 # Finally, it can also use the libvirt native event loop impl
27 # This setting thus allows 'poll', 'native' or 'asyncio' as valid
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
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
48 class virEventLoopPollHandle
:
49 def __init__(self
, handle
, fd
, events
, cb
, opaque
):
65 def set_events(self
, events
):
68 def dispatch(self
, events
):
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
):
79 self
.interval
= interval
87 def get_interval(self
):
90 def set_interval(self
, interval
):
91 self
.interval
= interval
93 def get_last_fired(self
):
96 def set_last_fired(self
, now
):
105 self
.poll
= select
.poll()
106 self
.pipetrick
= os
.pipe()
107 self
.pendingWakeup
= False
108 self
.runningPoll
= False
109 self
.nextHandleID
= 1
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
136 def next_timeout(self
):
138 for t
in self
.timers
:
139 last
= t
.get_last_fired()
140 interval
= t
.get_interval()
143 if next
== 0 or (last
+ interval
) < next
:
144 next
= last
+ interval
148 # Lookup a virEventLoopPollHandle object based on file descriptor
149 def get_handle_by_fd(self
, fd
):
150 for h
in self
.handles
:
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
:
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.
184 self
.runningPoll
= True
186 for opaque
in self
.cleanup
:
187 libvirt
.virEventInvokeFreeCallback(opaque
)
191 next
= self
.next_timeout()
192 debug("Next timeout due at %d" % next
)
194 now
= int(time
.time() * 1000)
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)
213 h
= self
.get_handle_by_fd(fd
)
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()
224 want
= t
.get_last_fired() + interval
225 # Deduct 20ms, since scheduler timeslice
226 # means we could be ever so slightly early
228 debug("Dispatch timer %d now %s want %s" % (t
.get_id(), str(now
), str(want
)))
229 t
.set_last_fired(now
)
232 except (os
.error
, select
.error
) as e
:
233 if e
.args
[0] != errno
.EINTR
:
236 self
.runningPoll
= False
239 # Actually run the event loop forever
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
))
265 debug("Add handle %d fd %d events %d" % (handleID
, fd
, events
))
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
)
282 debug("Add timer %d interval %d" % (timerID
, interval
))
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
)
291 self
.poll
.unregister(h
.get_fd())
292 self
.poll
.register(h
.get_fd(), self
.events_to_poll(events
))
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
)
304 debug("Update timer %d interval %d" % (timerID
, interval
))
307 # Stop monitoring for events on the file handle
308 def remove_handle(self
, handleID
):
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
)
317 self
.handles
= handles
320 # Stop firing the periodic timer
321 def remove_timer(self
, timerID
):
323 for h
in self
.timers
:
324 if h
.get_id() != timerID
:
327 debug("Remove timer %d" % timerID
)
328 self
.cleanup
.append(h
.opaque
)
332 # Convert from libvirt event constants, to poll() events constants
333 def events_to_poll(self
, events
):
335 if events
& libvirt
.VIR_EVENT_HANDLE_READABLE
:
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
345 # Convert from poll() event constants, to libvirt events constants
346 def events_from_poll(self
, events
):
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
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
):
384 return eventLoop
.add_handle(fd
, events
, cb
, opaque
)
386 def virEventUpdateHandleImpl(handleID
, events
):
388 return eventLoop
.update_handle(handleID
, events
)
390 def virEventRemoveHandleImpl(handleID
):
392 return eventLoop
.remove_handle(handleID
)
394 def virEventAddTimerImpl(interval
, cb
, opaque
):
396 return eventLoop
.add_timer(interval
, cb
, opaque
)
398 def virEventUpdateTimerImpl(timerID
, interval
):
400 return eventLoop
.update_timer(timerID
, interval
)
402 def virEventRemoveTimerImpl(timerID
):
404 return eventLoop
.remove_timer(timerID
)
406 # This tells libvirt what event loop implementation it
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():
421 def virEventLoopAIORun(loop
):
423 asyncio
.set_event_loop(loop
)
426 def virEventLoopNativeRun():
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
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",
470 return domEventStrings
[event
]
472 def domDetailToString(event
, detail
):
474 ( "Added", "Updated" ),
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" ),
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 ##########################################################################
577 ##########################################################################
578 def netEventToString(event
):
579 netEventStrings
= ( "Defined",
584 return netEventStrings
[event
]
586 def netDetailToString(event
, detail
):
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",
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
),
618 def myStoragePoolEventRefreshCallback(conn
, pool
, opaque
):
619 print("myStoragePoolEventRefreshCallback: Storage pool %s" % pool
.name())
621 ##########################################################################
623 ##########################################################################
624 def nodeDeviceEventToString(event
):
625 nodeDeviceEventStrings
= ( "Created",
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
),
635 def myNodeDeviceEventUpdateCallback(conn
, dev
, opaque
):
636 print("myNodeDeviceEventUpdateCallback: Node device %s" % dev
.name())
638 ##########################################################################
640 ##########################################################################
641 def secretEventToString(event
):
642 secretEventStrings
= ( "Defined",
645 return secretEventStrings
[event
]
647 def mySecretEventLifecycleCallback(conn
, secret
, event
, detail
, opaque
):
648 print("mySecretEventLifecycleCallback: Secret %s %s %d" % (secret
.UUIDString(),
649 secretEventToString(event
),
652 def mySecretEventValueChanged(conn
, secret
, opaque
):
653 print("mySecretEventValueChanged: Secret %s" % secret
.UUIDString())
655 ##########################################################################
656 # Set up and run the program
657 ##########################################################################
661 def myConnectionCloseCallback(conn
, reason
, opaque
):
663 "Error", "End-of-file", "Keepalive", "Client",
665 print("myConnectionCloseCallback: %s: %s" % (conn
.getURI(), reasonStrings
[reason
]))
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")
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"
686 if o
in ("-h", "--help"):
689 if o
in ("-d", "--debug"):
692 if o
in ("-l", "--loop"):
695 if o
in ("--timeout"):
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()
711 virEventLoopNativeStart()
713 vc
= libvirt
.openReadOnly(uri
)
715 # Close connection on exit (to test cleanup paths)
716 old_exitfunc
= getattr(sys
, 'exitfunc', None)
718 print("Closing " + vc
.getURI())
720 if (old_exitfunc
): old_exitfunc()
723 vc
.registerCloseCallback(myConnectionCloseCallback
, None)
725 #Add 2 lifecycle callbacks to prove this works with more than just one
726 vc
.domainEventRegister(myDomainEventCallback1
,None)
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))
755 netcallbacks
.append(vc
.networkEventRegisterAny(None, libvirt
.VIR_NETWORK_EVENT_ID_LIFECYCLE
, myNetworkEventLifecycleCallback
, None))
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))
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))
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.
776 while run
and (timeout
is None or count
< timeout
):
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()
796 # Allow delayed event loop cleanup to run, just for sake of testing
799 if __name__
== "__main__":