def update_countdown(self): """Update the countdown label.""" self.timeleft -= 1 if self.timeleft >= 0: self.lbl_countdown.set_text( _("Benchmark finishing in %d seconds...") % self.timeleft) else: self.lbl_countdown.set_text( _("Some clients didn't respond in time!") + "\n" + _("Waiting for %d more seconds...") % (self.timeleft + 3)) # Always recall; the timeout will be cancelled in on_iperf_exit. return True
def run(self, client, execute): """Show the dialog, then hide it so that it may be reused.""" self.client = client inst = client[C_INSTANCE] handle = inst.hsystem or client[C_SESSION_HANDLE] self.lbl_type.set_text(inst.type) self.lbl_alias.set_text(inst.alias) self.lbl_hostname.set_text(inst.hostname) self.lbl_mac.set_text(inst.mac) self.lbl_ip.set_text(handle.split(':')[0]) if client[C_SESSION_HANDLE]: uname, realname = inst.users[client[C_SESSION_HANDLE]].values() if realname: user = '******'.format(uname, realname) else: user = uname else: user = '' self.lbl_user.set_text(user) self.lbl_cpu.set_text('') self.lbl_ram.set_text('') self.lbl_vga.set_text('') if handle: execute(handle, 'echo "$CPU"').addCallback( self.cb_set_text, self.lbl_cpu) execute(handle, 'echo "$RAM MiB"').addCallback( self.cb_set_text, self.lbl_ram) execute(handle, 'echo "$VGA"').addCallback( self.cb_set_text, self.lbl_vga) # TODO: consider new string formatting vs updating translations self.dialog.set_title(_('Properties of %s') % inst.get_name()) self.dialog.run() self.dialog.hide()
def on_imi_clients_remove_from_group_activate(self, _widget): """Handle imi_clients_remove_from_group.activate event.""" clients = self.get_selected_clients() group = self.get_selected_group()[1] if self.confirmation_dialog( _('Are you sure you want to remove the selected client(s)' ' from group "%s"?') % group.name): for client in clients: group.remove_client(client[C_INSTANCE]) self.fill_icon_view(self.get_selected_group()[1], True)
def warning_dialog(self, text): """Show a warning dialog.""" dlg = Gtk.MessageDialog(self.mainwin, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.CLOSE, text, title=_('Warning')) dlg.run() dlg.destroy()
def disconnected(self, _daemon): """Called from uiconnection->Daemon->connectionLost.""" self.mainwin.set_sensitive(False) # If the reactor is not running at this point it means that we were # closed normally. # noinspection PyUnresolvedReferences if not reactor.running: return self.save_settings() msg = _("Lost connection with the epoptes service.") msg += "\n\n" + \ _("Make sure the service is running and then restart epoptes.") dlg = Gtk.MessageDialog(type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, message_format=msg) dlg.set_title(_('Service connection error')) dlg.run() dlg.destroy() # noinspection PyUnresolvedReferences reactor.stop()
def on_btn_group_remove_clicked(self, _widget): """Handle btn_group_remove.clicked event.""" group_iter = self.get_selected_group()[0] group = self.gstore[group_iter][G_INSTANCE] if self.confirmation_dialog( _('Are you sure you want to remove group "%s"?') % group.name): path = self.gstore.get_path(group_iter)[0] self.gstore.remove(group_iter) menuitem = self.mnu_add_to_group.get_children()[path - 1] self.mnu_add_to_group.remove(menuitem)
def confirmation_dialog(self, text): """Show a Yes/No dialog, returning True/False accordingly.""" dlg = Gtk.MessageDialog(self.mainwin, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, text, title=_('Confirm action')) resp = dlg.run() dlg.destroy() return resp == Gtk.ResponseType.YES
def on_btn_group_add_clicked(self, _widget): """Handle btn_group_add.clicked event.""" new_group = structs.Group(_('New group'), {}) itr = self.gstore.append([new_group.name, new_group, True]) # Edit the name of the newly created group self.trv_groups.set_cursor(self.gstore.get_path(itr), self.get('tvc_group'), True) menuitem = Gtk.MenuItem(new_group.name) menuitem.show() menuitem.connect('activate', self.on_imi_clients_add_to_group_activate, new_group) self.mnu_add_to_group.append(menuitem)
def __init__(self, summary, icon): """Initialize Notify and local variables.""" if not NotifyQueue.initialized: NotifyQueue.initialized = True if not Notify.init("Epoptes"): raise ImportError(_('Could not initialize notifications!')) self.summary = summary self.icon = icon self.items = [] # The heading of the last item enqueued self.last_heading = '' self.last_time = time.time() self.notification = None
def run(self, clients): """Show the dialog, then hide it so that it may be reused.""" self.clients = {} # This can happen on an empty group. # Btw, "if not clients" is wrong as it's a Gtk.ListStore, not a dict. # pylint: disable=len-as-condition if len(clients) == 0: self.warning_message( _('There are no selected clients to run the benchmark on.')) return # Check if offline clients or clients with no root client are selected off = [] for client in clients: inst = client[C_INSTANCE] if inst.hsystem: self.clients[inst.hsystem.split(':')[0]] =\ (inst.hsystem, inst.get_name()) else: off.append(inst.get_name()) # Now self.clients is the list of clients that can run the benchmark if not self.clients: self.warning_message( _('All of the selected clients are either offline,' ' or do not have epoptes-client running as root.')) return if off: self.warning_message( _('The following clients will be excluded from the benchmark' ' because they are either offline, or do not have' ' epoptes-client running as root.') + '\n\n' + ', '.join(off)) self.box_seconds.set_visible(True) self.box_countdown.set_visible(False) self.btn_start.set_visible(True) self.btn_stop.set_visible(False) self.dlg_benchmark.run()
def on_iperf_exit(self, out_data, err_data, reason): """The benchmark has finished, show the results dialog.""" GLib.source_remove(self.countdown_event) self.box_seconds.set_visible(True) self.box_countdown.set_visible(False) self.btn_start.set_visible(True) self.btn_stop.set_visible(False) if reason == "stopped": self.btn_stop.set_sensitive(True) return elif reason == "closed": return self.dlg_benchmark.hide() self.parse_iperf_output(out_data.decode("utf-8")) if not self.results: msg = _("Did not get measurements from any of the clients." " Check your network settings.") if err_data: msg += "\n\n" + err_data.decode("utf-8") self.error_message(msg) return # At this point we do have some results, so show dlg_results total_up = 0 total_down = 0 self.lss_results.clear() # List all the clients regardless of if we received measurements for client_ip in self.clients: client_name = self.clients[client_ip][1] if client_ip in self.results: upload, download = self.results[client_ip] else: upload, download = (0, 0) self.lss_results.append([client_name, upload, download]) total_up += upload total_down += download clients_n = len(self.clients) self.lbl_avg_up.set_text( humanize(total_up / clients_n, unit='bps')) self.lbl_total_up.set_text(humanize(total_up, unit='bps')) self.lbl_avg_down.set_text( humanize(total_down / clients_n, unit='bps')) self.lbl_total_down.set_text(humanize(total_down, unit='bps')) self.box_partial_results.set_visible( self.spawn_process.lines_count != 2*len(self.clients)) self.dlg_results.run()
def amp_client_disconnected(self, handle): """Called from uiconnection->Daemon->client_disconnected.""" def determine_offline(client_): """Helper function to call client.set_offline when appropriate.""" if client_.hsystem == '' and client_.users == {}: client_.set_offline() LOG.w("Disconnect from", handle) client = None for client in structs.clients: if client.hsystem == handle: if self.get_selected_group()[1].has_client(client) \ or self.is_default_group_selected(): self.notify_queue.enqueue(_("Shut down:"), client.get_name()) client.hsystem = '' determine_offline(client) break elif handle in client.users: if self.get_selected_group()[1].has_client(client) \ or self.is_default_group_selected(): self.notify_queue.enqueue( _("Disconnected:"), _("%(user)s from %(host)s") % { "user": client.users[handle]['uname'], "host": client.get_name() }) del client.users[handle] determine_offline(client) break else: client = None if client is not None: for row in self.cstore: if row[C_INSTANCE] is client: self.fill_icon_view(self.get_selected_group()[1], True) break
def on_btn_start_clicked(self, _widget): """Handle btn_start.clicked event.""" seconds = int(self.adj_seconds.get_value()) self.spawn_process.spawn('iperf -s -xS -yC'.split(), timeout=(seconds + 3), lines_max=2*len(self.clients)) for client in self.clients: handle = self.clients[client][0] # Half time for upload speed and half for download self.execute(handle, 'start_benchmark %d' % int(seconds/2)) self.timeleft = seconds self.box_seconds.set_visible(False) self.box_countdown.set_visible(True) self.btn_start.set_visible(False) self.btn_stop.set_visible(True) self.lbl_countdown.set_text(_("Benchmark finishing in %d seconds...") % self.timeleft) self.countdown_event = GLib.timeout_add(1000, self.update_countdown)
def on_icv_clients_selection_changed(self, _widget): """Handle icv_clients.selection_changed event.""" selected = self.get_selected_clients() single_client = False if len(selected) == 1: single_client = True self.get('imi_clients_information').set_sensitive(single_client) self.get('tlb_clients_information').set_sensitive(single_client) if selected: self.get('mni_add_to_group').set_sensitive(True) self.get('imi_clients_remove_from_group').set_sensitive( not self.is_default_group_selected()) else: self.get('mni_add_to_group').set_sensitive(False) self.get('imi_clients_remove_from_group').set_sensitive(False) if len(selected) > 1: self.get('lbl_status').set_text( _('%d clients selected' % len(selected))) else: self.get('lbl_status').set_text('')
def on_imi_restrictions_lock_screen_activate(self, _widget): """Handle imi_restrictions_lock_screen.activate event.""" msg = _("The screen is locked by a system administrator.") self.exec_on_selected_clients(['lock_screen', 0, msg])
def __init__(self): # Initialization of general-purpose variables self.about = None self.benchmark = None self.client_information = None self.current_macs = subprocess.Popen( [ 'sh', '-c', r"""ip -oneline -family inet link show | """ r"""sed -n '/.*ether[[:space:]]*\([[:xdigit:]:]*\).*/""" r"""{s//\1/;y/abcdef-/ABCDEF:/;p;}';""" r"""echo $LTSP_CLIENT_MAC""" ], stdout=subprocess.PIPE).communicate()[0].split() self.current_thumbshots = dict() self.daemon = None self.displayed_compatibility_warning = False self.exec_command = None self.imagetypes = { 'thin': GdkPixbuf.Pixbuf.new_from_file('images/thin.svg'), 'fat': GdkPixbuf.Pixbuf.new_from_file('images/fat.svg'), 'standalone': GdkPixbuf.Pixbuf.new_from_file('images/standalone.svg'), 'offline': GdkPixbuf.Pixbuf.new_from_file('images/offline.svg') } self.labels_order = (1, 0) self.notify_queue = NotifyQueue( 'Epoptes', '/usr/share/icons/hicolor/scalable/apps/epoptes.svg') self.send_message = None self.show_real_names = config.settings.getboolean('GUI', 'show_real_names', fallback=False) # Thumbshot width and height. Good width defaults are multiples of 16, # so that the height is an integer in both 16/9 and 4/3 aspect ratios. self.ts_width = config.settings.getint('GUI', 'thumbshots_width', fallback=128) self.ts_height = int(self.ts_width / (4 / 3)) self.uid = os.getuid() self.vncserver = None self.vncserver_port = None self.vncserver_pwd = None self.vncviewer = None self.vncviewer_port = None self.builder = Gtk.Builder() self.builder.add_from_file('epoptes.ui') self.get = self.builder.get_object # Hide the remote assistance menuitem if epoptes-client isn't installed if not os.path.isfile( '/usr/share/epoptes-client/remote_assistance.py'): self.get('mi_remote_assistance').set_property('visible', False) self.get('smi_help_remote_support').set_property('visible', False) self.mnu_add_to_group = self.get('mnu_add_to_group') self.mni_add_to_group = self.get('mni_add_to_group') self.gstore = Gtk.ListStore(str, object, bool) self.trv_groups = self.get('trv_groups') self.trv_groups.set_model(self.gstore) self.mainwin = self.get('wnd_main') self.cstore = Gtk.ListStore(str, GdkPixbuf.Pixbuf, object, str) self.icv_clients = self.get('icv_clients') self.set_labels_order(1, 0, None) self.icv_clients.set_model(self.cstore) self.icv_clients.set_pixbuf_column(C_PIXBUF) self.icv_clients.set_text_column(C_LABEL) self.cstore.set_sort_column_id(C_LABEL, Gtk.SortType.ASCENDING) self.on_icv_clients_selection_changed(None) self.icv_clients.enable_model_drag_source( Gdk.ModifierType.BUTTON1_MASK, [Gtk.TargetEntry.new("add", Gtk.TargetFlags.SAME_APP, 0)], Gdk.DragAction.COPY) self.trv_groups.enable_model_drag_dest( [("add", Gtk.TargetFlags.SAME_APP, 0)], Gdk.DragAction.COPY) self.default_group = structs.Group( '<b>' + _('Detected clients') + '</b>', {}) default_iter = self.gstore.append( [self.default_group.name, self.default_group, False]) self.default_group_ref = Gtk.TreeRowReference.new( self.gstore, self.gstore.get_path(default_iter)) # Connect glade handlers with the callback functions self.builder.connect_signals(self) self.trv_groups.get_selection().select_path( self.default_group_ref.get_path()) self.get('adj_icon_size').set_value(self.ts_width) _saved_clients, groups = config.read_groups( config.expand_filename('groups.json')) if groups: self.mni_add_to_group.set_sensitive(True) for group in groups: self.gstore.append([group.name, group, True]) mitem = Gtk.MenuItem(label=group.name) mitem.show() mitem.connect('activate', self.on_imi_clients_add_to_group_activate, group) self.mnu_add_to_group.append(mitem) self.fill_icon_view(self.get_selected_group()[1]) self.trv_groups.get_selection().select_path( config.settings.getint('GUI', 'selected_group', fallback=0)) mitem = self.get( config.settings.get('GUI', 'label', fallback='rmi_labels_host_user')) if not mitem: mitem = self.get('rmi_labels_host_user') mitem.set_active(True) self.get('cmi_show_real_names').set_active(self.show_real_names) self.mainwin.set_sensitive(False)
def on_imi_session_shutdown_activate(self, _widget): """Handle imi_session_shutdown.activate event.""" self.exec_on_selected_clients( ["shutdown"], warn=_('Are you sure you want to shutdown all the computers?'))
def on_imi_session_reboot_activate(self, _widget): """Handle imi_session_reboot.activate event.""" self.exec_on_selected_clients( ["reboot"], warn=_('Are you sure you want to reboot all the computers?'))
def warning_message(self, msg): """Show a warning dialog.""" self.dlg_message.set_property("message-type", Gtk.MessageType.WARNING) self.dlg_message.set_title(_("Warning")) self.dlg_message.set_markup(msg) self.dlg_message.run()
LOG.e(exc) # The system settings are shared with epoptes-client, that's why the caps. system = read_shell_file('/etc/default/epoptes') # TODO: check if the types, e.g. PORT=int, may cause problems. system.setdefault('PORT', 789) system.setdefault('SOCKET_GROUP', 'epoptes') system.setdefault('DIR', '/run/epoptes') # Allow running unencrypted, for clients with very low RAM. try: if os.path.getsize('/etc/epoptes/server.crt') == 0: system.setdefault('ENCRYPTION', False) except (IOError, OSError) as ex: LOG.e(ex) finally: system.setdefault('ENCRYPTION', True) settings = read_ini_file(expand_filename('settings')) if not settings.has_section('GUI'): settings.add_section('GUI') if not settings.has_option('GUI', 'messages_default_title'): settings.set('GUI', 'messages_default_title', _('Message from administrator')) if not settings.has_option('GUI', 'messages_use_markup'): settings.set('GUI', 'messages_use_markup', 'False') if not settings.has_option('GUI', 'grabkbdptr'): settings.set('GUI', 'grabkbdptr', 'False') history = read_plain_file(expand_filename('history'))
def error_message(self, msg): """Show an error dialog.""" self.dlg_message.set_property("message-type", Gtk.MessageType.ERROR) self.dlg_message.set_title(_("Error")) self.dlg_message.set_markup(msg) self.dlg_message.run()
def add_client(self, handle, reply, already=False): """Callback after running `info` on a client.""" # already is True if the client was started before epoptes LOG.w("add_client's been called for", handle) try: info = {} for line in reply.strip().split('\n'): key, value = line.split('=', 1) info[key.strip()] = value.strip() user, host, _ip, mac, type_, uid, version, name = \ info['user'], info['hostname'], info['ip'], info['mac'], \ info['type'], int(info['uid']), info['version'], info['name'] except (KeyError, ValueError) as exc: LOG.e(" Can't extract client information, won't add this client", exc) return False # Check if the incoming client is the same with the computer in which # epoptes is running, so we don't add it to the list. if (mac in self.current_macs) and ((uid == self.uid) or (uid == 0)): LOG.w(" Won't add this client to my lists") return False # Compatibility check if LooseVersion(version) < LooseVersion(COMPATIBILITY_VERSION): self.daemon.command(handle, "die 'Incompatible Epoptes server version!'") if not self.displayed_compatibility_warning: self.displayed_compatibility_warning = True self.warning_dialog( _("""A connection attempt was made by a client with""" """ version %s, which is incompatible with the current""" """ epoptes version.\n\nYou need to update your clients""" """ to the latest epoptes-client version.""") % version) return False sel_group = self.get_selected_group()[1] client = None for inst in structs.clients: # Find if the new handle is a known client if mac == inst.mac: client = inst LOG.w(' Old client: ', end='') break if client is None: LOG.w(' New client: ', end='') client = structs.Client(mac=mac) LOG.w('hostname=%s, type=%s, uid=%s, user=%s' % (host, type_, uid, user)) # Update/fill the client information client.type, client.hostname = type_, host if uid == 0: # This is a root epoptes-client client.hsystem = handle else: # This is a user epoptes-client client.add_user(user, name, handle) if not already and (sel_group.has_client(client) or self.is_default_group_selected()): self.notify_queue.enqueue( _("Connected:"), _("%(user)s on %(host)s") % { "user": user, "host": host }) if sel_group.has_client(client) or self.is_default_group_selected(): self.fill_icon_view(sel_group, True) return True
def on_imi_session_logout_activate(self, _widget): """Handle imi_session_logout.activate event.""" self.exec_on_selected_clients( ['logout'], mode=EM_SESSION, warn=_('Are you sure you want to log off all the users?'))