def add_new_tab(self, session_name, show_close_btn=True): """ Add a new tab in the tab widget :param session_name: name to be displayed :param show_close_btn: if true we add the close button :return: """ new_tab = QSSHSessionWidget(self) uuid = new_tab.uuid self.tabs.addTab(new_tab, session_name) if show_close_btn: kill_btn = QPushButton() kill_btn.setIcon(self.style().standardIcon( QStyle.SP_DialogCloseButton)) kill_btn.clicked.connect(lambda: self.on_close(uuid)) kill_btn.setToolTip('Close session') self.tabs.tabBar().setTabButton(self.tabs.count() - 1, QTabBar.RightSide, kill_btn) else: kill_btn = QPushButton() ico = QIcon() ico.addFile(resource_path('gui/icons/plus.png')) kill_btn.setIcon(ico) kill_btn.clicked.connect(self.on_new) kill_btn.setToolTip('New session') self.tabs.tabBar().setTabButton(self.tabs.count() - 1, QTabBar.RightSide, kill_btn) new_tab.logged_in.connect(self.on_login) new_tab.sessions_changed.connect(self.on_sessions_changed) logger.debug("Added new tab " + str(uuid))
def kill_display(self): """ Kill the display running on the server :return: """ current_status = self.status if self.status is Status.FINISHED: self.terminate.emit(self.display_id) return try: logger.debug("Killing remote display " + str(self.display_name)) self.status = Status.KILLING self.update_gui() self.kill_thread = KillThread(self.parent, self.session, self, current_status) self.kill_thread.finished.connect(self.on_killed) self.kill_thread.start() except: logger.error("Failed to start kill remote display thread " + str(self.display_name)) self.status = current_status self.update_gui()
def __init__(self): super().__init__() pack_info = rcm_utils.pack_info() title = "Remote Connection Manager - CINECA - v" + pack_info.rcmVersion self.setWindowTitle(title) width = 1000 height = 370 screen_width = QDesktopWidget().width() screen_height = QDesktopWidget().height() self.setGeometry((screen_width / 2) - (width / 2), (screen_height / 2) - (height / 2), width, height) self.setMinimumHeight(height) self.setMinimumWidth(width) self.build_menu() self.main_widget = MainWidget(self) self.setCentralWidget(self.main_widget) self.thread_pool = QThreadPool() logger.info("Welcome to RCM!") logger.debug("Multithreading with maximum %d threads" % self.thread_pool.maxThreadCount())
def update_gui(self): """ Update the status of the job running on the server in the gui and set the buttons enabled True/False accordingly :return: """ logger.debug("Updating display widget with status " + str(self.status)) if self.status is Status.NOTDEFINED: self.connect_btn.setEnabled(False) self.share_btn.setEnabled(False) self.kill_btn.setEnabled(True) self.time.setText('Not defined') self.resources_label.setText('Not defined') if self.status is Status.PENDING: self.connect_btn.setEnabled(False) self.share_btn.setEnabled(False) self.kill_btn.setEnabled(True) self.time.setText('Not defined') self.resources_label.setText('Not defined') if self.status is Status.RUNNING: self.connect_btn.setEnabled(True) self.share_btn.setEnabled(True) self.kill_btn.setEnabled(True) if self.session: timeleft = str(self.session.hash['timeleft']) self.time.setText(timeleft) try: strp_time = datetime.strptime(timeleft, "%H:%M:%S") self.timeleft = timedelta(hours=strp_time.hour, minutes=strp_time.minute, seconds=strp_time.second) except: self.timeleft = None self.resources_label.setText(str(self.session.hash['node'])) else: self.time.setText('Not defined') self.resources_label.setText('Not defined') if self.status is Status.KILLING: self.connect_btn.setEnabled(False) self.share_btn.setEnabled(False) self.kill_btn.setEnabled(False) if self.status is Status.FINISHED: self.connect_btn.setEnabled(False) self.share_btn.setEnabled(False) self.kill_btn.setEnabled(True) self.status_label.setText(str(self.status)) self.status_label.update()
def reload(self): logger.debug("Reloading...") # Show the reload widget self.containerLoginWidget.hide() self.containerSessionWidget.hide() self.containerWaitingWidget.hide() self.containerReloadWidget.show() self.reload_thread = ReloadThread(self) self.reload_thread.finished.connect(self.on_reloaded) self.reload_thread.start()
def remove_display(self, id): """ Remove the display widget from the tab :param id: display id name :return: """ # first we hide the display logger.debug("Hiding display " + str(id)) self.displays[id].hide() # then we remove it from the layout and from the dictionary self.rows_ver_layout.removeWidget(self.displays[id]) self.displays[id].setParent(None) del self.displays[id] logger.info("Removed display " + str(id))
def on_reloaded(self): if self.display_sessions: # kill not existing sessions for display_id in list(self.displays.keys()): missing = True for session in self.display_sessions.get_sessions(): if str(display_id) == str(session.hash['session name']): missing = False break if missing: self.remove_display(display_id) # update or create from scratch new sessions for session in self.display_sessions.get_sessions(): display_id = str(session.hash['session name']) display_state = str(session.hash['state']) display_node = str(session.hash['node']) display_name = display_id.split('-')[0] display_timeleft = str(session.hash['timeleft']) if display_id in self.displays.keys(): logger.debug("Display " + display_id + " already exists") self.displays[display_id].session = session if self.displays[display_id].status != Status( display_state): self.displays[display_id].status = Status( display_state) self.displays[display_id].update_gui() else: display_widget = QDisplaySessionWidget( parent=self, display_id=display_id, display_name=display_name, session=session, status=Status(display_state), resources=display_node, timeleft=display_timeleft) self.rows_ver_layout.addWidget(display_widget) self.displays[display_id] = display_widget logger.debug("Created display " + display_id) # Show the reload widget self.containerLoginWidget.hide() self.containerSessionWidget.show() self.containerWaitingWidget.hide() self.containerReloadWidget.hide()
def run(self): try: logger.debug("Worker for display " + str(self.display_id) + " started") self.signals.status.emit(Status.PENDING) display_session = self.remote_connection_manager.newconn( queue=self.session_queue, geometry=self.display_size, sessionname=self.display_id, vnc_id=self.session_vnc) self.signals.status.emit(Status.RUNNING) self.display_widget.session = display_session self.remote_connection_manager.vncsession( display_session, gui_cmd=self.display_widget.enable_connect_button) logger.debug("Worker for display " + str(self.display_id) + " finished") except Exception as e: logger.error(e)
def run(self): try: logger.debug("Worker for display " + str(self.display_id) + " started") self.signals.status.emit(Status.PENDING) api_version = self.remote_connection_manager.api_version() if api_version >= "1.0.0": display_session = self.remote_connection_manager.new( queue="dummy_queue", geometry="dummy_display_size", sessionname=self.display_id, vnc_id="dummy_vnc", choices=self.display_dlg.choices) else: display_session = self.remote_connection_manager.new( queue=self.display_dlg.session_queue, geometry=self.display_dlg.display_size, sessionname=self.display_id, vnc_id=self.display_dlg.session_vnc, choices=None) self.signals.status.emit(Status.RUNNING) self.display_widget.session = display_session self.remote_connection_manager.submit( display_session, gui_cmd=self.display_widget.enable_connect_button) logger.debug("Worker for display " + str(self.display_id) + " finished") except Exception as e: self.signals.status.emit(Status.FINISHED) logger.error("Exception: " + str(e) + " - " + str(traceback.format_exc()))
def share_display(self): try: logger.info("Sharing display " + str(self.display_name)) filename_suggested = self.session.hash['session name'].replace( ' ', '_') + '.vnc' options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, _ = QFileDialog.getSaveFileName( self, "Share display " + str(self.display_name), filename_suggested, " Vnc Files (*.vnc);;All Files (*)", options=options) if filename: with open(filename, 'w') as out_file: out_file.write("[Connection]\n") if self.session.hash['tunnel'] == 'y': # rcm_tunnel is a key word to know that I need to tunnel across that node out_file.write("rcm_tunnel={0}\n".format( self.session.hash['nodelogin'])) out_file.write("host={0}\n".format( self.session.hash['node'])) else: out_file.write("host={0}\n".format( self.session.hash['nodelogin'])) try: port = int(self.session.hash['port']) except Exception as e: logger.debug( str(e) + " - " + str(traceback.format_exc())) port = 5900 + int(self.session.hash['display']) out_file.write("port={0}\n".format(port)) out_file.write("password={0}\n".format( self.session.hash['vncpassword'])) except Exception as e: logger.debug(str(e) + " - " + str(traceback.format_exc())) logger.debug(str(self.session.hash)) logger.error("Failed to share display " + str(self.display_name))
def kill_reload_thread(self): if self.reload_thread: if not self.reload_thread.isFinished(): logger.debug("killing reload thread") self.reload_thread.terminate()
def kill_login_thread(self): if self.login_thread: if not self.login_thread.isFinished(): logger.debug("killing login thread") self.login_thread.terminate()
def update_executable(self): # update the executable only if we are running in a bundle if not pyinstaller_utils.is_bundled(): return logger.info("Checking if a new client version is available...") current_exe_checksum = rcm_utils.compute_checksum(sys.executable) logger.debug("Current client checksum: " + str(current_exe_checksum)) last_exe_checksum = self.platform_config.get_version()[0] last_exe_url = self.platform_config.get_version()[1] logger.debug("New client checksum: " + str(last_exe_checksum)) if current_exe_checksum != last_exe_checksum: question_title = "Release download" question_text = "A new version of the \"Remote Connection Manager\" is available at: " \ + last_exe_url + ". " \ "It is highly recommended to install the new version to keep working properly. " \ "Do you want to install it now?" buttonReply = QMessageBox.question( self, question_title, question_text, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if buttonReply == QMessageBox.No: return logger.info('Downloading the new client version...') exe_dir = os.path.dirname(sys.executable) tmp_dir = tempfile.gettempdir() last_exe_path = os.path.join(tmp_dir, os.path.basename(sys.executable)) rcm_utils.download_file(last_exe_url, last_exe_path) downloaded_exe_checksum = rcm_utils.compute_checksum(last_exe_path) time.sleep(5) if downloaded_exe_checksum != last_exe_checksum: logger.warning('Downloaded file checksum mismatched. ' 'Expected: ' + str(last_exe_path) + '. Found: ' + str(downloaded_exe_checksum) + '. Update stopped.') os.remove(last_exe_path) else: if sys.platform == 'win32': batch_filename = os.path.join(tmp_dir, "RCM_update.bat") with open(batch_filename, 'w') as batch_file: batch_file.write("rem start update bat" + "\n") batch_file.write("cd /D " + exe_dir + "\n") batch_file.write("copy mybatch.bat mybatch.txt\n") batch_file.write('ping -n 3 localhost >nul 2>&1' + "\n") batch_file.write("del mybatch.txt\n") batch_file.write("ren " + os.path.basename(sys.executable) + " _" + os.path.basename(sys.executable) + "\n") batch_file.write("copy " + last_exe_path + "\n") batch_file.write("del " + " _" + os.path.basename(sys.executable) + "\n") batch_file.write("del " + last_exe_path + "\n") batch_file.write("start " + os.path.basename(sys.executable) + "\n") batch_file.write("del " + batch_filename + "\n") batch_file.write("exit\n") logger.info( "The application will be closed and the new one will start in a while." ) os.startfile(batch_filename) else: batch_filename = os.path.join(tmp_dir, "RCM_update.sh") with open(batch_filename, 'w') as batch_file: batch_file.write("#!/bin/bash\n") batch_file.write("#start update bat" + "\n") batch_file.write("cd " + exe_dir + "\n") batch_file.write("sleep 3 \n") batch_file.write("rm " + os.path.basename(sys.executable) + "\n") batch_file.write("cp " + last_exe_path + " .\n") batch_file.write("chmod a+x " + os.path.basename(sys.executable) + "\n") batch_file.write("sleep 2 \n") batch_file.write("./" + os.path.basename(sys.executable) + "\n") logger.info( "The application will be closed and the new one will start in a while!" ) subprocess.Popen(["sh", batch_filename]) else: logger.info('The client is up-to-date')
def kill_all_threads(self): if self.kill_thread: if not self.kill_thread.isFinished(): logger.debug("killing kill display thread") self.kill_thread.terminate()