examples: add a ton of comments to mitop.py.

pull/180/head
David Wilson 6 years ago
parent f440e88f2f
commit a68e833463

@ -10,15 +10,28 @@ import mitogen.utils
class Host(object):
"""
A target host from the perspective of the master process.
"""
#: String hostname.
name = None
#: mitogen.parent.Context used to call functions on the host.
context = None
#: mitogen.core.Receiver the target delivers state updates to.
recv = None
def __init__(self):
self.procs = {} #: pid -> Process()
#: Mapping of pid -> Process() for each process described
#: in the host's previous status update.
self.procs = {}
class Process(object):
"""
A single process running on a target host.
"""
host = None
user = None
pid = None
@ -30,14 +43,20 @@ class Process(object):
rss = None
@mitogen.core.takes_router
def remote_main(context_id, handle, delay, router):
context = mitogen.core.Context(router, context_id)
sender = mitogen.core.Sender(context, handle)
def child_main(sender, delay):
"""
Executed on the main thread of the Python interpreter running on the target
machine, using context.call() by the master. It simply sends the output of
the UNIX 'ps' command at regular intervals toward a Receiver on master.
:param mitogen.core.Sender sender:
The Sender to use for delivering our result. This could target
anywhere, but the sender supplied by the master simply causes results
to be delivered to the master's associated per-host Receiver.
"""
args = ['ps', '-axwwo', 'user,pid,ppid,pgid,%cpu,rss,command']
while True:
sender.put(subprocess.check_output(args))
sender.send(subprocess.check_output(args))
time.sleep(delay)
@ -124,7 +143,12 @@ class Painter(object):
self.stdscr.refresh()
def local_main(painter, router, select, delay):
def master_main(painter, router, select, delay):
"""
Loop until CTRL+C is pressed, waiting for the next result delivered by the
Select. Use parse_output() to turn that result ('ps' command output) into
rich data, and finally repaint the screen if the repaint delay has passed.
"""
next_paint = 0
while True:
msg = select.get()
@ -134,8 +158,13 @@ def local_main(painter, router, select, delay):
painter.paint()
def main(router, argv):
mitogen.utils.log_to_file()
@mitogen.main()
def main(router):
"""
Main program entry point. @mitogen.main() is just a helper to handle
reliable setup/destruction of Broker, Router and the logging package.
"""
argv = sys.argv[1:]
if not len(argv):
print 'mitop: Need a list of SSH hosts to connect to.'
sys.exit(1)
@ -144,36 +173,60 @@ def main(router, argv):
select = mitogen.master.Select(oneshot=False)
hosts = []
# For each hostname on the command line, create a Host instance, a Mitogen
# connection, a Receiver to accept messages from the host, and finally
# start child_main() on the host to pump messages into the receiver.
for hostname in argv:
print 'Starting on', hostname
host = Host()
host.name = hostname
if host.name == 'localhost':
host.context = router.local()
else:
host.context = router.ssh(hostname=host.name)
# A receiver wires up a handle (via Router.add_handler()) to an
# internal thread-safe queue object, which can be drained through calls
# to recv.get().
host.recv = mitogen.core.Receiver(router)
host.recv.host = host
# But we don't want to receive data from just one receiver, we want to
# receive data from many. In this case we can use a Select(). It knows
# how to efficiently sleep while waiting for the first message sent to
# many receivers.
select.add(host.recv)
call_recv = host.context.call_async(remote_main,
mitogen.context_id, host.recv.handle, delay)
# The inverse of a Receiver is a Sender. Unlike receivers, senders are
# serializable, so we can call the .to_sender() helper method to create
# one equivalent to our host's receiver, and pass it directly to the
# host as a function parameter.
sender = host.recv.to_sender()
# Finally invoke the function in the remote target. Since child_main()
# is an infinite loop, using .call() would block the parent, since
# child_main() never returns. Instead use .call_async(), which returns
# another Receiver. We also want to wait for results from receiver --
# even child_main() never returns, if there is an exception, it will be
# delivered instead.
call_recv = host.context.call_async(child_main, sender, delay)
call_recv.host = host
# Adding call_recv to the select will cause CallError to be thrown by
# .get() if startup in the context fails, halt local_main() and cause
# .get() if startup in the context fails, halt master_main() and cause
# the exception to be printed.
select.add(call_recv)
hosts.append(host)
# Painter just wraps up all the prehistory ncurses code and keeps it out of
# master_main().
painter = Painter(hosts)
try:
try:
local_main(painter, router, select, delay)
master_main(painter, router, select, delay)
except KeyboardInterrupt:
# Shut down gracefully when the user presses CTRL+C.
pass
finally:
painter.close()
if __name__ == '__main__':
mitogen.utils.run_with_router(main, sys.argv[1:])

Loading…
Cancel
Save