def load_configuration(self): cfg_path = os.path.join(get_data_dir(), "server.cfg") # try: f = open(cfg_path, "r") t = f.read() f.close() self.text_edit_widget.setText(t)
def find_highest_installed_version(): codename = configuration.get("Globals", "codename") mainlog.debug("Looking for new version of '{}' in {}".format( codename, get_data_dir())) select = re.compile(codename + r'-([0-9]+\.[0-9]+\.[0-9]+)$') highest_version = None for dirname in os.listdir(get_data_dir()): res = select.match(dirname) if res: d = os.path.join(get_data_dir(), dirname) if os.path.isdir(d): version = StrictVersion(res.group(1)) if not highest_version or version > highest_version: highest_version = version return highest_version
def _save(self): try: old_server_ip = configuration.get("DEFAULT", "public_ip") cfg_path = os.path.join(get_data_dir(), "server.cfg") f = open(cfg_path, "w") f.write(self.text_edit_widget.toPlainText()) f.close() self._log._log_success( "Server configuration saved in {}".format(cfg_path)) load_configuration(cfg_path, "server_config_check.cfg") self._log._log_success("Server configuration reloaded") server_ip = configuration.get("DEFAULT", "public_ip") if old_server_ip != server_ip: self._log._log_success( "Updating IP address in the downloadable delivery_slips") inject_public_ip_in_client(server_ip) except Exception as ex: self._log._log_error( "Something went wrong while saving the configuration : {}". format(ex)) self._log._log("Reloading server configuration") import threading def open_server(url): try: urlopen(url) except ConnectionResetError as ex: pass threading.Thread(target=open_server, args=['http://127.0.0.1:8079/reload']).start()
def upgrade_process(args): if platform.system() != 'Windows': mainlog.info( "The upgrade process won't work on something else than Windows... I skip that." ) return this_version = configuration.this_version # the one of this very code mainlog.debug("Client version is {}".format(this_version)) if args.no_update: mainlog.info("Skipping update process because --no-update is set") # This is rather strange. If we are started by regular Windows ways # (double click, cmd,...) PySide finds its DLL fine. # But, if it is started through the upgrade process (via Popen), then # it doesn't because Windows can't expand junction points correctly # (according to what I saw, this is not a bug in windows, but rather a # feature to prevent old code to misuse junction points) # So, for this code to work, one has to make sure that _setupQtDir # is not called during the import but right after (else it crashes). # This is how to patch the __init__py of PySide : # def _setupQtDirectories(zedir=None): # import sys # import os # from . import _utils # # if zedir: # pysideDir = zedir # else: # pysideDir = _utils.get_pyside_dir() # try: from PySide import _setupQtDirectories except Exception as ex: mainlog.error( "Unable to import _setupQtDirectories. Remember this was a bug fix, make sure " + "_setupQtDirectories is not called at the end of the __init__.py of pyside. " + "Check the comments in the code for more info.") mainlog.exception(ex) return if getattr(sys, 'frozen', False): # Frozen mainlog.debug("Fixing Qt import on frozen exe {}".format( os.path.normpath(os.getcwd()))) _setupQtDirectories(os.path.normpath(os.getcwd())) else: mainlog.debug("Fixed Qt import on NON frozen exe") _setupQtDirectories() return next_version = get_server_version( configuration.update_url_version ) # available on the server (abd maybe already downloaded) current_version = find_highest_installed_version( ) # one we have downloaded in the past mainlog.info( "This version is {}, last downloaded version = {}, version available on server = {}" .format(this_version, current_version, next_version)) if (not current_version or (current_version and this_version >= current_version)) and \ (not next_version or (next_version and this_version >= next_version)): mainlog.info( "The available versions are not more recent than the current one. No update necessary." ) return codename = configuration.get("Globals", "codename") # Update only if we have no current version or if the # next version is higher than ours if next_version and (not current_version or next_version > current_version): try: tmpfile = make_temp_file(prefix='NewVersion_' + version_to_str(next_version), extension='.zip') download_file(configuration.update_url_file, tmpfile) newdir = os.path.join( get_data_dir(), "{}-{}".format(codename, version_to_str(next_version))) extractAll(tmpfile, newdir) # show that we actually downloaded something current_version = next_version except Exception as ex: mainlog.error( "The download of version {} failed. Therefore, I'll go on with the current one." .format(next_version)) mainlog.exception(ex) # If we were able to download a version now or in the # past, then use this one. If not, then we run the # program (that is, the version that was installed # by the user) if current_version: current_dir = os.path.join( get_data_dir(), "{}-{}".format(codename, version_to_str(current_version))) # --no-update "signals" the control transfer (without it we'd # try to update with the latest version again creating an # endless loop) # os.chdir(os.path.join(current_dir,codename)) # FIXME Not sure this is useful; too tired to test cmd = [ os.path.join(os.path.join(current_dir, codename), codename + '.exe'), '--no-update' ] mainlog.info("Transferring control to {}".format(' '.join(cmd))) # DETACHED_PROCESS = 0x00000008 # CREATE_NEW_PROCESS_GROUP = 0x00000200 # subprocess.Popen( cmd,cwd=os.path.join(current_dir,'xxx'),creationflags=DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP) # From what I can see WinExec don't run in os.getcwd(), so I give it an absolute path. try: # win32api.WinExec will *NOT* block. The new version is run # in parallel. This allow us to quit so we don't have two # instances of Koi running simulatenaously # Unfortunaately, because of that it's hard to build a watch # dog that will protect us against a broken upgrade. # For example, we'd have to release our log files... res = win32api.WinExec(" ".join(cmd), win32con.SW_SHOWMAXIMIZED) sys.exit(RETURN_CODE_SUCCESS) except Exception as ex: mainlog.error( "Control transfer failed. There was an error while starting the newer version {}" .format(current_version)) mainlog.exception(ex) return False
def __init__(self): super(MainWindow, self).__init__() self.edit_config = EditConfigurationDialog(self) self.edit_config.load_configuration() w = QWidget(self) big_hlayout = QHBoxLayout() layout = QVBoxLayout() big_hlayout.addLayout(layout) big_hlayout.addWidget(self.edit_config) layout.addWidget( QLabel("<h1>{} administration</h1>".format( configuration.get("Globals", "name")))) glayout = QGridLayout() row = 0 HOST = "{} or {}".format(guess_server_public_ip(), socket.gethostname()) glayout.addWidget(QLabel("<b>Server's IP address"), row, 0) ip_address = configuration.get("DEFAULT", "public_ip") if not ip_address: ip_address = "<font color='red'><b>NOT DEFINED</b></font>" glayout.addWidget(QLabel("{} (guessed: {})".format(ip_address, HOST)), row, 1) row += 1 glayout.addWidget(QLabel("<b>Client database URL"), row, 0) db_url = configuration.get("Database", "url") self.public_url_edit = QLabel(db_url) glayout.addWidget(self.public_url_edit, row, 1) row += 1 glayout.addWidget(QLabel("<b>Client server URL"), row, 0) url = configuration.get("DownloadSite", "public_url") self.public_web_url_edit = QLabel(url) glayout.addWidget(self.public_web_url_edit, row, 1) row += 1 glayout.addWidget(QLabel("Server local DB URL"), row, 0) db_url = configuration.get("Database", "admin_url") self.url_edit = QLabel(db_url) glayout.addWidget(self.url_edit, row, 1) row += 1 glayout.addWidget(QLabel("Backup directory"), row, 0) db_url = configuration.get("Backup", "backup_directory") self.backup_directory_edit = QLabel(db_url) glayout.addWidget(self.backup_directory_edit, row, 1) row += 1 glayout.addWidget(QLabel("Data/logs directory"), row, 0) self.data_directory_edit = QLabel(get_data_dir()) glayout.addWidget(self.data_directory_edit, row, 1) qgb = QGroupBox("Life data") qgb.setLayout(glayout) layout.addWidget(qgb) hlayout = QHBoxLayout() b = QPushButton("Check database") b.clicked.connect(self.check_database) hlayout.addWidget(b) b = QPushButton("Check web server") b.clicked.connect(self.check_server) hlayout.addWidget(b) b = QPushButton("Show delivery_slips download page") b.clicked.connect(self.show_client_dowload_page) hlayout.addWidget(b) qgb = QGroupBox("Checks") qgb.setLayout(hlayout) layout.addWidget(qgb) hlayout = QHBoxLayout() # b = QPushButton("Set backup directory") # b.clicked.connect(self.set_backup_directory) # hlayout.addWidget( b) b = QPushButton("Restore backup") b.clicked.connect(self.restore_backup) hlayout.addWidget(b) b = QPushButton("Reset admin account") b.clicked.connect(self.create_root_account) hlayout.addWidget(b) # b = QPushButton("Set public IP") # b.clicked.connect(self.set_public_ip) # hlayout.addWidget( b) # Please use the command line, this is not for the faint hearted. # b = QPushButton("Clear database") # b.clicked.connect(self.create_database) # hlayout.addWidget( b) qgb = QGroupBox("Actions") qgb.setLayout(hlayout) layout.addWidget(qgb) vlayout = QVBoxLayout() # if platform.system() == 'Windows': # # when running on Linux, it's expected that the # # whole server configuration is set up by us # # hlayout = QHBoxLayout() # b = QPushButton("Start server manually") # b.clicked.connect(self.start_server_manually) # hlayout.addWidget( b) # # b = QPushButton("Stop server manually") # b.clicked.connect(self.stop_server_manually) # hlayout.addWidget( b) # vlayout.addLayout(hlayout) # # hlayout = QHBoxLayout() # b = QPushButton("Install services") # b.clicked.connect(self.install_service) # hlayout.addWidget( b) # # b = QPushButton("Uninstall services") # b.clicked.connect(self.uninstall_service) # hlayout.addWidget( b) # vlayout.addLayout(hlayout) # # b = QPushButton("Install scheduled services") # b.clicked.connect(self.install_on_start_tasks) # vlayout.addWidget( b) # # # b = QPushButton("Upgrade delivery_slips") # # b.clicked.connect(self.upgrade_client) # # layout.addWidget( b) # # qgb = QGroupBox("Service & installation") # qgb.setLayout(vlayout) # layout.addWidget(qgb) self.log_view = QTextEdit() layout.addWidget(self.log_view) self.url_edit.setTextInteractionFlags(Qt.TextSelectableByMouse) self.public_url_edit.setTextInteractionFlags(Qt.TextSelectableByMouse) self.public_web_url_edit.setTextInteractionFlags( Qt.TextSelectableByMouse) self.backup_directory_edit.setTextInteractionFlags( Qt.TextSelectableByMouse) self.log_view.setReadOnly(True) w.setLayout(big_hlayout) self.setCentralWidget(w)
def restore_backup(self): self._clear_log() self._log("Restore procedure started") url = self.url_edit.text() psql_path = configuration.get("Commands", "psql") if not psql_path: self._log_error( "The Commands/psql path is not set in the server.cfg") self._log("Please fix the configuration file (on the right)") return if not configuration.get("Commands", "pg_restore"): self._log_error( "The Commands/pg_restore path is not set in the server.cfg") self._log("Please fix the configuration file (on the right)") return if not configuration.get("Backup", "backup_directory"): self._log( "The Backup/backup_directory path is not set in the server.cfg" ) self._log("I'm setting it myself.") configuration.set("Backup", "backup_directory", get_data_dir()) configuration.set("DocumentsDatabase", "documents_root", os.path.join(get_data_dir(), "documents")) configuration.save() self.edit_config.load_configuration() login_clt, password_clt, dummy, dummy, dummy = self._extract_db_params_from_url( configuration.get("Database", "url")) login_adm, password_adm, dbname, host, port = self._extract_db_params_from_url( configuration.get("Database", "admin_url")) self._log("{} / {}".format(login_adm, password_adm)) full_path_backup = None d = "" if configuration.get("Backup", "backup_directory"): d = configuration.get("Backup", "backup_directory") if platform.system() == "Windows": if configuration.get("Backup", "backup_directory"): d = configuration.get("Backup", "backup_directory") # Using the static method gives a more native FileDialog. # with support for network backup_file = QFileDialog.getOpenFileName( self, _("Please select a backup file"), d, "{} database backup (*.pgbackup)".format( configuration.get("Globals", "name")))[0] if not backup_file: self._log("Restore aborted") return full_path_backup = backup_file if not os.path.isdir(full_path_backup): self._log( "{} is not a directory, so I'll go up a level".format( full_path_backup)) full_path_backup = os.path.dirname(full_path_backup) if not os.path.isdir(full_path_backup): self._log_error( "{} is not a directory either. Aborting restore.". format(full_path_backup)) return elif platform.system() == "Linux": d = AskWindowsShare(None) d.exec_() if d.result() == QDialog.Accepted: # //192.168.0.6/postgresqlbackup script_path = "/tmp/horse_mount.sh" script = open(script_path, "w") script.write("""#!/bin/bash echo "Creating transfer directory" mkdir /tmp/backup_win echo "Unmounting previous transfer directory (can fail)" umount /tmp/backup_win echo "Mouting the backup directory" mount -t cifs -ousername={},password={} {} /tmp/backup_win """.format(d.user.text().strip(), d.password.text().strip(), d.address.text().strip())) script.close() import stat os.chmod(script_path, stat.S_IEXEC | stat.S_IWRITE | stat.S_IREAD) cmd = [ 'gksudo', '--sudo-mode', '--message', 'Allow Koi to connect to the backup server.', script_path ] # gksudo seems to like to have the DISPLAY set. So I basically copy # it from the calling environment. ret, dummy, dummy = self._run_shell( cmd, {'DISPLAY': os.environ.get('DISPLAY')}) if ret > 0: self._log_error( "The mount operation failed. Please review the parameters you've given." ) self._log_error( "Network address : {}, windows user name : {}".format( d.address.text() or "?", d.user.text() or "?")) return full_path_backup = "/tmp/backup_win" else: dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.Directory) dialog.setNameFilters(['Koi database backup (*.pgbackup)']) dialog.setWindowTitle("Please select a backup file") if configuration.get("Backup", "backup_directory"): dialog.setDirectory( configuration.get("Backup", "backup_directory")) if dialog.exec_(): full_path_backup = dialog.selectedFiles()[0] else: self._log_error( "Without proper source directory, I can't continue !") return else: self._log_error("Unsupported operating system") # At this poitn full_path_backup is the path to the backup # directory of Horse that we want to restore. # It is different than the current backup directory. if full_path_backup: full_restore(configuration, full_path_backup, backup_file, True, mainlog) self._log_success("Backup successfully restored !")
mainlog.addHandler(LoggerHandler(window)) # d = AskWindowsShare(None) # d.exec_() if args.reset_database: window.create_database() sys.exit(0) if args.create_root_account: window.create_root_account() sys.exit(0) window.setMinimumSize(1000, 700) # window._upgrade_client_to_version("1.0.42","postgresql://jsdfksdhf","192.168.16.16","666") cfg_path = os.path.join(get_data_dir(), "server.cfg") if not os.path.exists(cfg_path): f = open(cfg_path, "w") c = configuration.base_configuration.copy() for section in sorted(c.keys()): f.write("[{}]\n".format(section)) for entry, value in c[section].items(): f.write("{} = {}\n".format(entry, value)) f.write("\n") f.close()