Ejemplo n.º 1
0
    def _check_next_update(self):
        if self._cancelling or len(self._bundles_to_check) == 0:
            self._completion_cb(self._updates)
            return

        total = self._total_bundles_to_check
        current = total - len(self._bundles_to_check)
        progress = current / float(total)

        self._bundle_update = self._bundles_to_check.pop()
        _logger.debug("Check %s", self._bundle_update.bundle_id)

        # There is no need for a special name lookup for an automatic update.
        # The name lookup is only for UI purposes, but we are running in the
        # background.
        if self._bundle_update.name is None and self._auto:
            self._bundle_update.name = self._bundle_update.bundle_id

        if self._bundle_update.name is not None:
            # if we know the name, we just perform an asynchronous size check
            _logger.debug("Performing async size lookup")
            size_check = Downloader(self._bundle_update.link)
            size_check.connect('complete', self._size_lookup_cb)
            size_check.get_size()
            self._progress_cb(self._bundle_update.name, progress)
        else:
            # if we don't know the name, we run a metadata lookup and get
            # the size and name that way
            _logger.debug("Performing metadata lookup")
            namelookup = MetadataLookup(self._bundle_update.link)
            namelookup.connect('complete', self._name_lookup_complete)
            namelookup.run()
            self._progress_cb(self._bundle_update.bundle_id, progress)
Ejemplo n.º 2
0
    def test_get_size(self):
        downloader = Downloader("http://0.0.0.0:%d/data/test.txt" % self._port)
        self._complete = False
        downloader.connect('complete', self.download_complete_cb)
        downloader.get_size()

        while not self._complete:
            Gtk.main_iteration()

        self.assertEqual(6, self._result)
Ejemplo n.º 3
0
    def _do_download(self, method, **kwargs):
        # set up Downloader, call method on it with given kwargs, wait for
        # response
        self._complete = False
        downloader = Downloader(self._url)
        downloader.connect('complete', self._downloader_complete_cb)
        getattr(downloader, method)(**kwargs)

        while not self._complete:
            Gtk.main_iteration()

        if isinstance(self._result, Exception):
            raise self._result
Ejemplo n.º 4
0
    def _do_download(self, method, **kwargs):
        # set up Downloader, call method on it with given kwargs, wait for
        # response
        self._complete = False
        downloader = Downloader(self._url)
        downloader.connect('complete', self._downloader_complete_cb)
        getattr(downloader, method)(**kwargs)

        while not self._complete:
            Gtk.main_iteration()

        if isinstance(self._result, Exception):
            raise self._result
Ejemplo n.º 5
0
    def check(self, bundle):
        # ASLO knows only about stable SP releases
        major, minor = config.version.split('.')[0:2]
        sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2)

        url = '%s?id=%s&appVersion=%s' % \
            (_UPDATE_PATH, bundle.get_bundle_id(), sp_version)

        self._bundle = bundle

        _logger.debug('Fetch %s', url)
        self._downloader = Downloader(url)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download()
Ejemplo n.º 6
0
    def fetch_update_info(self, installed_bundles, auto, progress_cb,
                          completion_cb, error_cb):
        self._completion_cb = completion_cb
        self._progress_cb = progress_cb
        self._error_cb = error_cb

        self._bundles = installed_bundles

        self._progress_cb('', 0)  # Set the status to 'Looking for updates'

        settings = Gio.Settings('org.sugarlabs.update')
        data_json_url = settings.get_string('new-aslo-url')
        self._downloader = Downloader(data_json_url)
        self._downloader.connect('complete',
                                 self.__data_json_download_complete_cb)
        self._downloader.download()
Ejemplo n.º 7
0
    def _check_next_update(self):
        if self._cancelling or len(self._bundles_to_check) == 0:
            self._completion_cb(self._updates)
            return

        total = self._total_bundles_to_check
        current = total - len(self._bundles_to_check)
        progress = current / float(total)

        self._bundle_update = self._bundles_to_check.pop()
        _logger.debug("Check %s", self._bundle_update.bundle_id)

        # There is no need for a special name lookup for an automatic update.
        # The name lookup is only for UI purposes, but we are running in the
        # background.
        if self._bundle_update.name is None and self._auto:
            self._bundle_update.name = self._bundle_update.bundle_id

        if self._bundle_update.name is not None:
            # if we know the name, we just perform an asynchronous size check
            _logger.debug("Performing async size lookup")
            size_check = Downloader(self._bundle_update.link)
            size_check.connect('complete', self._size_lookup_cb)
            size_check.get_size()
            self._progress_cb(self._bundle_update.name, progress)
        else:
            # if we don't know the name, we run a metadata lookup and get
            # the size and name that way
            _logger.debug("Performing metadata lookup")
            namelookup = MetadataLookup(self._bundle_update.link)
            namelookup.connect('complete', self._name_lookup_complete)
            namelookup.run()
            self._progress_cb(self._bundle_update.bundle_id, progress)
Ejemplo n.º 8
0
    def check(self, bundle):
        # ASLO knows only about stable SP releases
        major, minor = config.version.split(".")[0:2]
        sp_version = "%s.%s" % (major, int(minor) + int(minor) % 2)

        url = "%s?id=%s&appVersion=%s" % (_UPDATE_PATH, bundle.get_bundle_id(), sp_version)

        self._bundle = bundle

        _logger.debug("Fetch %s", url)
        self._downloader = Downloader(url)
        self._downloader.connect("complete", self.__downloader_complete_cb)
        self._downloader.download()
Ejemplo n.º 9
0
    def _download_next_update(self):
        if self._cancelling:
            self._finished(True)
            return

        if len(self._bundles_to_update) == 0:
            self._finished()
            return

        self._state = STATE_DOWNLOADING
        self._bundle_update = self._bundles_to_update.pop()
        _logger.debug("Downloading update for %s",
                      self._bundle_update.bundle_id)

        total = self._total_bundles_to_update * 2
        current = total - len(self._bundles_to_update) * 2 - 2
        progress = current / float(total)
        self.emit('progress', self._state, self._bundle_update.name, progress)

        self._downloader = Downloader(self._bundle_update.link)
        self._downloader.connect('progress', self.__downloader_progress_cb)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download_to_temp()
Ejemplo n.º 10
0
    def test_download_to_temp(self):
        downloader = Downloader("http://0.0.0.0:%d/data/test.txt" % self._port)
        self._complete = False
        downloader.connect('complete', self.download_complete_cb)
        downloader.download_to_temp()

        while not self._complete:
            Gtk.main_iteration()

        self.assertIsNone(self._result)
        path = downloader.get_local_file_path()
        text = open(path, "r").read()
        self.assertEqual("hello\n", text)
Ejemplo n.º 11
0
    def test_get_size(self):
        downloader = Downloader("http://0.0.0.0:%d/data/test.txt" % self._port)
        self._complete = False
        downloader.connect('complete', self.download_complete_cb)
        downloader.get_size()

        while not self._complete:
            Gtk.main_iteration()

        self.assertEqual(6, self._result)
Ejemplo n.º 12
0
    def fetch_update_info(self, installed_bundles, auto, progress_cb,
                          completion_cb, error_cb):
        self._completion_cb = completion_cb
        self._progress_cb = progress_cb
        self._error_cb = error_cb

        self._bundles = installed_bundles

        self._progress_cb('', 0)  # Set the status to 'Looking for updates'

        settings = Gio.Settings('org.sugarlabs.update')
        data_json_url = settings.get_string('new-aslo-url')
        self._downloader = Downloader(data_json_url)
        self._downloader.connect('complete',
                                 self.__data_json_download_complete_cb)
        self._downloader.download()
Ejemplo n.º 13
0
    def _query(self):
        self.clean()
        settings = Gio.Settings.new(_MICROFORMAT_URL_PATH)
        url = settings.get_string(_MICROFORMAT_URL_KEY)
        _logger.debug("Query %s %r", url, url)
        if url == "":
            self._completion_cb([])
            return

        self._parser = _UpdateHTMLParser(url)
        # wiki.laptop.org have agresive cache, we set max-age=600
        # to be sure the page is no older than 10 minutes
        request_headers = {'Cache-Control': 'max-age=600'}
        downloader = Downloader(url, request_headers=request_headers)
        downloader.connect('got-chunk', self._got_chunk_cb)
        downloader.connect('complete', self._complete_cb)
        downloader.download_chunked()
        self._progress_cb(None, 0)
Ejemplo n.º 14
0
    def test_download_to_temp(self):
        downloader = Downloader("http://0.0.0.0:%d/data/test.txt" % self._port)
        self._complete = False
        downloader.connect('complete', self.download_complete_cb)
        downloader.download_to_temp()

        while not self._complete:
            Gtk.main_iteration()

        self.assertIsNone(self._result)
        path = downloader.get_local_file_path()
        text = open(path, "r").read()
        self.assertEqual("hello\n", text)
Ejemplo n.º 15
0
    def _query(self):
        self.clean()
        settings = Gio.Settings(_MICROFORMAT_URL_PATH)
        url = settings.get_string(_MICROFORMAT_URL_KEY)
        _logger.debug("Query %s %r", url, url)
        if url == "":
            self._completion_cb([])
            return

        self._parser = _UpdateHTMLParser(url)
        # wiki.laptop.org have agresive cache, we set max-age=600
        # to be sure the page is no older than 10 minutes
        request_headers = {'Cache-Control': 'max-age=600'}
        downloader = Downloader(url, request_headers=request_headers)
        downloader.connect('got-chunk', self._got_chunk_cb)
        downloader.connect('complete', self._complete_cb)
        downloader.download_chunked()
        self._progress_cb(None, 0)
Ejemplo n.º 16
0
    def _download_next_update(self):
        if self._cancelling:
            self._finished(True)
            return

        if len(self._bundles_to_update) == 0:
            self._finished()
            return

        self._state = STATE_DOWNLOADING
        self._bundle_update = self._bundles_to_update.pop()
        _logger.debug("Downloading update for %s",
                      self._bundle_update.bundle_id)

        total = self._total_bundles_to_update * 2
        current = total - len(self._bundles_to_update) * 2 - 2
        progress = current / float(total)
        self.emit('progress', self._state, self._bundle_update.name, progress)

        self._downloader = Downloader(self._bundle_update.link)
        self._downloader.connect('progress', self.__downloader_progress_cb)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download_to_temp()
Ejemplo n.º 17
0
class NewAsloUpdater(object):
    """
    Checks for updates using the new ASLO's update.json file
    """

    def __init__(self):
        self._completion_cb = None
        self._progress_cb = None
        self._error_cb = None

        self._bundles = []
        self._downloader = None
        self._canceled = False

    def fetch_update_info(self, installed_bundles, auto, progress_cb,
                          completion_cb, error_cb):
        self._completion_cb = completion_cb
        self._progress_cb = progress_cb
        self._error_cb = error_cb

        self._bundles = installed_bundles

        self._progress_cb('', 0)  # Set the status to 'Looking for updates'

        settings = Gio.Settings('org.sugarlabs.update')
        data_json_url = settings.get_string('new-aslo-url')
        self._downloader = Downloader(data_json_url)
        self._downloader.connect('complete',
                                 self.__data_json_download_complete_cb)
        self._downloader.download()

    def __data_json_download_complete_cb(self, downloader, result):
        if self._canceled:
            return

        try:
            activities = json.loads(result.get_data())['activities']
        except ValueError:
            self._error_cb('Can not parse loaded update.json')
            return

        updates = []

        for i, bundle in enumerate(self._bundles):
            self._progress_cb(bundle.get_name(), i/len(self._bundles))

            if bundle.get_bundle_id() not in activities:
                logging.debug('%s not in activities' % bundle.get_bundle_id())
                continue
            activity = activities[bundle.get_bundle_id()]

            try:
                version = NormalizedVersion(str(activity['version']))
                min_sugar = NormalizedVersion(str(activity['minSugarVersion']))
            except KeyError:
                logging.debug('KeyError - %s' % bundle.get_bundle_id())
                continue
            except InvalidVersionError:
                logging.debug('InvalidVersion - %s' % bundle.get_bundle_id())
                continue

            if NormalizedVersion(bundle.get_activity_version()) >= version:
                logging.debug('%s is up to date' % bundle.get_bundle_id())
                continue

            if NormalizedVersion(config.version) < min_sugar:
                logging.debug('Upgrade sugar for %s' % bundle.get_bundle_id())
                continue

            logging.debug('Marked for update: %s' % bundle.get_bundle_id())
            u = BundleUpdate(bundle.get_bundle_id(), bundle.get_name(),
                             version,
                             activity['xo_url'],
                             activity.get('xo_size', 1024 * 2))
            updates.append(u)

        self._completion_cb(updates)

    def cancel(self):
        self._canceled = True

        if self._downloader:
            self._downloader.cancel()
            self._completion_cb(None)

    def clean(self):
        self._canceled = False
Ejemplo n.º 18
0
class Updater(GObject.GObject):
    __gtype_name__ = 'SugarUpdater'

    __gsignals__ = {
        'updates-available': (GObject.SignalFlags.RUN_FIRST,
                              None,
                              (object,)),
        'progress': (GObject.SignalFlags.RUN_FIRST,
                     None,
                     (int, str, float)),
        'finished': (GObject.SignalFlags.RUN_FIRST,
                     None,
                     (object, object, bool))
    }

    def __init__(self):
        GObject.GObject.__init__(self)

        settings = Gio.Settings(_UPDATE_KEYS_PATH)
        backend = settings.get_string(_UPDATE_BACKEND_KEY)
        module_name, class_name = backend.rsplit('.', 1)
        _logger.debug("Use backend %s.%s", module_name, class_name)
        module = importlib.import_module("jarabe.model.update." + module_name)
        self._model = getattr(module, class_name)()

        self._updates = None
        self._bundles_to_update = None
        self._total_bundles_to_update = 0
        self._bundle_update = None
        self._bundles_updated = None
        self._bundles_failed = None

        self._downloader = None
        self._cancelling = False
        self._state = STATE_IDLE
        self._auto = False

    def get_state(self):
        return self._state

    def trigger_automatic_update(self):
        if self._state == STATE_IDLE:
            _logger.debug("Starting automatic activity update")
            self.check_updates(True)

    def check_updates(self, auto=False):
        if self._state not in (STATE_IDLE, STATE_CHECKED):
            raise UpdaterStateException()

        self._auto = auto
        self._updates = []
        self._bundles_updated = []
        self._bundles_failed = []
        self._state = STATE_CHECKING
        bundles = list(bundleregistry.get_registry())
        self._model.fetch_update_info(bundles, auto,
                                      self._backend_progress_cb,
                                      self._backend_finished_cb)

    def _backend_progress_cb(self, bundle_name, progress):
        self.emit('progress', self._state, bundle_name, progress)

    def _backend_finished_cb(self, updates):
        _logger.debug("_backend_finished_cb")
        if self._cancelling:
            self._finished(True)
            return

        self._updates = updates
        self._state = STATE_CHECKED
        if self._auto:
            self.update(None)
        else:
            self.emit('updates-available', self._updates)

    def update(self, bundle_ids):
        if self._state != STATE_CHECKED:
            raise UpdaterStateException()

        if bundle_ids is None:
            self._bundles_to_update = self._updates
        else:
            self._bundles_to_update = []
            for bundle_update in self._updates:
                if bundle_update.bundle_id in bundle_ids:
                    self._bundles_to_update.append(bundle_update)

        self._total_bundles_to_update = len(self._bundles_to_update)
        _logger.debug("Starting update of %d activities",
                      self._total_bundles_to_update)
        self._download_next_update()

    def _download_next_update(self):
        if self._cancelling:
            self._finished(True)
            return

        if len(self._bundles_to_update) == 0:
            self._finished()
            return

        self._state = STATE_DOWNLOADING
        self._bundle_update = self._bundles_to_update.pop()
        _logger.debug("Downloading update for %s",
                      self._bundle_update.bundle_id)

        total = self._total_bundles_to_update * 2
        current = total - len(self._bundles_to_update) * 2 - 2
        progress = current / float(total)
        self.emit('progress', self._state, self._bundle_update.name, progress)

        self._downloader = Downloader(self._bundle_update.link)
        self._downloader.connect('progress', self.__downloader_progress_cb)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download_to_temp()

    def __downloader_complete_cb(self, downloader, result):
        if self._cancelling:
            self._cleanup_downloader()
            self._finished(True)
            return

        if isinstance(result, Exception):
            _logger.error('Error downloading update: %s', result)
            self._cleanup_downloader()
            self._bundles_failed.append(self._bundle_update)
            self._download_next_update()
        else:
            self._install_update(self._bundle_update,
                                 self._downloader.get_local_file_path())
            self._downloader = None

    def __downloader_progress_cb(self, downloader, progress):
        total = self._total_bundles_to_update * 2
        current = total - len(self._bundles_to_update) * 2 - 2 + progress
        progress = current / float(total)
        self.emit('progress', self._state, self._bundle_update.name, progress)

    def _install_update(self, bundle_update, local_file_path):
        self._state = STATE_UPDATING
        total = self._total_bundles_to_update
        current = total - len(self._bundles_to_update) - 0.5
        progress = current / float(total)

        _logger.debug("Installing update for %s", bundle_update.bundle_id)
        self.emit('progress', self._state, bundle_update.name, progress)

        current += 0.5
        bundle = bundle_from_archive(local_file_path)
        registry = bundleregistry.get_registry()
        registry.install_async(bundle, self._bundle_installed_cb, current)

    def _bundle_installed_cb(self, bundle, result, progress):
        _logger.debug("%s installed: %r", bundle.get_bundle_id(), result)
        progress = progress / float(self._total_bundles_to_update)
        self.emit('progress', self._state, bundle.get_name(), progress)

        # Remove downloaded bundle archive
        try:
            os.unlink(bundle.get_path())
        except OSError:
            pass

        if result is True:
            self._bundles_updated.append(bundle)
        else:
            self._bundles_failed.append(bundle)

        # do it in idle so the UI has a chance to refresh
        GLib.idle_add(self._download_next_update)

    def _finished(self, cancelled=False):
        self._state = STATE_IDLE
        self._cancelling = False

        _logger.debug("Update finished")
        self.emit('finished', self._bundles_updated, self._bundles_failed,
                  cancelled)
        if not cancelled and len(self._bundles_failed) == 0:
            settings = Gio.Settings(_UPDATE_KEYS_PATH)
            settings.set_int(_LAST_UPDATE_KEY, time.time())
            try:
                os.unlink(_URGENT_TRIGGER_FILE)
            except OSError:
                pass

    def cancel(self):
        # From STATE_CHECKED we can cancel immediately.
        if self._state == STATE_CHECKED:
            self._state = STATE_IDLE
            return

        self._cancelling = True
        self._model.cancel()
        if self._downloader:
            self._downloader.cancel()

    def _cleanup_downloader(self):
        if self._downloader is None:
            return

        file_path = self._downloader.get_local_file_path()
        if file_path is not None:
            try:
                os.unlink(file_path)
            except OSError:
                pass
Ejemplo n.º 19
0
class _UpdateChecker(GObject.GObject):
    __gsignals__ = {
        'check-complete': (GObject.SignalFlags.RUN_FIRST, None, (object,)),
    }
    _CHUNK_SIZE = 10240

    def __init__(self):
        GObject.GObject.__init__(self)
        self._bundle = None

    def check(self, bundle):
        # ASLO knows only about stable SP releases
        major, minor = config.version.split('.')[0:2]
        sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2)

        url = '%s?id=%s&appVersion=%s' % \
            (_UPDATE_PATH, bundle.get_bundle_id(), sp_version)

        self._bundle = bundle

        _logger.debug('Fetch %s', url)
        self._downloader = Downloader(url)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download()

    def __downloader_complete_cb(self, downloader, result):
        if isinstance(result, Exception):
            self.emit('check-complete', result)
            return

        if result is None:
            _logger.error('No XML update data returned from ASLO')
            return

        document = XML(result.get_data())

        if document.find(_FIND_DESCRIPTION) is None:
            _logger.debug('Bundle %s not available in the server for the '
                          'version %s',
                          self._bundle.get_bundle_id(),
                          config.version)
            version = None
            link = None
            size = None
            self.emit('check-complete', None)
            return

        try:
            version = NormalizedVersion(document.find(_FIND_VERSION).text)
        except InvalidVersionError:
            _logger.exception('Exception occurred while parsing version')
            self.emit('check-complete', None)
            return

        link = document.find(_FIND_LINK).text

        try:
            size = long(document.find(_FIND_SIZE).text) * 1024
        except ValueError:
            _logger.exception('Exception occurred while parsing size')
            size = 0

        if version > NormalizedVersion(self._bundle.get_activity_version()):
            result = BundleUpdate(self._bundle.get_bundle_id(),
                                  self._bundle.get_name(), version, link, size)
        else:
            result = None

        self.emit('check-complete', result)
Ejemplo n.º 20
0
class NewAsloUpdater(object):
    """
    Checks for updates using the new ASLO's update.json file
    """

    def __init__(self):
        self._completion_cb = None
        self._progress_cb = None
        self._error_cb = None

        self._bundles = []
        self._downloader = None
        self._canceled = False

    def fetch_update_info(self, installed_bundles, auto, progress_cb,
                          completion_cb, error_cb):
        self._completion_cb = completion_cb
        self._progress_cb = progress_cb
        self._error_cb = error_cb

        self._bundles = installed_bundles

        self._progress_cb('', 0)  # Set the status to 'Looking for updates'

        settings = Gio.Settings('org.sugarlabs.update')
        data_json_url = settings.get_string('new-aslo-url')
        self._downloader = Downloader(data_json_url)
        self._downloader.connect('complete',
                                 self.__data_json_download_complete_cb)
        self._downloader.download()

    def __data_json_download_complete_cb(self, downloader, result):
        if self._canceled:
            return

        try:
            activities = json.loads(result.get_data())['activities']
        except ValueError:
            self._error_cb('Can not parse loaded update.json')
            return

        updates = []

        for i, bundle in enumerate(self._bundles):
            self._progress_cb(bundle.get_name(), i/len(self._bundles))

            if bundle.get_bundle_id() not in activities:
                logging.debug('%s not in activities' % bundle.get_bundle_id())
                continue
            activity = activities[bundle.get_bundle_id()]

            try:
                version = NormalizedVersion(str(activity['version']))
                min_sugar = NormalizedVersion(str(activity['minSugarVersion']))
            except KeyError:
                logging.debug('KeyError - %s' % bundle.get_bundle_id())
                continue
            except InvalidVersionError:
                logging.debug('InvalidVersion - %s' % bundle.get_bundle_id())
                continue

            if NormalizedVersion(bundle.get_activity_version()) >= version:
                logging.debug('%s is up to date' % bundle.get_bundle_id())
                continue

            if NormalizedVersion(config.version) < min_sugar:
                logging.debug('Upgrade sugar for %s' % bundle.get_bundle_id())
                continue

            logging.debug('Marked for update: %s' % bundle.get_bundle_id())
            u = BundleUpdate(bundle.get_bundle_id(), bundle.get_name(),
                             version,
                             activity['xo_url'],
                             activity.get('xo_size', 1024 * 2))
            updates.append(u)

        self._completion_cb(updates)

    def cancel(self):
        self._canceled = True

        if self._downloader:
            self._downloader.cancel()
            self._completion_cb(None)

    def clean(self):
        self._canceled = False
Ejemplo n.º 21
0
class _UpdateChecker(GObject.GObject):
    __gsignals__ = {
        'check-complete': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
    }
    _CHUNK_SIZE = 10240

    def __init__(self):
        GObject.GObject.__init__(self)
        self._bundle = None

    def check(self, bundle):
        # ASLO knows only about stable SP releases
        major, minor = config.version.split('.')[0:2]
        sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2)

        url = '%s?id=%s&appVersion=%s' % \
            (_UPDATE_PATH, bundle.get_bundle_id(), sp_version)

        self._bundle = bundle

        _logger.debug('Fetch %s', url)
        self._downloader = Downloader(url)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download()

    def __downloader_complete_cb(self, downloader, result):
        if isinstance(result, Exception):
            self.emit('check-complete', result)
            return

        if result is None:
            _logger.error('No XML update data returned from ASLO')
            return

        document = XML(result.get_data())

        if document.find(_FIND_DESCRIPTION) is None:
            _logger.debug(
                'Bundle %s not available in the server for the '
                'version %s', self._bundle.get_bundle_id(), config.version)
            version = None
            link = None
            size = None
            self.emit('check-complete', None)
            return

        try:
            version = NormalizedVersion(document.find(_FIND_VERSION).text)
        except InvalidVersionError:
            _logger.exception('Exception occurred while parsing version')
            self.emit('check-complete', None)
            return

        link = document.find(_FIND_LINK).text

        try:
            size = long(document.find(_FIND_SIZE).text) * 1024
        except ValueError:
            _logger.exception('Exception occurred while parsing size')
            size = 0

        if version > NormalizedVersion(self._bundle.get_activity_version()):
            result = BundleUpdate(self._bundle.get_bundle_id(),
                                  self._bundle.get_name(), version, link, size)
        else:
            result = None

        self.emit('check-complete', result)
Ejemplo n.º 22
0
class Updater(GObject.GObject):
    __gtype_name__ = 'SugarUpdater'

    __gsignals__ = {
        'updates-available': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'progress': (GObject.SignalFlags.RUN_FIRST, None, (int, str, float)),
        'finished':
        (GObject.SignalFlags.RUN_FIRST, None, (object, object, bool))
    }

    def __init__(self):
        GObject.GObject.__init__(self)

        client = GConf.Client.get_default()
        backend = client.get_string(_UPDATE_BACKEND_KEY)
        module_name, class_name = backend.rsplit('.', 1)
        _logger.debug("Use backend %s.%s", module_name, class_name)
        module = importlib.import_module("jarabe.model.update." + module_name)
        self._model = getattr(module, class_name)()

        self._updates = None
        self._bundles_to_update = None
        self._total_bundles_to_update = 0
        self._bundle_update = None
        self._bundles_updated = None
        self._bundles_failed = None

        self._downloader = None
        self._cancelling = False
        self._state = STATE_IDLE
        self._auto = False

    def get_state(self):
        return self._state

    def trigger_automatic_update(self):
        if self._state == STATE_IDLE:
            _logger.debug("Starting automatic activity update")
            self.check_updates(True)

    def check_updates(self, auto=False):
        if self._state not in (STATE_IDLE, STATE_CHECKED):
            raise UpdaterStateException()

        self._auto = auto
        self._updates = []
        self._bundles_updated = []
        self._bundles_failed = []
        self._state = STATE_CHECKING
        bundles = list(bundleregistry.get_registry())
        self._model.fetch_update_info(bundles, auto, self._backend_progress_cb,
                                      self._backend_finished_cb)

    def _backend_progress_cb(self, bundle_name, progress):
        self.emit('progress', self._state, bundle_name, progress)

    def _backend_finished_cb(self, updates):
        _logger.debug("_backend_finished_cb")
        if self._cancelling:
            self._finished(True)
            return

        self._updates = updates
        self._state = STATE_CHECKED
        if self._auto:
            self.update(None)
        else:
            self.emit('updates-available', self._updates)

    def update(self, bundle_ids):
        if self._state != STATE_CHECKED:
            raise UpdaterStateException()

        if bundle_ids is None:
            self._bundles_to_update = self._updates
        else:
            self._bundles_to_update = []
            for bundle_update in self._updates:
                if bundle_update.bundle_id in bundle_ids:
                    self._bundles_to_update.append(bundle_update)

        self._total_bundles_to_update = len(self._bundles_to_update)
        _logger.debug("Starting update of %d activities",
                      self._total_bundles_to_update)
        self._download_next_update()

    def _download_next_update(self):
        if self._cancelling:
            self._finished(True)
            return

        if len(self._bundles_to_update) == 0:
            self._finished()
            return

        self._state = STATE_DOWNLOADING
        self._bundle_update = self._bundles_to_update.pop()
        _logger.debug("Downloading update for %s",
                      self._bundle_update.bundle_id)

        total = self._total_bundles_to_update * 2
        current = total - len(self._bundles_to_update) * 2 - 2
        progress = current / float(total)
        self.emit('progress', self._state, self._bundle_update.name, progress)

        self._downloader = Downloader(self._bundle_update.link)
        self._downloader.connect('progress', self.__downloader_progress_cb)
        self._downloader.connect('complete', self.__downloader_complete_cb)
        self._downloader.download_to_temp()

    def __downloader_complete_cb(self, downloader, result):
        if self._cancelling:
            self._cleanup_downloader()
            self._finished(True)
            return

        if isinstance(result, Exception):
            _logger.error('Error downloading update: %s', result)
            self._cleanup_downloader()
            self._bundles_failed.append(self._bundle_update)
            self._download_next_update()
        else:
            self._install_update(self._bundle_update,
                                 self._downloader.get_local_file_path())
            self._downloader = None

    def __downloader_progress_cb(self, downloader, progress):
        total = self._total_bundles_to_update * 2
        current = total - len(self._bundles_to_update) * 2 - 2 + progress
        progress = current / float(total)
        self.emit('progress', self._state, self._bundle_update.name, progress)

    def _install_update(self, bundle_update, local_file_path):
        self._state = STATE_UPDATING
        total = self._total_bundles_to_update
        current = total - len(self._bundles_to_update) - 0.5
        progress = current / float(total)

        _logger.debug("Installing update for %s", bundle_update.bundle_id)
        self.emit('progress', self._state, bundle_update.name, progress)

        current += 0.5
        bundle = bundle_from_archive(local_file_path)
        registry = bundleregistry.get_registry()
        registry.install_async(bundle, self._bundle_installed_cb, current)

    def _bundle_installed_cb(self, bundle, result, progress):
        _logger.debug("%s installed: %r", bundle.get_bundle_id(), result)
        progress = progress / float(self._total_bundles_to_update)
        self.emit('progress', self._state, bundle.get_name(), progress)

        # Remove downloaded bundle archive
        try:
            os.unlink(bundle.get_path())
        except OSError:
            pass

        if result is True:
            self._bundles_updated.append(bundle)
        else:
            self._bundles_failed.append(bundle)

        # do it in idle so the UI has a chance to refresh
        GLib.idle_add(self._download_next_update)

    def _finished(self, cancelled=False):
        self._state = STATE_IDLE
        self._cancelling = False

        _logger.debug("Update finished")
        self.emit('finished', self._bundles_updated, self._bundles_failed,
                  cancelled)
        if not cancelled and len(self._bundles_failed) == 0:
            client = GConf.Client.get_default()
            client.set_int(_LAST_UPDATE_KEY, time.time())
            try:
                os.unlink(_URGENT_TRIGGER_FILE)
            except OSError:
                pass

    def cancel(self):
        # From STATE_CHECKED we can cancel immediately.
        if self._state == STATE_CHECKED:
            self._state = STATE_IDLE
            return

        self._cancelling = True
        self._model.cancel()
        if self._downloader:
            self._downloader.cancel()

    def _cleanup_downloader(self):
        if self._downloader is None:
            return

        file_path = self._downloader.get_local_file_path()
        if file_path is not None:
            try:
                os.unlink(file_path)
            except OSError:
                pass