def _compute_packages_changes(self): """Analyse changes in the universe of known packages. This method will verify if there are packages that: - are now installed, and were not; - are now available, and were not; - are now locked, and were not; - were previously available but are not anymore; - were previously installed but are not anymore; - were previously locked but are not anymore; Additionally it will report package locks that: - are now set, and were not; - were previously set but are not anymore; In all cases, the server is notified of the new situation with a "packages" message. @return: A deferred resulting in C{True} if package changes were detected with respect to the previous run, or C{False} otherwise. """ self._facade.ensure_channels_reloaded() old_installed = set(self._store.get_installed()) old_available = set(self._store.get_available()) old_upgrades = set(self._store.get_available_upgrades()) old_locked = set(self._store.get_locked()) current_installed = set() current_available = set() current_upgrades = set() current_locked = set() for package in self._facade.get_packages(): hash = self._facade.get_package_hash(package) id = self._store.get_hash_id(hash) if id is not None: if self._facade.is_package_installed(package): current_installed.add(id) if self._facade.is_package_available(package): current_available.add(id) else: current_available.add(id) # Are there any packages that this package is an upgrade for? if self._facade.is_package_upgrade(package): current_upgrades.add(id) for package in self._facade.get_locked_packages(): hash = self._facade.get_package_hash(package) id = self._store.get_hash_id(hash) if id is not None: current_locked.add(id) new_installed = current_installed - old_installed new_available = current_available - old_available new_upgrades = current_upgrades - old_upgrades new_locked = current_locked - old_locked not_installed = old_installed - current_installed not_available = old_available - current_available not_upgrades = old_upgrades - current_upgrades not_locked = old_locked - current_locked message = {} if new_installed: message["installed"] = \ list(sequence_to_ranges(sorted(new_installed))) if new_available: message["available"] = \ list(sequence_to_ranges(sorted(new_available))) if new_upgrades: message["available-upgrades"] = \ list(sequence_to_ranges(sorted(new_upgrades))) if new_locked: message["locked"] = \ list(sequence_to_ranges(sorted(new_locked))) if not_installed: message["not-installed"] = \ list(sequence_to_ranges(sorted(not_installed))) if not_available: message["not-available"] = \ list(sequence_to_ranges(sorted(not_available))) if not_upgrades: message["not-available-upgrades"] = \ list(sequence_to_ranges(sorted(not_upgrades))) if not_locked: message["not-locked"] = \ list(sequence_to_ranges(sorted(not_locked))) if not message: return succeed(False) message["type"] = "packages" result = self.send_message(message) logging.info("Queuing message with changes in known packages: " "%d installed, %d available, %d available upgrades, " "%d locked, %d not installed, %d not available, " "%d not available upgrades, %d not locked." % (len(new_installed), len(new_available), len(new_upgrades), len(new_locked), len(not_installed), len(not_available), len(not_upgrades), len(not_locked))) def update_currently_known(result): if new_installed: self._store.add_installed(new_installed) if not_installed: self._store.remove_installed(not_installed) if new_available: self._store.add_available(new_available) if new_locked: self._store.add_locked(new_locked) if not_available: self._store.remove_available(not_available) if new_upgrades: self._store.add_available_upgrades(new_upgrades) if not_upgrades: self._store.remove_available_upgrades(not_upgrades) if not_locked: self._store.remove_locked(not_locked) # Something has changed wrt the former run, let's update the # timestamp and return True. stamp_file = self._config.detect_package_changes_stamp touch_file(stamp_file) return True result.addCallback(update_currently_known) return result
def _compute_packages_changes(self): """Analyse changes in the universe of known packages. This method will verify if there are packages that: - are now installed, and were not; - are now available, and were not; - are now locked, and were not; - were previously available but are not anymore; - were previously installed but are not anymore; - were previously locked but are not anymore; Additionally it will report package locks that: - are now set, and were not; - were previously set but are not anymore; Also, packages coming from the security pocket will be reported as such. In all cases, the server is notified of the new situation with a "packages" message. @return: A deferred resulting in C{True} if package changes were detected with respect to the previous run, or C{False} otherwise. """ self._facade.ensure_channels_reloaded() old_installed = set(self._store.get_installed()) old_available = set(self._store.get_available()) old_upgrades = set(self._store.get_available_upgrades()) old_locked = set(self._store.get_locked()) old_autoremovable = set(self._store.get_autoremovable()) old_security = set(self._store.get_security()) current_installed = set() current_available = set() current_upgrades = set() current_locked = set() current_autoremovable = set() current_security = set() lsb = parse_lsb_release(LSB_RELEASE_FILENAME) backports_archive = "{}-backports".format(lsb["code-name"]) security_archive = "{}-security".format(lsb["code-name"]) for package in self._facade.get_packages(): # Don't include package versions from the official backports # archive. The backports archive is enabled by default since # xenial with a pinning policy of 100. Ideally we would # support pinning, but we don't yet. In the mean time, we # ignore backports, so that packages don't get automatically # upgraded to the backports version. backport_origins = [ origin for origin in package.origins if origin.archive == backports_archive] if backport_origins and ( len(backport_origins) == len(package.origins)): # Ignore the version if it's only in the official # backports archive. If it's somewhere else as well, # e.g. a PPA, we assume it was added manually and the # user wants to get updates from it. continue hash = self._facade.get_package_hash(package) id = self._store.get_hash_id(hash) if id is not None: if self._facade.is_package_installed(package): current_installed.add(id) if self._facade.is_package_available(package): current_available.add(id) if self._facade.is_package_autoremovable(package): current_autoremovable.add(id) else: current_available.add(id) # Are there any packages that this package is an upgrade for? if self._facade.is_package_upgrade(package): current_upgrades.add(id) # Is this package present in the security pocket? security_origins = any( origin for origin in package.origins if origin.archive == security_archive) if security_origins: current_security.add(id) for package in self._facade.get_locked_packages(): hash = self._facade.get_package_hash(package) id = self._store.get_hash_id(hash) if id is not None: current_locked.add(id) new_installed = current_installed - old_installed new_available = current_available - old_available new_upgrades = current_upgrades - old_upgrades new_locked = current_locked - old_locked new_autoremovable = current_autoremovable - old_autoremovable new_security = current_security - old_security not_installed = old_installed - current_installed not_available = old_available - current_available not_upgrades = old_upgrades - current_upgrades not_locked = old_locked - current_locked not_autoremovable = old_autoremovable - current_autoremovable not_security = old_security - current_security message = {} if new_installed: message["installed"] = \ list(sequence_to_ranges(sorted(new_installed))) if new_available: message["available"] = \ list(sequence_to_ranges(sorted(new_available))) if new_upgrades: message["available-upgrades"] = \ list(sequence_to_ranges(sorted(new_upgrades))) if new_locked: message["locked"] = \ list(sequence_to_ranges(sorted(new_locked))) if new_autoremovable: message["autoremovable"] = list( sequence_to_ranges(sorted(new_autoremovable))) if not_autoremovable: message["not-autoremovable"] = list( sequence_to_ranges(sorted(not_autoremovable))) if new_security: message["security"] = list( sequence_to_ranges(sorted(new_security))) if not_security: message["not-security"] = list( sequence_to_ranges(sorted(not_security))) if not_installed: message["not-installed"] = \ list(sequence_to_ranges(sorted(not_installed))) if not_available: message["not-available"] = \ list(sequence_to_ranges(sorted(not_available))) if not_upgrades: message["not-available-upgrades"] = \ list(sequence_to_ranges(sorted(not_upgrades))) if not_locked: message["not-locked"] = \ list(sequence_to_ranges(sorted(not_locked))) if not message: return succeed(False) message["type"] = "packages" result = self.send_message(message) logging.info( "Queuing message with changes in known packages: " "%(installed)d installed, %(available)d available, " "%(upgrades)d available upgrades, %(locked)d locked, " "%(auto)d autoremovable, %(security)d security, " "%(not_installed)d not installed, " "%(not_available)d not available, " "%(not_upgrades)d not available upgrades, " "%(not_locked)d not locked, " "%(not_auto)d not autoremovable, " "%(not_security)d not security.", dict( installed=len(new_installed), available=len(new_available), upgrades=len(new_upgrades), locked=len(new_locked), auto=len(new_autoremovable), not_installed=len(not_installed), not_available=len(not_available), not_upgrades=len(not_upgrades), not_locked=len(not_locked), not_auto=len(not_autoremovable), security=len(new_security), not_security=len(not_security))) def update_currently_known(result): if new_installed: self._store.add_installed(new_installed) if not_installed: self._store.remove_installed(not_installed) if new_available: self._store.add_available(new_available) if new_locked: self._store.add_locked(new_locked) if new_autoremovable: self._store.add_autoremovable(new_autoremovable) if not_available: self._store.remove_available(not_available) if new_upgrades: self._store.add_available_upgrades(new_upgrades) if not_upgrades: self._store.remove_available_upgrades(not_upgrades) if not_locked: self._store.remove_locked(not_locked) if not_autoremovable: self._store.remove_autoremovable(not_autoremovable) if new_security: self._store.add_security(new_security) if not_security: self._store.remove_security(not_security) # Something has changed wrt the former run, let's update the # timestamp and return True. stamp_file = self._config.detect_package_changes_stamp touch_file(stamp_file) return True result.addCallback(update_currently_known) return result
def test_three_elements(self): self.assertEqual(list(sequence_to_ranges([1, 2, 3])), [(1, 3)])
def test_many_elements(self): sequence = [1, 2, 15, 16, 17, 19, 21, 22, 23, 24, 26, 27] self.assertEqual(list(sequence_to_ranges(sequence)), [1, 2, (15, 17), 19, (21, 24), 26, 27])
def test_two_elements(self): self.assertEqual(list(sequence_to_ranges([1, 2])), [1, 2])
def test_one_element(self): self.assertEqual(list(sequence_to_ranges([1])), [1])
def test_empty(self): self.assertEqual(list(sequence_to_ranges([])), [])
def test_duplicated_item(self): self.assertRaises(SequenceError, next, sequence_to_ranges([1, 1]))
def test_out_of_order(self): self.assertRaises(SequenceError, next, sequence_to_ranges([2, 1]))