#!/usr/bin/env python # # -*- python -*- # # Copyright (C) 2006 Christopher R. Gabriel # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # import gtk,gobject import os,math,time from gettext import gettext as _ from octofuss.desktop import control as desktopcontrol from octofuss.utils import hostup from octofuss import art,config,log,firewall,utils,utils class Host: def __init__(self,name): self.name = name self.isup = hostup.host_is_up(name) # basic cluster definition class Cluster(list): def __init__(self): list.__init__(self) def add_host(self,host): if not self.has_host(host): self.append(host) def del_host(self,host): for hostname in self: if host == hostname.name: index = self.index(hostname) del(self[index]) def load_from_list(self,host_list): self.__add__(host_list) def has_host(self,host): if host in self: return True else: return False #cluster's cluster class Network(dict): def __init__(self): dict.__init__(self) self.conf = config self.loaded = False def load(self): """ """ if self.loaded is False: # load clusters from cssh's main cluster file try: self.load_from_file("/etc/clusters") except IOError: pass # load clusters from custom cluster file try: self.load_from_file(os.path.join(os.getenv("HOME"),".clusters")) except IOError: pass self.loaded = True def get_from_tuple(self,path): k = self.keys() #k.sort() if len(path) is 1: cluster = path[0] return (k[cluster],) if len(path) is 2: cluster = path[0] host = path[1] return (k[cluster],self[k[cluster]][host].name) def load_from_file(self,file): f = open(file).readlines() for cluster in f: d = cluster.split() if len(d) > 0: cname = d[0] del(d[0]) c = Cluster() for h in d: # avoid duplicates with host's name if h not in c: host = Host(h) c.append(host) self.__setitem__(cname,c) def load_from_data(self,data): for cluster in data: cname = cluster[0] c = Cluster() hosts = cluster[1].split() for host in hosts: if host not in c: h = Host(host) c.append(h) self.__setitem__(cname,c) def save(self): f = open(os.path.join(os.getenv("HOME"),".clusters"),"w") for c in self.keys(): f.write("%s\t\t" % c) for host in self[c]: f.write(" %s" % host.name) f.write("\n") f.close() def find_host(self,host): for i in self.keys(): if host in self[i]: return i ### UI STUFF class ClusterTreeModel(gtk.TreeStore): def __init__(self,data): gtk.TreeStore.__init__(self,gobject.TYPE_STRING) self.network = data self.network.load() clusters = self.network.keys() #clusters.sort() for cluster in clusters: iter = self.append(None) self.set(iter, 0, cluster) for host in self.network[cluster]: child_iter = self.append(iter) self.set(child_iter, 0, host.name) def add_cluster(self,cname): c = Cluster() self.network[cname] = c iter = self.append(None) self.set(iter,0,cname) def add_host(self,path,cluster,host): """ Add an host to cluster identified by cname """ iter = self.get_iter(path) child_iter = self.append(iter) self.set(child_iter,0,host) self.network[cluster].append(Host(host)) def has_host(self, clusterName, hostname): """ Returns true if cluster has hostname """ return self.network[cname].has_host(hostname) class HostWindow(gtk.HBox): def __init__(self): gtk.HBox.__init__(self) self.octofuss_name = _("Cluster/host management") self.hpaned = gtk.HPaned() self.hpaned.set_border_width(5) self.pack_start(self.hpaned, True, True) self.network = Network() self.selected_cluster = None #Check when cannot import some modules self.restartRequired = False self.model = ClusterTreeModel(self.network) #Host view self.view = gtk.IconView() #self.model = self.model.filter_new() self.cluster_tree = gtk.TreeView(self.model) self.cluster_tree.columns_autosize() self.cluster_tree.connect("button_press_event", self.item_event) cell = gtk.CellRendererText() column = gtk.TreeViewColumn(_("Clusters"), cell, text=0) column.set_clickable(True) column.connect("clicked",self.show_cluster_icon_list) self.cluster_tree.append_column(column) v = gtk.VBox() self.hpaned.pack1(v,resize=True, shrink=False) frame = gtk.Frame() frame.set_shadow_type(gtk.SHADOW_IN) v.pack_start(frame) sw = gtk.ScrolledWindow() sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) sw.add(self.cluster_tree) frame.add(sw) b = gtk.Button(stock='gtk-add') b.connect("clicked", self.on_cluster_add,sw ) v.pack_start(b,False,False, 10) bb = gtk.Button(stock='gtk-remove') bb.connect("clicked", self.on_cluster_del,self.cluster_tree ,sw) v.pack_start(bb,False,False, 10) self.app_container = gtk.Frame() self.app_container.set_shadow_type(gtk.SHADOW_IN) self.hpaned.add2(self.app_container) self.app_container.add(self.get_cluster_icon_list()) def change_app_content(self,obj): self.app_container.get_child().destroy() self.app_container.add(obj) obj.show_all() def get_possible_button_event(self, event): '''mouse or keyboard caused the event?''' if event.type == gtk.gdk.KEY_PRESS: return 0 # no event.button so pass 0 return event.button def on_cluster_add(self,button,sw): dialog = gtk.Dialog("Add new cluster", None, 0, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) dialog.vbox.pack_start(gtk.Label("Cluster name:")) entry = gtk.Entry() dialog.vbox.pack_start(entry) dialog.vbox.show_all() response = dialog.run() if response == gtk.RESPONSE_OK: cname = entry.get_text() if cname: if not self.network.has_key(cname): self.model.add_cluster(cname) self.network.save() self.model = ClusterTreeModel(self.network) self.cluster_tree.set_model(self.model) sw.show() dialog.destroy() def on_cluster_del(self,button,treeView,sw): """ Delete cluster """ model = treeView.get_model() treeSelection = treeView.get_selection() selection = treeSelection.get_selected() iter = selection[1] try: value = model.get_value(iter,0) # Delete cluster from network del(self.network[value]) # Save self.network.save() # Rebuild model self.model = ClusterTreeModel(self.network) self.cluster_tree.set_model(self.model) sw.show() except: dialog = gtk.MessageDialog(type=gtk.MESSAGE_WARNING, message_format="You must select a cluster!", buttons=gtk.BUTTONS_OK) dialog.show_all() response = dialog.run() dialog.destroy() return def on_host_select(self,icon_view, clusterPath): selected = icon_view.get_selected_items() if selected: self.show_context_menu_host(None, (clusterPath,selected[0][0])) def get_host_icon_list(self,cluster, clusterPath): hbox = gtk.HBox() swin = gtk.ScrolledWindow() swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.set_shadow_type(gtk.SHADOW_ETCHED_IN) hbox.pack_start(swin) self.view = gtk.IconView() self.view.set_text_column(0) self.view.set_pixbuf_column(1) self.view.set_selection_mode(gtk.SELECTION_SINGLE) self.view.set_size_request(200, 150) model = gtk.ListStore(str, gtk.gdk.Pixbuf) for host in cluster: logged_user = None if host.isup: pix = art.get_pixbuf_from_name("system-up") try: logged_user = desktopcontrol.logged_user(host.name) except: pass else: pix = art.get_pixbuf_from_name("system-down") if firewall.host_is_listed_no_wan(host.name): pix = art.get_blocked_pixbuf(pix) if logged_user: model.append([host.name+"\n"+logged_user,pix]) else: model.append([host.name,pix]) self.view.set_model(model) #view.connect('selection-changed', self.on_host_select, clusterPath) self.view.connect("button_press_event", self.host_item_event, cluster, clusterPath) self.view.unparent() swin.add(self.view) hbox.show_all() return hbox def show_cluster_icon_list(self,obj): s = self.get_cluster_icon_list() self.change_app_content(s) def show_host_icon_list(self,event,path): res = self.network.get_from_tuple(path) hostNumber = len(self.network[res[0]]) if len(res) is 1: #self.selected_cluster = path[0] s = self.get_host_icon_list(self.network[res[0]],path[0]) self.change_app_content(s) if hostNumber == 0: hboxEmpty = gtk.HBox() label = gtk.Label("No hosts in selected cluster!") hboxEmpty.pack_start(label) hboxEmpty.show_all() self.change_app_content(hboxEmpty) def on_cluster_select(self,icon_view,model=None): selected = icon_view.get_selected_items() self.selected_cluster = selected[0][0] if len(selected) == 0: return if len(selected) == 1: self.show_host_icon_list(None,selected[0]) def get_cluster_icon_list(self): hbox = gtk.HBox() swin = gtk.ScrolledWindow() swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.set_shadow_type(gtk.SHADOW_ETCHED_IN) hbox.pack_start(swin) view = gtk.IconView() view.set_text_column(0) view.set_pixbuf_column(1) view.set_selection_mode(gtk.SELECTION_SINGLE) view.set_size_request(200, 150) model = gtk.ListStore(str, gtk.gdk.Pixbuf) cList = self.network.keys() #cList.sort() for cluster in cList: pix = art.get_pixbuf_from_name("cluster-new") model.append([cluster,pix]) view.set_model(model) view.connect('selection-changed', self.on_cluster_select, model) swin.add(view) hbox.show_all() return hbox def remote_shell(self,obj,path): # check if we'got a cluster or a host from octofuss.sshterm import SshTerm if len(path) is 2: s = SshTerm(path[1]) self.change_app_content(s) def install_software(self,obj,path): try: if self.restartRequired: raise Exception("You need to check some problems!\nRestart needed.") from octofuss.packages import PackagesAdmin if len(path) is 1: s = PackagesAdmin(self.network[path[0]]) elif len(path) is 2: s = PackagesAdmin(Host(path[1])) self.change_app_content(s) except Exception, e: log.debug(str(e)) self.restartRequired = True self.__error_dialog(str(e)) def dialogYesNo(self, message): dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, message ) dialog.show() # Close dialog on user response resp = dialog.run() dialog.destroy() return resp def shutdown_now(self, obj, path, res): shutdownHostList = [] if len(res) > 1: if self.dialogYesNo("Are you sure to shutdown the host?") == gtk.RESPONSE_NO: return # add a single to host list to be shutted down shutdownHostList.append(res[1]) elif len(res) == 1: # add a list to host list to be shutted down if self.dialogYesNo("Are you sure to shutdown the cluster?") == gtk.RESPONSE_NO: return cluster = self.network[res[0]] for host in cluster: shutdownHostList.append(host.name) from octofuss.utils import remotecmd remotecmd.shutdown(shutdownHostList) def lock_desktop(self, obj, path, res): lockdesktoplist = [] if len(res) > 1: if self.dialogYesNo("Are you sure to lock desktop for this host?") == gtk.RESPONSE_NO: return # add a single to host list to be shutted down lockdesktoplist.append(res[1]) elif len(res) == 1: # add a list to host list to be shutted down if self.dialogYesNo("Are you sure to lock desktop to this cluster?") == gtk.RESPONSE_NO: return cluster = self.network[res[0]] for host in cluster: lockdesktoplist.append(host.name) for host in lockdesktoplist: desktopcontrol.screen_lock(host, "Lock from network administrator") def popup_message(self, obj, path, res): PopupMessageHostList = [] if len(res) > 1: # add a single to host list to be shutted down PopupMessageHostList.append(res[1]) elif len(res) == 1: cluster = self.network[res[0]] for host in cluster: PopupMessageHostList.append(host.name) desktopcontrol.PopupMessageWindow(PopupMessageHostList) def add_host(self, obj,path, res): """ Add host to cluster """ dialog = gtk.Dialog("Add new host", None, 0, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) dialog.vbox.pack_start(gtk.Label("Host name:")) entry = gtk.Entry() dialog.vbox.pack_start(entry) dialog.vbox.show_all() response = dialog.run() if response == gtk.RESPONSE_OK: hostname = entry.get_text() clustername = res[0] if not self.network[clustername].has_host(hostname): self.model.add_host(path,clustername,hostname) self.show_host_icon_list(obj,path) self.network.save() dialog.destroy() def remove_host(self, obj,path, res, treeView): """ Delete host from cluster """ cluster = res[0] host = res[1] log.debug("Removing host "+host+" in cluster "+cluster) self.network[cluster].del_host(host) self.network.save() # Update the model self.model = ClusterTreeModel(self.network) self.cluster_tree.set_model(self.model) self.cluster_tree.show() #TODO: update the icon view model self.show_host_icon_list(obj,(path[0],)) def show_context_menu(self,event,path): menu = gtk.Menu() res = self.network.get_from_tuple(path) if len(res) is 2: menufor = "%s: %s" % (res[0],res[1]) else: self.selected_cluster = res[0] menufor = res[0] item = gtk.MenuItem(menufor) item.set_sensitive(False) menu.append(item) # remote shell item = gtk.MenuItem(_("Remote shell")) item.connect("activate", self.remote_shell, res) menu.append(item) # install software item = gtk.MenuItem(_("Install software")) item.connect("activate", self.install_software,res) menu.append(item) # message item = gtk.MenuItem(_("Send message")) item.connect("activate", self.popup_message,path,res) menu.append(item) # separator item = gtk.SeparatorMenuItem() menu.append(item) # lockdown item = gtk.MenuItem(_("Lock desktop")) item.connect("activate", self.lock_desktop,path,res) menu.append(item) # shutdown pcs item = gtk.MenuItem(_("Shutdown now")) item.connect("activate", self.shutdown_now,path,res) menu.append(item) # separator item = gtk.SeparatorMenuItem() menu.append(item) # new host item = gtk.MenuItem(_("Add host")) item.connect("activate", self.add_host,path,res) menu.append(item) #event_button = self.get_possible_button_event(event) menu.attach_to_widget(self.cluster_tree, None) #menu.popup(None,None,None,3,0) menu.popup(None, None, None, 3,0) menu.show_all() def show_context_menu_host(self,event,path): menu = gtk.Menu() res = self.network.get_from_tuple(path) if len(res) is 2: menufor = "%s: %s" % (res[0],res[1]) else: self.selected_cluster = res[0] menufor = res[0] item = gtk.MenuItem(menufor) item.set_sensitive(False) menu.append(item) # separator item = gtk.SeparatorMenuItem() menu.append(item) ### HOST OPERATIONS # remote shell myhost = Host(res[1]) # remote shell item = gtk.MenuItem(_("Remote shell")) item.set_sensitive(myhost.isup) item.connect("activate", self.remote_shell, res) menu.append(item) # install software item = gtk.MenuItem(_("Install software")) item.connect("activate", self.install_software,res) menu.append(item) #Block internet if firewall.host_is_listed_no_wan(res[1]): #Host blocked item = gtk.MenuItem(_("Enable Internet")) item.connect("activate", self.enable_wan_services,res[1],path) menu.append(item) else: #Host no blocked item = gtk.MenuItem(_("Disable Internet")) item.connect("activate", self.disable_wan_services,res[1],path) menu.append(item) # remote desktop rdesktop_client = "vncviewer" self.command = utils.which(rdesktop_client) item = gtk.MenuItem(_("Show Desktop")) item.connect("activate", self.execute_command,rdesktop_client,res[1]) item.set_sensitive(False) if self.command and Host(res[1]).isup: item.set_sensitive(True) menu.append(item) # message item = gtk.MenuItem(_("Send message")) item.connect("activate", self.popup_message,path,res) if not Host(res[1]).isup: item.set_sensitive(False) menu.append(item) # separator item = gtk.SeparatorMenuItem() menu.append(item) # lockdown item = gtk.MenuItem(_("Lock desktop")) item.connect("activate", self.lock_desktop,path,res) if not Host(res[1]).isup: item.set_sensitive(False) menu.append(item) # shutdown host item = gtk.MenuItem(_("Shutdown now")) item.connect("activate", self.shutdown_now,path,res) if not Host(res[1]).isup: item.set_sensitive(False) menu.append(item) # separator item = gtk.SeparatorMenuItem() menu.append(item) # delete host item = gtk.MenuItem(_("Delete host")) item.connect("activate", self.remove_host,path,res, self.cluster_tree) menu.append(item) #event_button = self.get_possible_button_event(event) menu.attach_to_widget(self.cluster_tree, None) #menu.popup(None,None,None,3,0) menu.popup(None, None, None, 3,0) menu.show_all() def execute_command(self,a,command, host): """ Execute a command """ import os os.system(command + " " + host + "&") def enable_wan_services(self, a,host,path): """ Enable wan services """ if not firewall.host_remove_no_wan(host): log.debug("Problem enabling wan services for host "+host) else: #Update view self.show_host_icon_list(a,(path[0],)) def disable_wan_services(self,a,host,path): """ Disable wan services """ if not firewall.host_no_wan(host): log.debug("Problem disabling wan services for host "+host) else: #Update view self.show_host_icon_list(a,(path[0],)) def item_event(self,obj,event): try: path, column, x, y = self.cluster_tree.get_path_at_pos(int(event.x), int(event.y)) if len(path) == 1: if event.button == 1: self.show_host_icon_list(event,path) if event.button == 3: self.show_context_menu(event,path) else: if len(path) == 2: if event.button == 3: self.show_context_menu_host(event,path) except TypeError,e: log.debug(str(e)) except Exception, e: log.debug(str(e)) def host_item_event(self,obj,event, cluster, clusterPath): try: try: subpath = self.view.get_path_at_pos(int(event.x), int(event.y)) path = (clusterPath, subpath[0]) if event.button == 3: self.show_context_menu_host(event,path) except Exception, e: pass except TypeError,e: log.debug(str(e)) except Exception, e: log.debug(str(e)) def __error_dialog(self, error): dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, message_format=error, buttons=gtk.BUTTONS_OK) dialog.show_all() response = dialog.run() dialog.destroy() # routine test if __name__ == "__main__": w = gtk.Window() w.set_title("Cluster window test") w.connect("delete_event", gtk.main_quit) w.connect("destroy_event", gtk.main_quit) s = HostWindow() w.add(s) w.show_all() gtk.main()