#!/usr/bin/env python import gtk import sys import os import signal import string from socket import socket, AF_UNIX, SOCK_DGRAM from time import time, ctime, sleep from threading import Thread, Event, Lock import threading import select import re class Application: def __init__(self, title, socket_file, arand_pid_file, arand_startup_script): self.title = title self.socket_file = socket_file self.arand_pid_file = arand_pid_file self.arand_startup_script = arand_startup_script # create a DeviceDictionary to keep track of the devices we come into # contact with self.arand_routing_table = ArandRoutingTable() #create an ArandPidWatcher to watch the pid file self.apw = ArandPidWatcher(5, self.arand_pid_file) # create a ArandListener to listen for arand's status information self.al = ArandListener(self.socket_file) # create the main view of the application self.main_view = MainView(self.title, 240, 320) def to_console(self, message): if self.console_debug_msgs: print message class MainView: def __init__(self, title, width=240, height=320): # create the window self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL) # set the window size (width, height) self.window.set_default_size(width, height) # set the border width self.window.set_border_width(5) # set the title bar text self.window.set_title(title) # connect the delete_event signal to the main window. self.window.connect("delete_event", self.delete_event) self.notebook = gtk.GtkNotebook() self.notebook.set_tab_pos(gtk.POS_TOP) table = gtk.GtkTable(3,6,gtk.FALSE) self.window.add(table) table.attach(self.notebook, 0,6,0,1) self.notebook.show_tabs = gtk.TRUE self.notebook.show() ## Page 1 -- Routing Table buffer_label = "Routing Table" self.frame1 = gtk.GtkFrame("Routing Table") self.frame1.set_border_width(3) self.frame1.set_usize(100, 75) self.frame1.show() # make a scrolled window to put our list in self.scrolled_window = gtk.GtkScrolledWindow() self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) # add the scrolled window to the frame self.frame1.add(self.scrolled_window) # show the scrolled window self.scrolled_window.show() self.routing_table_list = RoutingTableList(4, [ "Node", "Next Hop", "Lifetime", "Status" ]) self.scrolled_window.add_with_viewport(self.routing_table_list) self.routing_table_list.show() self.tab1label = gtk.GtkLabel(buffer_label) self.notebook.append_page(self.frame1, self.tab1label) ## ## Page 2 -- Control buffer_label = "Control" self.frame2 = gtk.GtkFrame("Control") self.frame2.set_border_width(3) self.frame2.set_usize(100, 75) self.frame2.show() self.control_vbox = gtk.GtkVBox(gtk.FALSE, 5) self.control_button_hbox = gtk.GtkHBox(gtk.FALSE, 5) self.control_label = gtk.GtkLabel("") self.control_start_button = gtk.GtkToggleButton("Run arand") self.control_start_button.connect("toggled", control_button_callback, None) self.control_vbox.pack_start(self.control_label, gtk.TRUE, gtk.FALSE, 0) self.control_button_hbox.pack_start(self.control_start_button, gtk.TRUE, gtk.FALSE, 0) self.control_vbox.pack_start(self.control_button_hbox, gtk.TRUE, gtk.FALSE, 0) self.control_label.show() self.control_start_button.show() self.control_button_hbox.show() self.control_vbox.show() self.frame2.add(self.control_vbox) self.tab2label = gtk.GtkLabel(buffer_label) self.notebook.append_page(self.frame2, self.tab2label) # show the table table.show() # show the window self.window.show() def delete_event(self, widget, event, data=None): gtk.mainquit() return gtk.FALSE class RoutingTableList(gtk.GtkCList): def __init__(self, cols, titles): gtk.GtkCList.__init__(self, cols, titles) self.connect("select_row", self.selection_made) def selection_made(self, clist, row, column, event, data=None): # Get the text that is stored in the selected row and column # which was clicked in. We will receive it as a pointer in the # argument text. #text = clist.get_text(row, column) text = clist.get_text(row, 0) app.to_console("selected %s\n" % text) return class ArandPidWatcher(Thread): """Watches the /var/run/arand.pid file to determine if arand is started or stopped""" def __init__(self, sleep_interval, pid_file): Thread.__init__(self) self.sleep_interval = sleep_interval self.pid_file = pid_file self.finished = threading.Event() self.finished.set() def shutdown(self): """Shutdown thread""" self.finished.clear() def run(self): while self.finished.isSet(): label="Status: arand stopped" toggle_button_state = gtk.FALSE # get the pid try: pid = open(self.pid_file).readline() except: pass else: if pid: label = "Status: arand started pid = " + pid toggle_button_state = gtk.TRUE gtk.threads_enter() app.main_view.control_label.set_text(label) app.main_view.control_start_button.set_active(toggle_button_state) gtk.threads_leave() sleep(self.sleep_interval); class ArandListener(Thread): """Listens for status messages from arand""" def __init__(self, socket_file): Thread.__init__(self) self.socket_file = socket_file try: os.unlink(self.socket_file) except: pass self.receive_socket = socket(AF_UNIX, SOCK_DGRAM) self.receive_socket.setblocking(0) self.receive_socket.bind(self.socket_file) self.finished = threading.Event() self.finished.set() self.packets_received = 0 def shutdown(self): """Shutdown the thread""" self.finished.clear() def run(self): while self.finished.isSet(): ready = select.select([self.receive_socket],[], [], 0) if self.receive_socket in ready[0]: data, addr = self.receive_socket.recvfrom(1500) # increment our packet counter self.packets_received = self.packets_received + 1 app.to_console("Received packet [" + data + "] from " + `addr`) # create a regular expression to search through the data we recieve # the token before the first comma will tell us what type of packet it is-- # a heartbeat or a message result = string.split(data, "\n") if result: for entry in result: if (entry == ''): continue result2 = string.split(entry, ":") if len(result2) != 5: app.to_console("wrong number of fields in status message") continue asm = ArandStatusMessage(result2[0], result2[1], result2[2], result2[3], result2[4]) app.arand_routing_table.update(asm.node, asm) app.arand_routing_table.routing_table_lock.acquire() gtk.threads_enter() app.main_view.routing_table_list.clear() app.main_view.routing_table_list.freeze() for node in app.arand_routing_table.routing_table.values(): app.main_view.routing_table_list.append([ "%s" % node.node, "%s" % node.next_hop, "%s" % node.lifetime, "%s" % node.routing_flags ]) app.main_view.routing_table_list.thaw() app.arand_routing_table.routing_table_lock.release() gtk.threads_leave() class ArandRoutingTable: """Manage device dictionary""" def __init__(self): self.routing_table = {} self.routing_table_lock = Lock() def update(self, node, asm): self.routing_table_lock.acquire() self.routing_table[node] = asm self.routing_table_lock.release() class ArandStatusMessage: """Class describing an arand status message""" def __init__(self, node, next_hop, interface, lifetime, routing_flags): self.node = node self.next_hop = next_hop self.lifetime = lifetime self.interface = interface self.routing_flags = routing_flags def control_button_callback(widget, data): if widget.active: if not os.path.exists(app.arand_pid_file): if os.fork() == 0: os.execlp('sh', 'sh', app.arand_startup_script) else: pass else: if os.path.exists(app.arand_pid_file): try: pid = int(open(app.arand_pid_file).readline()) os.kill(pid, signal.SIGHUP) except: app.to_console("error reading pid from " + app.arand_pid_file) else: app.to_console("pid file " + app.arand_pid_file + " does not exist") def main(): """Startup function for the program. It parses any command line options, creates an application object, and starts up our network threads.""" # parse command line arguments here # create application object create_global_app(Application("arand view", "/tmp/arand-socket", "/var/run/arand.pid", "/usr/local/bin/arand-run.sh")) app.console_debug_msgs = 0 # start up the network threads app.al.start() app.apw.start() # start the GTK main loop to process GUI events gtk.mainloop() # control returns here when the window is closed app.apw.shutdown() app.al.shutdown() def create_global_app(a): """"Helper function to move the application instance into a global namespace.""" global app app = a if __name__ == "__main__": main()