def __init__(self, main, send_events=False, interface=None): super(SyncdaemonService, self).__init__(main, interface) self.send_events = send_events self.network_manager = NetworkManagerState(result_cb=self.network_state_changed) self.network_manager.find_online_state() if interface is None: self.interface = ExternalInterface(service=self) else: self.interface = interface self._create_children() self.main.event_q.subscribe(self.event_listener) self.all_events_sender = None if self.send_events: self.all_events_sender = AllEventsSender(self.interface.events) self.main.event_q.subscribe(self.all_events_sender) self.oauth_credentials = None
class SyncdaemonService(SyncdaemonObject): """The main service.""" def __init__(self, main, send_events=False, interface=None): super(SyncdaemonService, self).__init__(main, interface) self.send_events = send_events self.network_manager = NetworkManagerState( result_cb=self.network_state_changed) self.network_manager.find_online_state() if interface is None: self.interface = ExternalInterface(service=self) else: self.interface = interface self._create_children() self.main.event_q.subscribe(self.event_listener) self.all_events_sender = None if self.send_events: self.all_events_sender = AllEventsSender(self.interface.events) self.main.event_q.subscribe(self.all_events_sender) self.auth_credentials = None def _create_children(self): """Create the specific syncdaemon objects.""" self.status = SyncdaemonStatus(self.main, self.interface) self.file_system = SyncdaemonFileSystem(self.main, self.interface) self.shares = SyncdaemonShares(self.main, self.interface) self.config = SyncdaemonConfig(self.main, self.interface) self.folders = SyncdaemonFolders(self.main, self.interface) self.public_files = SyncdaemonPublicFiles(self.main, self.interface) self.events = SyncdaemonEvents(self.main, self.interface) self.event_listener = SyncdaemonEventListener(self.main, self.interface) self.sync = self # for API compatibility @log_call(logger.info) def start(self): """Start listening for ipc messages.""" return self.interface.start() @log_call(logger.info) def shutdown(self, with_restart=False): """Shutdown the interface and unsubscribe from the event queue.""" self.main.event_q.unsubscribe(self.event_listener) if self.send_events: self.main.event_q.unsubscribe(self.all_events_sender) self.interface.shutdown(with_restart=with_restart) @log_call(logger.info) def connect(self, autoconnecting=True): """Push the SYS_USER_CONNECT event with the stored credentials. If 'autoconnecting' is False, nothing will be done. """ d = defer.succeed(None) if not autoconnecting: logger.info('connect: autoconnecting not set, doing nothing.') return d if self.auth_credentials is None: logger.error('connect: autoconnecting set but no credentials.') return defer.fail(NoAccessToken("got empty credentials.")) logger.debug('connect: auth credentials were given by parameter.') self.main.event_q.push( 'SYS_USER_CONNECT', access_token=self.auth_credentials) return d @log_call(logger.debug) def disconnect(self): """Disconnect from the server.""" self.main.event_q.push('SYS_USER_DISCONNECT') @log_call(logger.debug) def get_homedir(self): """Return the home dir point.""" return self.main.get_homedir().decode('utf-8') @log_call(logger.debug) def get_rootdir(self): """Return the root dir/mount point.""" return self.main.get_rootdir().decode('utf-8') @log_call(logger.debug) def get_sharesdir(self): """Return the shares dir/mount point.""" return self.main.get_sharesdir().decode('utf-8') @log_call(logger.debug) def get_sharesdir_link(self): """Return the shares dir/mount point.""" return self.main.get_sharesdir_link().decode('utf-8') @log_call(logger.debug) def wait_for_nirvana(self, last_event_interval): """Return a deferred that will be fired when nirvana is reached. Nirvana means there are no more events/transfers. """ return self.main.wait_for_nirvana(last_event_interval) @log_call(logger.debug) def quit(self): """Shutdown the syncdaemon.""" return self.main.quit() @unicode_to_bytes @log_call(logger.debug) def rescan_from_scratch(self, volume_id): """Request a rescan from scratch of the volume with volume_id.""" # check that the volume exists volume = self.main.vm.get_volume(volume_id) self.main.action_q.rescan_from_scratch(volume.volume_id) @log_call(logger.debug) def network_state_changed(self, state): """Receive the connection state and call the proper function.""" if state == ONLINE: self.network_connected() else: self.network_disconnected() @log_call(logger.debug) def network_connected(self): """Push the connected event.""" self.main.event_q.push('SYS_NET_CONNECTED') @log_call(logger.debug) def network_disconnected(self): """Push the disconnected event.""" self.main.event_q.push('SYS_NET_DISCONNECTED')
class SyncdaemonService(SyncdaemonObject): """The main service.""" def __init__(self, main, send_events=False, interface=None): super(SyncdaemonService, self).__init__(main, interface) self.send_events = send_events self.network_manager = NetworkManagerState(result_cb=self.network_state_changed) self.network_manager.find_online_state() if interface is None: self.interface = ExternalInterface(service=self) else: self.interface = interface self._create_children() self.main.event_q.subscribe(self.event_listener) self.all_events_sender = None if self.send_events: self.all_events_sender = AllEventsSender(self.interface.events) self.main.event_q.subscribe(self.all_events_sender) self.oauth_credentials = None def _create_children(self): """Create the specific syncdaemon objects.""" self.status = SyncdaemonStatus(self.main, self.interface) self.file_system = SyncdaemonFileSystem(self.main, self.interface) self.shares = SyncdaemonShares(self.main, self.interface) self.config = SyncdaemonConfig(self.main, self.interface) self.folders = SyncdaemonFolders(self.main, self.interface) self.public_files = SyncdaemonPublicFiles(self.main, self.interface) self.events = SyncdaemonEvents(self.main, self.interface) self.event_listener = SyncdaemonEventListener(self.main, self.interface) self.sync = self # for API compatibility @log_call(logger.info) def shutdown(self, with_restart=False): """Shutdown the interface and unsubscribe from the event queue.""" self.main.event_q.unsubscribe(self.event_listener) if self.send_events: self.main.event_q.unsubscribe(self.all_events_sender) self.interface.shutdown(with_restart=with_restart) @defer.inlineCallbacks @log_call(logger.info) def connect(self, autoconnecting=False): """Push the SYS_USER_CONNECT event with the token. The token is requested via com.ubuntuone.credentials service. If 'autoconnecting' is True, no UI window will be raised to prompt the user for login/registration, only already existent credentials will be used. """ if self.oauth_credentials is not None: logger.debug("connect: oauth credentials were given by parameter.") ckey = csecret = key = secret = None if len(self.oauth_credentials) == 4: ckey, csecret, key, secret = self.oauth_credentials elif len(self.oauth_credentials) == 2: ckey, csecret = ("ubuntuone", "hammertime") key, secret = self.oauth_credentials else: msg = "connect: oauth_credentials (%r) was set but is useless!" logger.warning(msg, self.oauth_credentials) return token = {"consumer_key": ckey, "consumer_secret": csecret, "token": key, "token_secret": secret} else: try: token = yield self._request_token(autoconnecting=autoconnecting) except Exception, e: logger.exception("failure while getting the token") raise NoAccessToken(e) if not token: raise NoAccessToken("got empty credentials.") self.main.event_q.push("SYS_USER_CONNECT", access_token=token)
class SyncdaemonService(SyncdaemonObject): """The main service.""" def __init__(self, main, send_events=False, interface=None): super(SyncdaemonService, self).__init__(main, interface) self.send_events = send_events self.network_manager = NetworkManagerState( result_cb=self.network_state_changed) self.network_manager.find_online_state() if interface is None: self.interface = ExternalInterface(service=self) else: self.interface = interface self._create_children() self.main.event_q.subscribe(self.event_listener) self.all_events_sender = None if self.send_events: self.all_events_sender = AllEventsSender(self.interface.events) self.main.event_q.subscribe(self.all_events_sender) self.auth_credentials = None def _create_children(self): """Create the specific syncdaemon objects.""" self.status = SyncdaemonStatus(self.main, self.interface) self.file_system = SyncdaemonFileSystem(self.main, self.interface) self.shares = SyncdaemonShares(self.main, self.interface) self.config = SyncdaemonConfig(self.main, self.interface) self.folders = SyncdaemonFolders(self.main, self.interface) self.public_files = SyncdaemonPublicFiles(self.main, self.interface) self.events = SyncdaemonEvents(self.main, self.interface) self.event_listener = SyncdaemonEventListener(self.main, self.interface) self.sync = self # for API compatibility @log_call(logger.info) def start(self): """Start listening for ipc messages.""" return self.interface.start() @log_call(logger.info) def shutdown(self, with_restart=False): """Shutdown the interface and unsubscribe from the event queue.""" self.main.event_q.unsubscribe(self.event_listener) if self.send_events: self.main.event_q.unsubscribe(self.all_events_sender) self.interface.shutdown(with_restart=with_restart) @defer.inlineCallbacks @log_call(logger.info) def connect(self, autoconnecting=False): """Push the SYS_USER_CONNECT event with the token. The token is requested via com.ubuntuone.credentials service. If 'autoconnecting' is True, no UI window will be raised to prompt the user for login/registration, only already existent credentials will be used. """ if self.auth_credentials is not None: logger.debug('connect: auth credentials were given by parameter.') token = self.auth_credentials else: try: token = yield self._request_token( autoconnecting=autoconnecting) except Exception as e: logger.exception('failure while getting the token') raise NoAccessToken(e) if not token: raise NoAccessToken("got empty credentials.") self.main.event_q.push('SYS_USER_CONNECT', access_token=token) def _request_token(self, autoconnecting): """Request to SSO auth service to fetch the token.""" # FIXME: we need to unbind this from SSO, probably just # get tokens from keyring # call ubuntu sso management = credentials.CredentialsManagementTool() # return the deferred, since we are no longer using signals if autoconnecting: return management.find_credentials() else: return management.login(window_id=0) # no window ID @log_call(logger.debug) def disconnect(self): """Disconnect from the server.""" self.main.event_q.push('SYS_USER_DISCONNECT') @log_call(logger.debug) def get_homedir(self): """Return the home dir point.""" return self.main.get_homedir().decode('utf-8') @log_call(logger.debug) def get_rootdir(self): """Return the root dir/mount point.""" return self.main.get_rootdir().decode('utf-8') @log_call(logger.debug) def get_sharesdir(self): """Return the shares dir/mount point.""" return self.main.get_sharesdir().decode('utf-8') @log_call(logger.debug) def get_sharesdir_link(self): """Return the shares dir/mount point.""" return self.main.get_sharesdir_link().decode('utf-8') @log_call(logger.debug) def wait_for_nirvana(self, last_event_interval): """Return a deferred that will be fired when nirvana is reached. Nirvana means there are no more events/transfers. """ return self.main.wait_for_nirvana(last_event_interval) @log_call(logger.debug) def quit(self): """Shutdown the syncdaemon.""" return self.main.quit() @unicode_to_bytes @log_call(logger.debug) def rescan_from_scratch(self, volume_id): """Request a rescan from scratch of the volume with volume_id.""" # check that the volume exists volume = self.main.vm.get_volume(volume_id) self.main.action_q.rescan_from_scratch(volume.volume_id) @log_call(logger.debug) def network_state_changed(self, state): """Receive the connection state and call the proper function.""" if state == ONLINE: self.network_connected() else: self.network_disconnected() @log_call(logger.debug) def network_connected(self): """Push the connected event.""" self.main.event_q.push('SYS_NET_CONNECTED') @log_call(logger.debug) def network_disconnected(self): """Push the disconnected event.""" self.main.event_q.push('SYS_NET_DISCONNECTED')
def __init__(self, root_dir, shares_dir, data_dir, partials_dir, host='fs-1.one.ubuntu.com', port=443, dns_srv=None, ssl=True, disable_ssl_verify=False, glib_loop=False, mark_interval=120, broadcast_events=False, handshake_timeout=30, shares_symlink_name='Shared With Me', read_limit=None, write_limit=None, throttling_enabled=False, ignore_files=None, oauth_credentials=None): self.root_dir = root_dir self.shares_dir = shares_dir self.shares_dir_link = os.path.join(self.root_dir, shares_symlink_name) self.data_dir = data_dir self.partials_dir = partials_dir self.tritcask_dir = os.path.join(self.data_dir, 'tritcask') self.logger = logging.getLogger('ubuntuone.SyncDaemon.Main') user_config = config.get_user_config() if read_limit is None: read_limit = user_config.get_throttling_read_limit() if write_limit is None: write_limit = user_config.get_throttling_write_limit() if not throttling_enabled: throttling_enabled = user_config.get_throttling() self.logger.info("Starting Ubuntu One client version %s", clientdefs.VERSION) self.logger.info("Using %r as root dir", self.root_dir) self.logger.info("Using %r as data dir", self.data_dir) self.logger.info("Using %r as shares root dir", self.shares_dir) self.db = tritcask.Tritcask(self.tritcask_dir) self.vm = volume_manager.VolumeManager(self) self.fs = filesystem_manager.FileSystemManager( data_dir, partials_dir, self.vm, self.db) self.event_q = event_queue.EventQueue(self.fs, ignore_files) self.fs.register_eq(self.event_q) # subscribe VM to EQ, to be unsubscribed in shutdown self.event_q.subscribe(self.vm) self.vm.init_root() # we don't have the oauth tokens yet, we 'll get them later self.action_q = action_queue.ActionQueue(self.event_q, self, host, port, dns_srv, ssl, disable_ssl_verify, read_limit, write_limit, throttling_enabled) self.hash_q = hash_queue.HashQueue(self.event_q) events_nanny.DownloadFinishedNanny(self.fs, self.event_q, self.hash_q) # call StateManager after having AQ self.state_manager = StateManager(self, handshake_timeout) self.sync = sync.Sync(self) self.lr = local_rescan.LocalRescan(self.vm, self.fs, self.event_q, self.action_q) self.external = ExternalInterface( self, glib_loop, broadcast_events) self.external.oauth_credentials = oauth_credentials if user_config.get_autoconnect(): self.external.connect(autoconnecting=True) self.eventlog_listener = None self.start_event_logger() self.status_listener = None self.start_status_listener() self.mark = task.LoopingCall(self.log_mark) self.mark.start(mark_interval)
class Main(object): """The one who executes the syncdaemon.""" def __init__(self, root_dir, shares_dir, data_dir, partials_dir, host='fs-1.one.ubuntu.com', port=443, dns_srv=None, ssl=True, disable_ssl_verify=False, glib_loop=False, mark_interval=120, broadcast_events=False, handshake_timeout=30, shares_symlink_name='Shared With Me', read_limit=None, write_limit=None, throttling_enabled=False, ignore_files=None, oauth_credentials=None): self.root_dir = root_dir self.shares_dir = shares_dir self.shares_dir_link = os.path.join(self.root_dir, shares_symlink_name) self.data_dir = data_dir self.partials_dir = partials_dir self.tritcask_dir = os.path.join(self.data_dir, 'tritcask') self.logger = logging.getLogger('ubuntuone.SyncDaemon.Main') user_config = config.get_user_config() if read_limit is None: read_limit = user_config.get_throttling_read_limit() if write_limit is None: write_limit = user_config.get_throttling_write_limit() if not throttling_enabled: throttling_enabled = user_config.get_throttling() self.logger.info("Starting Ubuntu One client version %s", clientdefs.VERSION) self.logger.info("Using %r as root dir", self.root_dir) self.logger.info("Using %r as data dir", self.data_dir) self.logger.info("Using %r as shares root dir", self.shares_dir) self.db = tritcask.Tritcask(self.tritcask_dir) self.vm = volume_manager.VolumeManager(self) self.fs = filesystem_manager.FileSystemManager( data_dir, partials_dir, self.vm, self.db) self.event_q = event_queue.EventQueue(self.fs, ignore_files) self.fs.register_eq(self.event_q) # subscribe VM to EQ, to be unsubscribed in shutdown self.event_q.subscribe(self.vm) self.vm.init_root() # we don't have the oauth tokens yet, we 'll get them later self.action_q = action_queue.ActionQueue(self.event_q, self, host, port, dns_srv, ssl, disable_ssl_verify, read_limit, write_limit, throttling_enabled) self.hash_q = hash_queue.HashQueue(self.event_q) events_nanny.DownloadFinishedNanny(self.fs, self.event_q, self.hash_q) # call StateManager after having AQ self.state_manager = StateManager(self, handshake_timeout) self.sync = sync.Sync(self) self.lr = local_rescan.LocalRescan(self.vm, self.fs, self.event_q, self.action_q) self.external = ExternalInterface( self, glib_loop, broadcast_events) self.external.oauth_credentials = oauth_credentials if user_config.get_autoconnect(): self.external.connect(autoconnecting=True) self.eventlog_listener = None self.start_event_logger() self.status_listener = None self.start_status_listener() self.mark = task.LoopingCall(self.log_mark) self.mark.start(mark_interval) def start_event_logger(self): """Start the event logger if it's available for this platform.""" self.eventlog_listener = event_logging.get_listener(self.fs, self.vm) # subscribe to EQ, to be unsubscribed in shutdown if self.eventlog_listener: self.event_q.subscribe(self.eventlog_listener) def start_status_listener(self): """Start the status listener if it is configured to start.""" self.status_listener = status_listener.get_listener(self.fs, self.vm) # subscribe to EQ, to be unsubscribed in shutdown if self.status_listener: self.event_q.subscribe(self.status_listener) def log_mark(self): """Log a "mark" that includes the current AQ state and queue size.""" self.logger.note("---- MARK (state: %s; queue: %d; hash: %d) ----", self.state_manager, len(self.action_q.queue), len(self.hash_q)) def wait_for_nirvana(self, last_event_interval=0.5): """Get a deferred that will fire on Nirvana. That is, when there are no more events or transfers. """ self.logger.debug('wait_for_nirvana(%s)' % last_event_interval) d = defer.Deferred() def start(): """Request the event empty notification.""" self.logger.debug('starting wait_for_nirvana') self.event_q.add_empty_event_queue_callback(callback) def callback(): """Event queue is empty.""" if not (self.state_manager.state == StateManager.QUEUE_MANAGER and self.state_manager.queues.state == QueueManager.IDLE and not self.action_q.queue and self.hash_q.empty()): self.logger.debug("I can't attain Nirvana yet. [state: %s; " "queue: %d; hash: %d]", self.state_manager, len(self.action_q.queue), len(self.hash_q)) return self.logger.debug("Nirvana reached!! I'm a Buddha") self.event_q.remove_empty_event_queue_callback(callback) d.callback(True) reactor.callLater(last_event_interval, start) return d def wait_for(self, *waiting_events, **waiting_kwargs): """defer until event appears""" return WaitingHelpingHandler(self.event_q, waiting_events, waiting_kwargs).deferred def start(self): """Setup the daemon to be ready to run.""" # hook the event queue to the root dir self.event_q.push('SYS_INIT_DONE') @defer.inlineCallbacks def shutdown(self, with_restart=False): """Shutdown the daemon; optionally restart it.""" self.event_q.push('SYS_USER_DISCONNECT') self.event_q.push('SYS_QUIT') self.event_q.unsubscribe(self.vm) if getattr(self, 'eventlog_listener', None) is not None: self.event_q.unsubscribe(self.eventlog_listener) if getattr(self, 'status_listener', None) is not None: self.event_q.unsubscribe(self.status_listener) # shutdown these before event_q so the listeners are unsubscribed self.state_manager.shutdown() self.hash_q.shutdown() self.external.shutdown(with_restart) yield self.event_q.shutdown() self.db.shutdown() self.mark.stop() def restart(self): """Restart the daemon. This ultimately shuts down the daemon and asks external interface to start one again. """ self.quit(exit_value=42, with_restart=True) def check_version(self): """Check the client protocol version matches that of the server.""" self.action_q.check_version() def authenticate(self): """Do the authentication dance.""" self.action_q.authenticate() def set_capabilities(self): """Set the capabilities with the server""" self.logger.debug("capabilities query: %r", syncdaemon.REQUIRED_CAPS) self.action_q.set_capabilities(syncdaemon.REQUIRED_CAPS) def server_rescan(self): """Do the server rescan.""" return self.vm.server_rescan() def get_rootdir(self): """Return the base dir/mount point.""" return self.root_dir def get_sharesdir(self): """Return the dir/mount point for shares.""" return self.shares_dir def get_sharesdir_link(self): """Return the dir link for shares.""" return self.shares_dir_link def quit(self, exit_value=0, with_restart=False): """Shutdown and stop the reactor.""" self.shutdown(with_restart) if reactor.running: reactor.stop() else: sys.exit(exit_value) def local_rescan(self): """Do the local rescan.""" self.logger.note("Local rescan starting...") d = self.lr.start() def _wait_for_hashq(): """Keep on calling this until the hash_q finishes.""" if len(self.hash_q): self.logger.info("hash queue pending. Waiting for it...") reactor.callLater(.1, _wait_for_hashq) else: self.logger.info("hash queue empty. We are ready!") # nudge the action queue into action self.event_q.push('SYS_LOCAL_RESCAN_DONE') def local_rescan_done(_): """After local rescan finished.""" self.logger.note("Local rescan finished!") _wait_for_hashq() def stop_the_press(failure): """Something went wrong in LR, can't continue.""" self.logger.error("Local rescan finished with error: %s", failure.getBriefTraceback()) self.event_q.push('SYS_UNKNOWN_ERROR') d.addCallbacks(local_rescan_done, stop_the_press)