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')
Example #5
0
    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)
Example #6
0
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)