class Core(object): def __init__(self, args): self.args = args self.gateways = [] self.sync_folders = [] self.config = Config(args.config[0] if args.config else None) self.servers_connected = 0 self.servers_known = 0 self.total_available_space = 0 self.status_text = 'Status: ' self.new_messages = [] self.settings = {} self.tray = None def initialize_gateways(self): logging.debug("Initializing Tahoe-LAFS gateway(s)...") logging.debug(self.settings) for gateway in self.settings: try: t = Tahoe(gateway, self.settings[gateway]['tahoe.cfg']) except KeyError: t = Tahoe(gateway) self.gateways.append(t) for section, contents in self.settings[gateway].items(): if section == 'sync': for local_dir, dircap in contents.items(): self.add_sync_folder(local_dir, dircap, t) def add_sync_folder(self, local_dir, dircap=None, tahoe=None): logging.debug("Adding SyncFolder (%s)...", local_dir) # TODO: Add error handling if not os.path.isdir(local_dir): logging.debug( "Directory %s doesn't exist; creating %s...", local_dir, local_dir) os.makedirs(local_dir) sync_folder = SyncFolder(self, local_dir, dircap, tahoe) self.sync_folders.append(sync_folder) def insert_new_dircap(self, sync_folder): # FIXME: Ugly hack. This should all probably move to SyncFolder:start local_dir = sync_folder.local_dir logging.debug( "No dircap assaciated with %s; creating new dircap...", local_dir) dircap = sync_folder.tahoe.command(['mkdir'], num_attempts=10) for gateway, settings in self.settings.items(): for setting, value in settings.items(): if setting == 'sync' and value[local_dir] is None: sync_folder.remote_dircap = dircap self.settings[gateway]['sync'][local_dir] = dircap self.config.save(self.settings) if gateway.startswith('pb://'): introducer_furl = gateway else: client_settings = setting['tahoe.cfg']['client'] introducer_furl = client_settings['introducer.furl'] dircap_txt = os.path.join( local_dir, 'Gridsync Invite Code.txt') with open(dircap_txt, 'w') as f: f.write( 'gridsync' + introducer_furl[2:] + '/' + dircap) self.notify("Sync Folder Initialized", "Monitoring {}".format(local_dir)) reactor.callInThread(sync_folder.start) def start_sync_folders(self): logging.debug("Starting SyncFolders...") for sync_folder in self.sync_folders: if not sync_folder.remote_dircap: #reactor.callInThread( # reactor.callLater, 5, self.insert_new_dircap, sync_folder) reactor.callLater(5, self.insert_new_dircap, sync_folder) else: reactor.callInThread(sync_folder.start) def stop_sync_folders(self): logging.debug("Stopping SyncFolders...") for sync_folder in self.sync_folders: reactor.callInThread(sync_folder.stop) def check_state(self): active_jobs = [] for sync_folder in self.sync_folders: if sync_folder.sync_state: active_jobs.append(sync_folder) for message in sync_folder.sync_log: self.new_messages.append(message) sync_folder.sync_log.remove(message) if active_jobs: if not self.args.no_gui and self.tray.animation.state() != 2: self.tray.animation.setPaused(False) self.tray.setToolTip("Gridsync - Syncing...") for sync_folder in self.sync_folders: for operation in sync_folder.tahoe.get_operations(): logging.debug(operation) else: if not self.args.no_gui and self.tray.animation.state() == 2: self.tray.animation.setPaused(True) self.tray.setToolTip("Gridsync - Up to date") self.tray.set_icon(":gridsync-checkmark.png") if self.new_messages: message = '\n'.join(self.new_messages) self.notify("Sync complete", message) self.new_messages = [] def notify(self, title, message): if not self.args.no_gui: self.tray.showMessage(title, message, msecs=5000) else: print(title, message) def start_gateways(self): logging.debug("Starting Tahoe-LAFS gateway(s)...") for gateway in self.gateways: reactor.callInThread(gateway.start) def first_run(self): from gridsync.wizard import Wizard w = Wizard() w.exec_() if not w.introducer_furl or not w.folder: logging.debug("Setup wizard not completed; exiting") reactor.stop() return self.settings = {w.introducer_furl: {'tahoe.cfg': DEFAULT_SETTINGS}} self.settings[w.introducer_furl]['sync'] = {w.folder: None} logging.debug("Setup wizard finished. Using: %s", self.settings) self.initialize_gateways() self.start_gateways() def start(self): reactor.listenTCP(52045, CoreFactory(self), interface='localhost') try: os.makedirs(self.config.config_dir) except OSError: pass if self.args.debug: logging.basicConfig( format='%(asctime)s %(funcName)s %(message)s', level=logging.DEBUG, stream=sys.stdout) else: logfile = os.path.join(self.config.config_dir, 'gridsync.log') logging.basicConfig( format='%(asctime)s %(funcName)s %(message)s', level=logging.INFO, filename=logfile) logging.info("Core started with args: %s", self.args) logging.debug("$PATH is: %s", os.getenv('PATH')) try: self.settings = self.config.load() except IOError: self.settings = {} if not self.settings: reactor.callLater(0, self.first_run) else: self.initialize_gateways() reactor.callLater(0, self.start_gateways) if not self.args.no_gui: self.tray = SystemTrayIcon(self) self.tray.show() state_checker = LoopingCall(self.check_state) state_checker.start(1.0) connection_status_updater = LoopingCall( reactor.callInThread, self.update_connection_status) reactor.callLater(5, connection_status_updater.start, 60) reactor.callLater(1, self.start_sync_folders) reactor.addSystemEventTrigger("before", "shutdown", self.stop) reactor.suggestThreadPoolSize(20) # XXX Adjust? reactor.run() def update_connection_status(self): servers_connected = 0 servers_known = 0 available_space = 0 for gateway in self.gateways: try: prev_servers = gateway.status['servers_connected'] except KeyError: pass try: gateway.update_status() servers_connected += gateway.status['servers_connected'] servers_known += gateway.status['servers_known'] available_space += h2b(gateway.status['total_available_space']) except (OSError, IndexError): # XXX pass try: if prev_servers != gateway.status['servers_connected']: # TODO: Notify on (dis)connects # FIXME: This should only be called if a Tahoe flag is set logging.debug("New storage node (dis)connected.") #reactor.callInThread(gateway.adjust) except UnboundLocalError: pass self.servers_connected = servers_connected self.total_available_space = b2h(available_space) self.servers_known = servers_known # XXX Add logic to check for paused state, etc. self.status_text = "Status: Connected ({} of {} servers)".format( self.servers_connected, self.servers_known) def stop(self): self.stop_sync_folders() self.stop_gateways() self.config.save(self.settings) logging.debug("Stopping reactor...") def stop_gateways(self): logging.debug("Stopping Tahoe-LAFS gateway(s)...") for gateway in self.gateways: reactor.callInThread(gateway.command, ['stop'])
def test_save(tmpdir): config = Config(os.path.join(str(tmpdir), 'test.yml')) config.save({'test': 'test'}) with open(config.config_file) as f: assert f.read() == 'test: test\n'
def test_save(tmpdir): config = Config(os.path.join(str(tmpdir), "test.yml")) config.save({"test": "test"}) with open(config.config_file) as f: assert f.read() == "test: test\n"
def test_config_save(tmpdir): config = Config(os.path.join(str(tmpdir), 'test_save.ini')) config.save({'test_section': {'test_option': 'test_value'}}) with open(config.filename) as f: assert f.read() == '[test_section]\ntest_option = test_value\n\n'
def test_config_save(tmpdir): config = Config(os.path.join(str(tmpdir), "test_save.ini")) config.save({"test_section": {"test_option": "test_value"}}) with open(config.filename) as f: assert f.read() == "[test_section]\ntest_option = test_value\n\n"
class Server(): def __init__(self, args): self.args = args self.gateways = [] self.sync_folders = [] self.config = Config(self.args.config) self.servers_connected = 0 self.servers_known = 0 self.total_available_space = 0 self.status_text = 'Status: ' self.new_messages = [] def initialize_gateways(self): logging.debug("Initializing Tahoe-LAFS gateway(s)...") logging.debug(self.settings) for gateway in self.settings.keys(): try: t = Tahoe(gateway, self.settings[gateway]['tahoe.cfg']) except KeyError: t = Tahoe(gateway) self.gateways.append(t) for section, contents in self.settings[gateway].items(): if section == 'sync': for local_dir, dircap in contents.items(): self.add_sync_folder(local_dir, dircap, t) def add_sync_folder(self, local_dir, dircap=None, tahoe=None): logging.debug("Adding SyncFolder ({})...".format(local_dir)) # TODO: Add error handling if not os.path.isdir(local_dir): logging.debug("Directory {} doesn't exist; " "creating {}...".format(local_dir, local_dir)) os.makedirs(local_dir) if not dircap: logging.debug("No dircap associated with {}; " "creating new dircap...".format(local_dir)) dircap = tahoe.mkdir() self.settings[tahoe.name]['sync'][local_dir] = dircap self.config.save(self.settings) sync_folder = SyncFolder(local_dir, dircap, tahoe) self.sync_folders.append(sync_folder) def start_sync_folders(self): logging.debug("Starting SyncFolders...") for sync_folder in self.sync_folders: reactor.callInThread(sync_folder.start) def stop_sync_folders(self): logging.debug("Stopping SyncFolders...") for sync_folder in self.sync_folders: reactor.callInThread(sync_folder.stop) def handle_command(self, command): if command.lower().startswith('gridsync:'): logging.info('Got gridsync URI: {}'.format(command)) # TODO: Handle this elif command.lower() in ('stop', 'quit', 'exit'): reactor.stop() else: logging.info("Invalid command: {}".format(command)) def check_state(self): active_jobs = [] for sync_folder in self.sync_folders: if sync_folder.sync_state: active_jobs.append(sync_folder) for message in sync_folder.sync_log: self.new_messages.append(message) sync_folder.sync_log.remove(message) if active_jobs: if not self.args.no_gui and self.tray.animation.state() != 2: self.tray.animation.setPaused(False) self.tray.setToolTip("Gridsync - Syncing...") for sync_folder in self.sync_folders: for operation in sync_folder.tahoe.get_operations(): logging.debug(operation) else: if not self.args.no_gui and self.tray.animation.state() == 2: self.tray.animation.setPaused(True) self.tray.setToolTip("Gridsync - Up to date") self.tray.set_icon(":gridsync.png") if self.new_messages: message = '\n'.join(self.new_messages) self.notify("Sync complete", message) self.new_messages = [] def notify(self, title, message): if not self.args.no_gui: self.tray.showMessage(title, message) else: print(title, message) def start_gateways(self): logging.debug("Starting Tahoe-LAFS gateway(s)...") for gateway in self.gateways: reactor.callInThread(gateway.start) def first_run(self): from gridsync.wizard import Wizard w = Wizard(self) w.exec_() logging.debug("Got first run settings: ", self.settings) self.initialize_gateways() self.start_gateways() def start(self): reactor.listenTCP(52045, ServerFactory(self), interface='localhost') try: os.makedirs(self.config.config_dir) except OSError: pass if self.args.debug: logging.basicConfig( format='%(asctime)s %(funcName)s %(message)s', level=logging.DEBUG, stream=sys.stdout) else: logfile = os.path.join(self.config.config_dir, 'gridsync.log') logging.basicConfig( format='%(asctime)s %(funcName)s %(message)s', level=logging.INFO, filename=logfile) logging.info("Server started with args: {}".format((self.args))) logging.debug("$PATH is: {}".format(os.getenv('PATH'))) try: output = Tahoe().command(["--version-and-path"]) logging.info(output.split('\n')[0]) except Exception as e: logging.error('Error checking Tahoe-LAFS version: {}'.format(e)) # TODO: Notify user? try: self.settings = self.config.load() except IOError: self.settings = {} if not self.settings: reactor.callLater(0, self.first_run) else: self.initialize_gateways() reactor.callLater(0, self.start_gateways) if not self.args.no_gui: self.tray = SystemTrayIcon(self) self.tray.show() state_checker = LoopingCall(self.check_state) state_checker.start(1.0) connection_status_updater = LoopingCall( reactor.callInThread, self.update_connection_status) reactor.callLater(5, connection_status_updater.start, 60) reactor.callLater(1, self.start_sync_folders) reactor.addSystemEventTrigger("before", "shutdown", self.stop) reactor.suggestThreadPoolSize(20) # XXX Adjust? reactor.run() def update_connection_status(self): servers_connected = 0 servers_known = 0 available_space = 0 for gateway in self.gateways: try: prev_servers = gateway.status['servers_connected'] except KeyError: pass try: gateway.update_status() servers_connected += gateway.status['servers_connected'] servers_known += gateway.status['servers_known'] available_space += h2b(gateway.status['total_available_space']) except: pass try: if prev_servers != gateway.status['servers_connected']: # TODO: Notify on (dis)connects # FIXME: This should only be called if a Tahoe flag is set logging.debug("New storage node (dis)connected.") #reactor.callInThread(gateway.adjust) except UnboundLocalError: pass self.servers_connected = servers_connected self.total_available_space = b2h(available_space) self.servers_known = servers_known # XXX Add logic to check for paused state, etc. self.status_text = "Status: Connected ({} of {} servers)".format( self.servers_connected, self.servers_known) def stop(self): self.stop_sync_folders() self.stop_gateways() self.config.save(self.settings) logging.debug("Stopping reactor...") def stop_gateways(self): logging.debug("Stopping Tahoe-LAFS gateway(s)...") for gateway in self.gateways: reactor.callInThread(gateway.command, ['stop'])
class Server(): def __init__(self, args): self.args = args self.gateways = [] self.sync_folders = [] self.config = Config(self.args.config) self.servers_connected = 0 self.servers_known = 0 self.total_available_space = 0 self.status_text = 'Status: ' self.new_messages = [] def initialize_gateways(self): logging.debug("Initializing Tahoe-LAFS gateway(s)...") logging.debug(self.settings) for gateway in self.settings.keys(): try: t = Tahoe(gateway, self.settings[gateway]['tahoe.cfg']) except KeyError: t = Tahoe(gateway) self.gateways.append(t) for section, contents in self.settings[gateway].items(): if section == 'sync': for local_dir, dircap in contents.items(): self.add_sync_folder(local_dir, dircap, t) def add_sync_folder(self, local_dir, dircap=None, tahoe=None): logging.debug("Adding SyncFolder ({})...".format(local_dir)) # TODO: Add error handling if not os.path.isdir(local_dir): logging.debug("Directory {} doesn't exist; " "creating {}...".format(local_dir, local_dir)) os.makedirs(local_dir) if not dircap: logging.debug("No dircap associated with {}; " "creating new dircap...".format(local_dir)) dircap = tahoe.mkdir() self.settings[tahoe.name]['sync'][local_dir] = dircap self.config.save(self.settings) sync_folder = SyncFolder(local_dir, dircap, tahoe) self.sync_folders.append(sync_folder) def start_sync_folders(self): logging.debug("Starting SyncFolders...") for sync_folder in self.sync_folders: reactor.callInThread(sync_folder.start) def stop_sync_folders(self): logging.debug("Stopping SyncFolders...") for sync_folder in self.sync_folders: reactor.callInThread(sync_folder.stop) def handle_command(self, command): if command.lower().startswith('gridsync:'): logging.info('Got gridsync URI: {}'.format(command)) # TODO: Handle this elif command.lower() in ('stop', 'quit', 'exit'): reactor.stop() else: logging.info("Invalid command: {}".format(command)) def check_state(self): active_jobs = [] for sync_folder in self.sync_folders: if sync_folder.sync_state: active_jobs.append(sync_folder) for message in sync_folder.sync_log: self.new_messages.append(message) sync_folder.sync_log.remove(message) if active_jobs: if not self.args.no_gui and self.tray.animation.state() != 2: self.tray.animation.setPaused(False) self.tray.setToolTip("Gridsync - Syncing...") for sync_folder in self.sync_folders: for operation in sync_folder.tahoe.get_operations(): logging.debug(operation) else: if not self.args.no_gui and self.tray.animation.state() == 2: self.tray.animation.setPaused(True) self.tray.setToolTip("Gridsync - Up to date") self.tray.set_icon(":gridsync.png") if self.new_messages: message = '\n'.join(self.new_messages) self.notify("Sync complete", message) self.new_messages = [] def notify(self, title, message): if not self.args.no_gui: self.tray.showMessage(title, message) else: print(title, message) def start_gateways(self): logging.debug("Starting Tahoe-LAFS gateway(s)...") for gateway in self.gateways: reactor.callInThread(gateway.start) def first_run(self): from gridsync.wizard import Wizard w = Wizard(self) w.exec_() logging.debug("Got first run settings: ", self.settings) self.initialize_gateways() self.start_gateways() def start(self): reactor.listenTCP(52045, ServerFactory(self), interface='localhost') try: os.makedirs(self.config.config_dir) except OSError: pass if self.args.debug: logging.basicConfig(format='%(asctime)s %(funcName)s %(message)s', level=logging.DEBUG, stream=sys.stdout) else: logfile = os.path.join(self.config.config_dir, 'gridsync.log') logging.basicConfig(format='%(asctime)s %(funcName)s %(message)s', level=logging.INFO, filename=logfile) logging.info("Server started with args: {}".format((self.args))) logging.debug("$PATH is: {}".format(os.getenv('PATH'))) try: output = Tahoe().command(["--version-and-path"]) logging.info(output.split('\n')[0]) except Exception as e: logging.error('Error checking Tahoe-LAFS version: {}'.format(e)) # TODO: Notify user? try: self.settings = self.config.load() except IOError: self.settings = {} if not self.settings: reactor.callLater(0, self.first_run) else: self.initialize_gateways() reactor.callLater(0, self.start_gateways) if not self.args.no_gui: self.tray = SystemTrayIcon(self) self.tray.show() state_checker = LoopingCall(self.check_state) state_checker.start(1.0) connection_status_updater = LoopingCall(reactor.callInThread, self.update_connection_status) reactor.callLater(5, connection_status_updater.start, 60) reactor.callLater(1, self.start_sync_folders) reactor.addSystemEventTrigger("before", "shutdown", self.stop) reactor.suggestThreadPoolSize(20) # XXX Adjust? reactor.run() def update_connection_status(self): servers_connected = 0 servers_known = 0 available_space = 0 for gateway in self.gateways: try: prev_servers = gateway.status['servers_connected'] except KeyError: pass try: gateway.update_status() servers_connected += gateway.status['servers_connected'] servers_known += gateway.status['servers_known'] available_space += h2b(gateway.status['total_available_space']) except: pass try: if prev_servers != gateway.status['servers_connected']: # TODO: Notify on (dis)connects # FIXME: This should only be called if a Tahoe flag is set logging.debug("New storage node (dis)connected.") #reactor.callInThread(gateway.adjust) except UnboundLocalError: pass self.servers_connected = servers_connected self.total_available_space = b2h(available_space) self.servers_known = servers_known # XXX Add logic to check for paused state, etc. self.status_text = "Status: Connected ({} of {} servers)".format( self.servers_connected, self.servers_known) def stop(self): self.stop_sync_folders() self.stop_gateways() self.config.save(self.settings) logging.debug("Stopping reactor...") def stop_gateways(self): logging.debug("Stopping Tahoe-LAFS gateway(s)...") for gateway in self.gateways: reactor.callInThread(gateway.command, ['stop'])