diff --git a/client/po/POTFILES.in b/client/po/POTFILES.in index 2a3120011..935d26b3a 100644 --- a/client/po/POTFILES.in +++ b/client/po/POTFILES.in @@ -28,15 +28,16 @@ ../lib/entropy/client/__init__.py ../lib/entropy/client/mirrors.py ../lib/entropy/client/misc.py +../lib/entropy/client/interfaces/sets.py ../lib/entropy/client/interfaces/cache.py ../lib/entropy/client/interfaces/__init__.py ../lib/entropy/client/interfaces/methods.py ../lib/entropy/client/interfaces/noticeboard.py +../lib/entropy/client/interfaces/settings.py ../lib/entropy/client/interfaces/loaders.py ../lib/entropy/client/interfaces/db.py ../lib/entropy/client/interfaces/client.py ../lib/entropy/client/interfaces/repository.py -../lib/entropy/client/interfaces/sets.py ../lib/entropy/client/interfaces/dep.py ../lib/entropy/client/interfaces/qa.py ../lib/entropy/client/interfaces/package/_content.py diff --git a/lib/entropy/client/interfaces/client.py b/lib/entropy/client/interfaces/client.py index 8ca3b81d4..ea66d6d15 100644 --- a/lib/entropy/client/interfaces/client.py +++ b/lib/entropy/client/interfaces/client.py @@ -10,7 +10,6 @@ """ import os -import codecs import threading from entropy.core import Singleton @@ -24,12 +23,11 @@ from entropy.client.interfaces.dep import CalculatorsMixin from entropy.client.interfaces.methods import RepositoryMixin, MiscMixin, \ MatchMixin from entropy.client.interfaces.noticeboard import NoticeBoardMixin +from entropy.client.interfaces.settings import ClientSystemSettingsPlugin from entropy.const import etpConst, const_debug_write, \ - const_convert_to_unicode, const_file_readable + const_convert_to_unicode from entropy.core.settings.base import SystemSettings -from entropy.core.settings.plugins.skel import SystemSettingsPlugin from entropy.misc import LogFile -from entropy.exceptions import SystemDatabaseError, RepositoryError from entropy.cache import EntropyCacher from entropy.i18n import _ @@ -37,675 +35,6 @@ import entropy.dump import entropy.dep import entropy.tools -class ClientSystemSettingsPlugin(SystemSettingsPlugin): - - ID = etpConst['system_settings_plugins_ids']['client_plugin'] - - def __init__(self, helper_interface): - SystemSettingsPlugin.__init__( - self, ClientSystemSettingsPlugin.ID, helper_interface) - self.__repos_files = {} - self.__repos_mtime = {} - self._mtime_cache = {} - # Package repositories must be able to live across - # SystemSettings.clear() calls, because they are very - # special and 3rd-party (but even Sulfur) tools expect to always - # have them there after having called Client.add_package_repository - self.__package_repositories = [] - self.__package_repositories_meta = {} - - @staticmethod - def client_conf_path(): - """ - Return current client.conf path, this takes into account the current - configuration files directory path (which is affected by "root" path - changes [default: /]) - """ - # path to /etc/entropy/server.conf (usually, depends on systemroot) - return os.path.join(etpConst['confdir'], "client.conf") - - def _add_package_repository(self, repository_id, repository_metadata): - """ - Internal method, used by Entropy Client. Add a package repository - to this SystemSettings plugins to make it able to live across - SystemSettings.clear() calls. - - @param repository_id: repository identifier - @type repository_id: string - @param repository_metadata: a dict object that will be merged - into SystemSettings['repositories'] when clear() will be called - @type repository_metadata: dict - @raise KeyError: if repository_id is already stored. - """ - if repository_id in self.__package_repositories: - raise KeyError("%s already added" % (repository_id,)) - self.__package_repositories.append(repository_id) - self.__package_repositories_meta[repository_id] = repository_metadata - - def _drop_package_repository(self, repository_id): - """ - Drop package repository previously added by calling - _add_package_repository() - - @param repository_id: repository identifier - @type repository_id: string - @raise KeyError: if repository_id is not available - """ - del self.__package_repositories_meta[repository_id] - self.__package_repositories.remove(repository_id) - - def __setup_repos_files(self, system_settings): - """ - This function collects available repositories configuration files - by filling internal dict() __repos_files and __repos_mtime. - - @param system_settings: SystemSettings instance - @type system_settings: instance of SystemSettings - @return: None - @rtype: None - """ - - self.__repos_mtime = { - 'repos_license_whitelist': {}, - 'repos_mask': {}, - 'repos_system_mask': {}, - 'repos_critical_updates': {}, - 'repos_keywords': {}, - } - self.__repos_files = { - 'repos_license_whitelist': {}, - 'repos_mask': {}, - 'repos_system_mask': {}, - 'repos_critical_updates': {}, - 'repos_keywords': {}, - } - - dmp_dir = etpConst['dumpstoragedir'] - avail_data = system_settings['repositories']['available'] - for repoid in system_settings['repositories']['order']: - - repo_data = avail_data[repoid] - if "__temporary__" in repo_data: - continue - - repos_mask_setting = {} - repos_mask_mtime = {} - repos_lic_wl_setting = {} - repos_lic_wl_mtime = {} - repos_sm_mask_setting = {} - repos_sm_mask_mtime = {} - confl_tagged = {} - repos_critical_updates_setting = {} - repos_critical_updates_mtime = {} - repos_keywords_setting = {} - repos_keywords_mtime = {} - - maskpath = os.path.join(repo_data['dbpath'], - etpConst['etpdatabasemaskfile']) - wlpath = os.path.join(repo_data['dbpath'], - etpConst['etpdatabaselicwhitelistfile']) - sm_path = os.path.join(repo_data['dbpath'], - etpConst['etpdatabasesytemmaskfile']) - critical_path = os.path.join(repo_data['dbpath'], - etpConst['etpdatabasecriticalfile']) - keywords_path = os.path.join(repo_data['dbpath'], - etpConst['etpdatabasekeywordsfile']) - - if const_file_readable(maskpath): - repos_mask_setting[repoid] = maskpath - repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \ - repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime" - - if const_file_readable(wlpath): - repos_lic_wl_setting[repoid] = wlpath - repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \ - repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \ - ".mtime" - - if const_file_readable(sm_path): - repos_sm_mask_setting[repoid] = sm_path - repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \ - repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \ - ".mtime" - - if const_file_readable(critical_path): - repos_critical_updates_setting[repoid] = critical_path - repos_critical_updates_mtime[repoid] = dmp_dir + "/repo_" + \ - repoid + "_" + etpConst['etpdatabasecriticalfile'] + \ - ".mtime" - - if const_file_readable(keywords_path): - repos_keywords_setting[repoid] = keywords_path - repos_keywords_mtime[repoid] = dmp_dir + "/repo_" + \ - repoid + "_" + etpConst['etpdatabasekeywordsfile'] + \ - ".mtime" - - self.__repos_files['repos_mask'].update(repos_mask_setting) - self.__repos_mtime['repos_mask'].update(repos_mask_mtime) - - self.__repos_files['repos_license_whitelist'].update( - repos_lic_wl_setting) - self.__repos_mtime['repos_license_whitelist'].update( - repos_lic_wl_mtime) - - self.__repos_files['repos_system_mask'].update( - repos_sm_mask_setting) - self.__repos_mtime['repos_system_mask'].update( - repos_sm_mask_mtime) - - self.__repos_files['repos_critical_updates'].update( - repos_critical_updates_setting) - self.__repos_mtime['repos_critical_updates'].update( - repos_critical_updates_mtime) - - self.__repos_files['repos_keywords'].update( - repos_keywords_setting) - self.__repos_mtime['repos_keywords'].update( - repos_keywords_mtime) - - def __generic_parser(self, filepath): - """ - Internal method. This is the generic file parser here. - - @param filepath: valid path - @type filepath: string - @return: raw text extracted from file - @rtype: list - """ - root = etpConst['systemroot'] - try: - mtime = os.path.getmtime(filepath) - except (OSError, IOError): - mtime = 0.0 - - cache_key = (root, filepath) - cache_obj = self._mtime_cache.get(cache_key) - if cache_obj is not None: - if cache_obj['mtime'] == mtime: - return cache_obj['data'] - - cache_obj = {'mtime': mtime,} - - enc = etpConst['conf_encoding'] - data = entropy.tools.generic_file_content_parser(filepath, - comment_tag = "##", encoding = enc) - if SystemSettings.DISK_DATA_CACHE: - cache_obj['data'] = data - self._mtime_cache[cache_key] = cache_obj - return data - - def __run_post_branch_migration_hooks(self, sys_settings_instance): - - # only root can do this - if os.getuid() != 0: - return - - old_branch_path = etpConst['etp_previous_branch_file'] - in_branch_upgrade_path = etpConst['etp_in_branch_upgrade_file'] - current_branch = sys_settings_instance['repositories']['branch'] - enc = etpConst['conf_encoding'] - - def write_current_branch(branch): - with codecs.open(old_branch_path, "w", encoding=enc) as old_brf: - old_brf.write(branch) - - def write_in_branch_upgrade(branch): - with codecs.open(in_branch_upgrade_path, "w", encoding=enc) as brf: - brf.write("in branch upgrade: %s" % (branch,)) - - if not os.path.isfile(old_branch_path): - write_current_branch(current_branch) - return - - with codecs.open(old_branch_path, "r", encoding=enc) as old_f: - old_branch = old_f.readline().strip() - - if old_branch == current_branch: # all fine, no need to run - return - - repos, err = self._helper._run_repositories_post_branch_switch_hooks( - old_branch, current_branch) - if not err: - write_in_branch_upgrade(current_branch) - write_current_branch(current_branch) - - def __run_post_branch_upgrade_hooks(self, sys_settings_instance): - - # only root can do this - if os.getuid() != 0: - return - - repos, errors = self._helper._run_repository_post_branch_upgrade_hooks( - pretend = True) - if not repos: - # no scripts to run - return - - # look for updates - # critical_updates = False is needed to avoid - # issues with metadata not being available - try: - outcome = self._helper.calculate_updates(critical_updates = False) - update, remove = outcome['update'], outcome['remove'] - fine, spm_fine = outcome['fine'], outcome['spm_fine'] - except (ValueError, SystemDatabaseError, RepositoryError,): - # RepositoryError is triggered when branch is hopped - # SystemDatabaseError is triggered when no client db is avail - # ValueError is triggered when repos are broken - update = 1 # foo! - - def delete_in_branch_upgrade(): - br_path = etpConst['etp_in_branch_upgrade_file'] - try: - os.remove(br_path) - except OSError: - pass - - # actually execute this only if - # there are no updates left - if not update: - self._helper._run_repository_post_branch_upgrade_hooks() - delete_in_branch_upgrade() - - def system_mask_parser(self, system_settings_instance): - - parser_data = {} - # match installed packages of system_mask - mask_installed = [] - mask_installed_keys = {} - while (self._helper.installed_repository() != None): - try: - self._helper.installed_repository().validate() - except SystemDatabaseError: - break - mc_cache = set() - repos_mask_list = self.__repositories_system_mask( - system_settings_instance) - m_list = repos_mask_list + system_settings_instance['system_mask'] - for atom in m_list: - m_ids, m_r = self._helper.installed_repository().atomMatch(atom, - multiMatch = True) - if m_r != 0: - continue - mykey = entropy.dep.dep_getkey(atom) - obj = mask_installed_keys.setdefault(mykey, set()) - for m_id in m_ids: - if m_id in mc_cache: - continue - mc_cache.add(m_id) - mask_installed.append(m_id) - obj.add(m_id) - break - - parser_data.update({ - 'repos_installed': mask_installed, - 'repos_installed_keys': mask_installed_keys, - }) - return parser_data - - def masking_validation_parser(self, system_settings_instance): - data = { - 'cache': {}, # package masking validation cache - } - return data - - def __repositories_repos_keywords(self, repo_keywords_path): - """ - Parser returning system packages mask metadata read from - packages.db.keywords file inside the repository directory. - This file contains maintainer supplied per-repository extra - package keywords. - """ - root = etpConst['systemroot'] - try: - mtime = os.path.getmtime(repo_keywords_path) - except (OSError, IOError): - mtime = 0.0 - - cache_key = (root, repo_keywords_path) - cache_obj = self._mtime_cache.get(cache_key) - if cache_obj is not None: - if cache_obj['mtime'] == mtime: - return cache_obj['data'] - - cache_obj = {'mtime': mtime,} - - data = { - # universal keywords: keywords added repository-wide to all - # the available packages (in repo). - 'universal': set(), - # per-package keywording, keys are atoms/dep (first line argument) - # values are provided keywords - 'packages': {}, - 'packages_ids': None, # reserved for entropy.db package validation - } - - enc = etpConst['conf_encoding'] - entries = entropy.tools.generic_file_content_parser( - repo_keywords_path, encoding = enc) - - # iterate over config file data - for entry in entries: - entry = entry.split() - if len(entry) == 1: - # universal keyword - item = entry[0] - if item == "**": - item = '' - data['universal'].add(item) - - elif len(entry) > 1: - # per package keyword - pkg = entry[0] - keywords = entry[1:] - obj = data['packages'].setdefault(pkg, set()) - obj.update(keywords) - - if SystemSettings.DISK_DATA_CACHE: - cache_obj['data'] = data - self._mtime_cache[cache_key] = cache_obj - return data - - def __repositories_system_mask(self, sys_settings_instance): - """ - Parser returning system packages mask metadata read from - packages.db.system_mask file inside the repository directory. - This file contains packages that should be always kept - installed, extending the already defined (in repository database) - set of atoms. - """ - system_mask = [] - enc = etpConst['conf_encoding'] - for repoid in self.__repos_files['repos_system_mask']: - filepath = self.__repos_files['repos_system_mask'][repoid] - mtimepath = self.__repos_mtime['repos_system_mask'][repoid] - sys_settings_instance.validate_entropy_cache( - filepath, mtimepath, repoid = repoid) - - entries = self.__generic_parser(filepath) - system_mask += [x for x in entries if x not in system_mask] - - return system_mask - - def repositories_parser(self, sys_settings_instance): - """ - Parser that generates repository settings metadata. - - @param sys_settings_instance: SystemSettings instance - @type sys_settings_instance: instance of SystemSettings - @return: parsed metadata - @rtype: dict - """ - - # add back repository metadata to SystemSettings['repositories'] - avail_data = sys_settings_instance['repositories']['available'] - for repository_id in self.__package_repositories: - if repository_id not in avail_data: - repodata = self.__package_repositories_meta[repository_id] - # if correct, this won't trigger a stack overflow - # add_repository calling SystemSettings.clear() I mean - added = self._helper.add_repository(repodata) - if not added: - raise ValueError("wtf? cannot add repository") - - # fill repositories metadata dictionaries - self.__setup_repos_files(sys_settings_instance) - - data = { - 'license_whitelist': {}, - 'mask': {}, - 'system_mask': [], - 'critical_updates': {}, - 'repos_keywords': {}, - } - - # parse license whitelist - # Parser returning licenses considered accepted by default - # (= GPL compatibles) read from package.lic_whitelist. - for repoid in self.__repos_files['repos_license_whitelist']: - sys_settings_instance.validate_entropy_cache( - self.__repos_files['repos_license_whitelist'][repoid], - self.__repos_mtime['repos_license_whitelist'][repoid], - repoid = repoid) - - data['license_whitelist'][repoid] = self.__generic_parser( - self.__repos_files['repos_license_whitelist'][repoid]) - - # package masking - # Parser returning packages masked at repository level read from - # packages.db.mask inside the repository database directory. - for repoid in self.__repos_files['repos_mask']: - sys_settings_instance.validate_entropy_cache( - self.__repos_files['repos_mask'][repoid], - self.__repos_mtime['repos_mask'][repoid], repoid = repoid) - - data['mask'][repoid] = self.__generic_parser( - self.__repos_files['repos_mask'][repoid]) - - # keywords masking - # Parser returning packages masked at repository level read from - # packages.db.keywords inside the repository database directory. - for repoid in self.__repos_files['repos_keywords']: - sys_settings_instance.validate_entropy_cache( - self.__repos_files['repos_keywords'][repoid], - self.__repos_mtime['repos_keywords'][repoid], - repoid = repoid) - - data['repos_keywords'][repoid] = \ - self.__repositories_repos_keywords( - self.__repos_files['repos_keywords'][repoid]) - - # system masking - data['system_mask'] = self.__repositories_system_mask( - sys_settings_instance) - - # critical updates - # Parser returning critical packages list metadata read from - # packages.db.critical file inside the repository directory. - # This file contains packages that should be always updated - # before anything else. - for repoid in self.__repos_files['repos_critical_updates']: - sys_settings_instance.validate_entropy_cache( - self.__repos_files['repos_critical_updates'][repoid], - self.__repos_mtime['repos_critical_updates'][repoid], - repoid = repoid) - - data['critical_updates'][repoid] = self.__generic_parser( - self.__repos_files['repos_critical_updates'][repoid]) - - return data - - def misc_parser(self, sys_settings_instance): - - """ - Parses Entropy client system configuration file. - - @return dict data - """ - - data = { - 'filesbackup': etpConst['filesbackup'], - 'forcedupdates': etpConst['forcedupdates'], - 'packagehashes': etpConst['packagehashes'], - 'gpg': etpConst['client_gpg'], - 'ignore_spm_downgrades': False, - 'splitdebug': etpConst['splitdebug'], - 'splitdebug_dirs': etpConst['splitdebug_dirs'], - 'multifetch': 1, - 'collisionprotect': etpConst['collisionprotect'], - 'configprotect': set(), - 'configprotectmask': set(), - 'configprotectskip': set(), - 'autoprune_days': None, # disabled by default - 'edelta_support': False, # disabled by default - } - - cli_conf = ClientSystemSettingsPlugin.client_conf_path() - root = etpConst['systemroot'] - try: - mtime = os.path.getmtime(cli_conf) - except (OSError, IOError): - mtime = 0.0 - - cache_key = (root, cli_conf) - cache_obj = self._mtime_cache.get(cache_key) - if cache_obj is not None: - if cache_obj['mtime'] == mtime: - return cache_obj['data'] - - cache_obj = {'mtime': mtime,} - - if not const_file_readable(cli_conf): - if SystemSettings.DISK_DATA_CACHE: - cache_obj['data'] = data - self._mtime_cache[cache_key] = cache_obj - return data - - def _filesbackup(setting): - bool_setting = entropy.tools.setting_to_bool(setting) - if bool_setting is not None: - data['filesbackup'] = bool_setting - - def _forcedupdates(setting): - bool_setting = entropy.tools.setting_to_bool(setting) - if bool_setting is not None: - data['forcedupdates'] = bool_setting - - def _autoprune(setting): - int_setting = entropy.tools.setting_to_int(setting, 0, 365) - if int_setting is not None: - data['autoprune_days'] = int_setting - - def _packagesdelta(setting): - bool_setting = entropy.tools.setting_to_bool(setting) - if bool_setting is not None: - data['edelta_support'] = bool_setting - - def _packagehashes(setting): - setting = setting.lower().split() - hashes = set() - for opt in setting: - if opt in etpConst['packagehashes']: - hashes.add(opt) - if hashes: - data['packagehashes'] = tuple(sorted(hashes)) - - def _multifetch(setting): - int_setting = entropy.tools.setting_to_int(setting, None, None) - bool_setting = entropy.tools.setting_to_bool(setting) - if int_setting is not None: - if int_setting not in range(2, 11): - int_setting = 10 - data['multifetch'] = int_setting - if bool_setting is not None: - if bool_setting: - data['multifetch'] = 3 - - def _gpg(setting): - bool_setting = entropy.tools.setting_to_bool(setting) - if bool_setting is not None: - data['gpg'] = bool_setting - - def _spm_downgrades(setting): - bool_setting = entropy.tools.setting_to_bool(setting) - if bool_setting is not None: - data['ignore_spm_downgrades'] = bool_setting - - def _splitdebug(setting): - bool_setting = entropy.tools.setting_to_bool(setting) - if bool_setting is not None: - data['splitdebug'] = bool_setting - - def _collisionprotect(setting): - int_setting = entropy.tools.setting_to_int(setting, 0, 2) - if int_setting is not None: - data['collisionprotect'] = int_setting - - def _configprotect(setting): - for opt in setting.split(): - data['configprotect'].add(const_convert_to_unicode(opt)) - - def _configprotectmask(setting): - for opt in setting.split(): - data['configprotectmask'].add(const_convert_to_unicode(opt)) - - def _configprotectskip(setting): - for opt in setting.split(): - data['configprotectskip'].add( - etpConst['systemroot'] + const_convert_to_unicode(opt)) - - settings_map = { - # backward compatibility - 'filesbackup': _filesbackup, - 'files-backup': _filesbackup, - # backward compatibility - 'forcedupdates': _forcedupdates, - 'forced-updates': _forcedupdates, - 'packages-autoprune-days': _autoprune, - 'packages-delta': _packagesdelta, - # backward compatibility - 'packagehashes': _packagehashes, - 'package-hashes': _packagehashes, - 'multifetch': _multifetch, - 'gpg': _gpg, - 'ignore-spm-downgrades': _spm_downgrades, - 'splitdebug': _splitdebug, - # backward compatibility - 'collisionprotect': _collisionprotect, - 'collision-protect': _collisionprotect, - # backward compatibility - 'configprotect': _configprotect, - 'config-protect': _configprotect, - # backward compatibility - 'configprotectmask': _configprotectmask, - 'config-protect-mask': _configprotectmask, - # backward compatibility - 'configprotectskip': _configprotectskip, - 'config-protect-skip': _configprotectskip, - } - - enc = etpConst['conf_encoding'] - with codecs.open(cli_conf, "r", encoding=enc) as client_f: - clientconf = [x.strip() for x in client_f.readlines() if \ - x.strip() and not x.strip().startswith("#")] - for line in clientconf: - - key, value = entropy.tools.extract_setting(line) - if key is None: - continue - - func = settings_map.get(key) - if func is None: - continue - func(value) - - # completely disable GPG feature - if not data['gpg'] and ("gpg" in data['packagehashes']): - data['packagehashes'] = tuple((x for x in data['packagehashes'] \ - if x != "gpg")) - - # support ETP_SPLITDEBUG - split_debug = os.getenv("ETP_SPLITDEBUG") - if split_debug is not None: - _splitdebug(split_debug) - - if SystemSettings.DISK_DATA_CACHE: - cache_obj['data'] = data - self._mtime_cache[cache_key] = cache_obj - return data - - def post_setup(self, system_settings_instance): - """ - Reimplemented from SystemSettingsPlugin. - """ - - if self._helper._can_run_sys_set_hooks: - # run post-branch migration scripts if branch setting got changed - self.__run_post_branch_migration_hooks(system_settings_instance) - # run post-branch upgrade migration scripts if the function - # above created migration files to handle - self.__run_post_branch_upgrade_hooks(system_settings_instance) - class Client(Singleton, TextInterface, LoadersMixin, CacheMixin, CalculatorsMixin, RepositoryMixin, MiscMixin, diff --git a/lib/entropy/client/interfaces/db.py b/lib/entropy/client/interfaces/db.py index ae015776e..d98860427 100644 --- a/lib/entropy/client/interfaces/db.py +++ b/lib/entropy/client/interfaces/db.py @@ -2223,8 +2223,6 @@ class AvailablePackagesRepositoryUpdater(object): return EntropyRepositoryBase.REPOSITORY_UPDATED_OK -_CL_PLUGIN_ID = etpConst['system_settings_plugins_ids']['client_plugin'] - class MaskableRepository(EntropyRepositoryBase): """ Objects inheriting from this class support package masking. @@ -2235,6 +2233,26 @@ class MaskableRepository(EntropyRepositoryBase): """ _MASK_FILTER_CACHE_ID = EntropyCacher.CACHE_IDS['mask_filter'] + _real_client_settings = None + _real_client_settings_lock = threading.Lock() + + def __init__(self, *args, **kwargs): + super(MaskableRepository, self).__init__(*args, **kwargs) + + @property + def _client_settings(self): + """ + Load the Entropy Client settings object. + """ + if self._real_client_settings is None: + with self._real_client_settings_lock: + if self._real_client_settings is None: + from entropy.client.interfaces import Client + + self._real_client_settings = Client().ClientSettings() + + return self._real_client_settings + def _mask_filter_fetch_cache(self, package_id): if self._caching: return loadobj("%s/%s/%s" % ( @@ -2294,8 +2312,8 @@ class MaskableRepository(EntropyRepositoryBase): myr = ref['user_package_mask'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = -1, myr except KeyError: # system settings client plugin not found pass @@ -2334,8 +2352,8 @@ class MaskableRepository(EntropyRepositoryBase): ref = self._settings['pkg_masking_reference'] myr = ref['user_package_unmask'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr except KeyError: # system settings client plugin not found @@ -2347,10 +2365,9 @@ class MaskableRepository(EntropyRepositoryBase): # check if repository packages.db.mask needs it masked repos_mask = {} - client_plg_id = etpConst['system_settings_plugins_ids']['client_plugin'] - client_settings = self._settings.get(client_plg_id, {}) - if client_settings: - repos_mask = client_settings['repositories']['mask'] + clset = self._client_settings + if clset: + repos_mask = clset['repositories']['mask'] repomask = repos_mask.get(self.name) if isinstance(repomask, (list, set, frozenset)): @@ -2376,8 +2393,7 @@ class MaskableRepository(EntropyRepositoryBase): myr = ref['repository_packages_db_mask'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ -1, myr except KeyError: # system settings client plugin not found @@ -2401,8 +2417,8 @@ class MaskableRepository(EntropyRepositoryBase): ref = self._settings['pkg_masking_reference'] myr = ref['user_license_mask'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = -1, myr except KeyError: # system settings client plugin not found pass @@ -2426,9 +2442,8 @@ class MaskableRepository(EntropyRepositoryBase): if same_keywords: myr = mask_ref['system_keyword'] try: - - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr @@ -2456,8 +2471,8 @@ class MaskableRepository(EntropyRepositoryBase): # all packages in this repo with keyword "keyword" are ok myr = mask_ref['user_repo_package_keywords_all'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr except KeyError: # system settings client plugin not found @@ -2483,8 +2498,8 @@ class MaskableRepository(EntropyRepositoryBase): myr = mask_ref['user_repo_package_keywords'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr except KeyError: # system settings client plugin not found @@ -2526,8 +2541,8 @@ class MaskableRepository(EntropyRepositoryBase): # valid! myr = mask_ref['user_package_keywords'] try: - cl_data = self._settings[_CL_PLUGIN_ID] - validator_cache = cl_data['masking_validation']['cache'] + clset = self._client_settings + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr except KeyError: # system settings client plugin not found @@ -2541,12 +2556,13 @@ class MaskableRepository(EntropyRepositoryBase): ## package keywords # check if repository contains keyword unmasking data - cl_data = self._settings.get(_CL_PLUGIN_ID) - if cl_data is None: + clset = self._client_settings + if clset is None: # SystemSettings Entropy Client plugin not available return + # let's see if something is available in repository config - repo_keywords = cl_data['repositories']['repos_keywords'].get( + repo_keywords = clset['repositories']['repos_keywords'].get( self.name) if repo_keywords is None: # nopers, sorry! @@ -2557,7 +2573,7 @@ class MaskableRepository(EntropyRepositoryBase): if same_keywords: # universal keyword matches! myr = mask_ref['repository_packages_db_keywords'] - validator_cache = cl_data['masking_validation']['cache'] + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr return package_id, myr @@ -2594,7 +2610,7 @@ class MaskableRepository(EntropyRepositoryBase): if same_keywords: # found! this pkg is not masked, yay! myr = mask_ref['repository_packages_db_keywords'] - validator_cache = cl_data['masking_validation']['cache'] + validator_cache = clset['masking_validation']['cache'] validator_cache[(package_id, self.name, live)] = \ package_id, myr return package_id, myr @@ -2603,7 +2619,7 @@ class MaskableRepository(EntropyRepositoryBase): """ Reimplemented from EntropyRepositoryBase """ - validator_cache = self._settings.get(_CL_PLUGIN_ID, {}).get( + validator_cache = self._client_settings.get( 'masking_validation', {}).get('cache', {}) cached = validator_cache.get((package_id, self.name, live)) @@ -2663,7 +2679,8 @@ class AvailablePackagesRepository(CachedRepository, MaskableRepository): to make possible to update the repository. """ def __init__(self, *args, **kwargs): - EntropyRepository.__init__(self, *args, **kwargs) + super(AvailablePackagesRepository, self).__init__(*args, **kwargs) + # ensure proper repository file permissions if entropy.tools.is_root() and os.path.isfile(self._db): const_setup_file(self._db, etpConst['entropygid'], 0o644, @@ -2740,8 +2757,15 @@ class AvailablePackagesRepository(CachedRepository, MaskableRepository): def clearCache(self): # clear package masking filter - cl_data = self._settings.get(_CL_PLUGIN_ID, {}) - cl_data.get('masking_validation', {}).get('cache', {}).clear() + try: + clset = self._client_settings + except AttributeError: + # EntropyBaseRepository constructor calls + # _maybeDatabaseSchemaUpdates that calls us here + # while _real_client_settings is not set yet. + pass + else: + clset.get('masking_validation', {}).get('cache', {}).clear() EntropyRepository.clearCache(self) diff --git a/lib/entropy/client/interfaces/loaders.py b/lib/entropy/client/interfaces/loaders.py index 3db9db694..59ee6dfe5 100644 --- a/lib/entropy/client/interfaces/loaders.py +++ b/lib/entropy/client/interfaces/loaders.py @@ -18,6 +18,7 @@ from entropy.const import etpConst from entropy.qa import QAInterface from entropy.security import System from entropy.security import Repository as RepositorySecurity +from entropy.client.interfaces.settings import ClientSystemSettingsPlugin class LoadersMixin: @@ -167,7 +168,7 @@ class LoadersMixin: """ Return SystemSettings Entropy Client plugin metadata dictionary """ - p_id = etpConst['system_settings_plugins_ids']['client_plugin'] + p_id = ClientSystemSettingsPlugin.ID return self._settings[p_id] def Cacher(self): diff --git a/lib/entropy/client/interfaces/settings.py b/lib/entropy/client/interfaces/settings.py new file mode 100644 index 000000000..d7c6f49ff --- /dev/null +++ b/lib/entropy/client/interfaces/settings.py @@ -0,0 +1,693 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Package Manager Client Settings module}. + +""" +import codecs +import os + +from entropy.const import etpConst, const_file_readable, \ + const_convert_to_unicode +from entropy.core.settings.plugins.skel import SystemSettingsPlugin +from entropy.core.settings.base import SystemSettings + +from entropy.exceptions import SystemDatabaseError, RepositoryError + +import entropy.dep +import entropy.tools + + +class ClientSystemSettingsPlugin(SystemSettingsPlugin): + + ID = etpConst['system_settings_plugins_ids']['client_plugin'] + + def __init__(self, helper_interface): + SystemSettingsPlugin.__init__( + self, self.ID, helper_interface) + self.__repos_files = {} + self.__repos_mtime = {} + self._mtime_cache = {} + # Package repositories must be able to live across + # SystemSettings.clear() calls, because they are very + # special and 3rd-party (but even Sulfur) tools expect to always + # have them there after having called Client.add_package_repository + self.__package_repositories = [] + self.__package_repositories_meta = {} + + @staticmethod + def client_conf_path(): + """ + Return current client.conf path, this takes into account the current + configuration files directory path (which is affected by "root" path + changes [default: /]) + """ + # path to /etc/entropy/server.conf (usually, depends on systemroot) + return os.path.join(etpConst['confdir'], "client.conf") + + def _add_package_repository(self, repository_id, repository_metadata): + """ + Internal method, used by Entropy Client. Add a package repository + to this SystemSettings plugins to make it able to live across + SystemSettings.clear() calls. + + @param repository_id: repository identifier + @type repository_id: string + @param repository_metadata: a dict object that will be merged + into SystemSettings['repositories'] when clear() will be called + @type repository_metadata: dict + @raise KeyError: if repository_id is already stored. + """ + if repository_id in self.__package_repositories: + raise KeyError("%s already added" % (repository_id,)) + self.__package_repositories.append(repository_id) + self.__package_repositories_meta[repository_id] = repository_metadata + + def _drop_package_repository(self, repository_id): + """ + Drop package repository previously added by calling + _add_package_repository() + + @param repository_id: repository identifier + @type repository_id: string + @raise KeyError: if repository_id is not available + """ + del self.__package_repositories_meta[repository_id] + self.__package_repositories.remove(repository_id) + + def __setup_repos_files(self, system_settings): + """ + This function collects available repositories configuration files + by filling internal dict() __repos_files and __repos_mtime. + + @param system_settings: SystemSettings instance + @type system_settings: instance of SystemSettings + @return: None + @rtype: None + """ + + self.__repos_mtime = { + 'repos_license_whitelist': {}, + 'repos_mask': {}, + 'repos_system_mask': {}, + 'repos_critical_updates': {}, + 'repos_keywords': {}, + } + self.__repos_files = { + 'repos_license_whitelist': {}, + 'repos_mask': {}, + 'repos_system_mask': {}, + 'repos_critical_updates': {}, + 'repos_keywords': {}, + } + + dmp_dir = etpConst['dumpstoragedir'] + avail_data = system_settings['repositories']['available'] + for repoid in system_settings['repositories']['order']: + + repo_data = avail_data[repoid] + if "__temporary__" in repo_data: + continue + + repos_mask_setting = {} + repos_mask_mtime = {} + repos_lic_wl_setting = {} + repos_lic_wl_mtime = {} + repos_sm_mask_setting = {} + repos_sm_mask_mtime = {} + confl_tagged = {} + repos_critical_updates_setting = {} + repos_critical_updates_mtime = {} + repos_keywords_setting = {} + repos_keywords_mtime = {} + + maskpath = os.path.join(repo_data['dbpath'], + etpConst['etpdatabasemaskfile']) + wlpath = os.path.join(repo_data['dbpath'], + etpConst['etpdatabaselicwhitelistfile']) + sm_path = os.path.join(repo_data['dbpath'], + etpConst['etpdatabasesytemmaskfile']) + critical_path = os.path.join(repo_data['dbpath'], + etpConst['etpdatabasecriticalfile']) + keywords_path = os.path.join(repo_data['dbpath'], + etpConst['etpdatabasekeywordsfile']) + + if const_file_readable(maskpath): + repos_mask_setting[repoid] = maskpath + repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \ + repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime" + + if const_file_readable(wlpath): + repos_lic_wl_setting[repoid] = wlpath + repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \ + repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \ + ".mtime" + + if const_file_readable(sm_path): + repos_sm_mask_setting[repoid] = sm_path + repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \ + repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \ + ".mtime" + + if const_file_readable(critical_path): + repos_critical_updates_setting[repoid] = critical_path + repos_critical_updates_mtime[repoid] = dmp_dir + "/repo_" + \ + repoid + "_" + etpConst['etpdatabasecriticalfile'] + \ + ".mtime" + + if const_file_readable(keywords_path): + repos_keywords_setting[repoid] = keywords_path + repos_keywords_mtime[repoid] = dmp_dir + "/repo_" + \ + repoid + "_" + etpConst['etpdatabasekeywordsfile'] + \ + ".mtime" + + self.__repos_files['repos_mask'].update(repos_mask_setting) + self.__repos_mtime['repos_mask'].update(repos_mask_mtime) + + self.__repos_files['repos_license_whitelist'].update( + repos_lic_wl_setting) + self.__repos_mtime['repos_license_whitelist'].update( + repos_lic_wl_mtime) + + self.__repos_files['repos_system_mask'].update( + repos_sm_mask_setting) + self.__repos_mtime['repos_system_mask'].update( + repos_sm_mask_mtime) + + self.__repos_files['repos_critical_updates'].update( + repos_critical_updates_setting) + self.__repos_mtime['repos_critical_updates'].update( + repos_critical_updates_mtime) + + self.__repos_files['repos_keywords'].update( + repos_keywords_setting) + self.__repos_mtime['repos_keywords'].update( + repos_keywords_mtime) + + def __generic_parser(self, filepath): + """ + Internal method. This is the generic file parser here. + + @param filepath: valid path + @type filepath: string + @return: raw text extracted from file + @rtype: list + """ + root = etpConst['systemroot'] + try: + mtime = os.path.getmtime(filepath) + except (OSError, IOError): + mtime = 0.0 + + cache_key = (root, filepath) + cache_obj = self._mtime_cache.get(cache_key) + if cache_obj is not None: + if cache_obj['mtime'] == mtime: + return cache_obj['data'] + + cache_obj = {'mtime': mtime,} + + enc = etpConst['conf_encoding'] + data = entropy.tools.generic_file_content_parser(filepath, + comment_tag = "##", encoding = enc) + if SystemSettings.DISK_DATA_CACHE: + cache_obj['data'] = data + self._mtime_cache[cache_key] = cache_obj + return data + + def __run_post_branch_migration_hooks(self, sys_settings_instance): + + # only root can do this + if os.getuid() != 0: + return + + old_branch_path = etpConst['etp_previous_branch_file'] + in_branch_upgrade_path = etpConst['etp_in_branch_upgrade_file'] + current_branch = sys_settings_instance['repositories']['branch'] + enc = etpConst['conf_encoding'] + + def write_current_branch(branch): + with codecs.open(old_branch_path, "w", encoding=enc) as old_brf: + old_brf.write(branch) + + def write_in_branch_upgrade(branch): + with codecs.open(in_branch_upgrade_path, "w", encoding=enc) as brf: + brf.write("in branch upgrade: %s" % (branch,)) + + if not os.path.isfile(old_branch_path): + write_current_branch(current_branch) + return + + with codecs.open(old_branch_path, "r", encoding=enc) as old_f: + old_branch = old_f.readline().strip() + + if old_branch == current_branch: # all fine, no need to run + return + + repos, err = self._helper._run_repositories_post_branch_switch_hooks( + old_branch, current_branch) + if not err: + write_in_branch_upgrade(current_branch) + write_current_branch(current_branch) + + def __run_post_branch_upgrade_hooks(self, sys_settings_instance): + + # only root can do this + if os.getuid() != 0: + return + + repos, errors = self._helper._run_repository_post_branch_upgrade_hooks( + pretend = True) + if not repos: + # no scripts to run + return + + # look for updates + # critical_updates = False is needed to avoid + # issues with metadata not being available + try: + outcome = self._helper.calculate_updates(critical_updates = False) + update, remove = outcome['update'], outcome['remove'] + fine, spm_fine = outcome['fine'], outcome['spm_fine'] + except (ValueError, SystemDatabaseError, RepositoryError,): + # RepositoryError is triggered when branch is hopped + # SystemDatabaseError is triggered when no client db is avail + # ValueError is triggered when repos are broken + update = 1 # foo! + + def delete_in_branch_upgrade(): + br_path = etpConst['etp_in_branch_upgrade_file'] + try: + os.remove(br_path) + except OSError: + pass + + # actually execute this only if + # there are no updates left + if not update: + self._helper._run_repository_post_branch_upgrade_hooks() + delete_in_branch_upgrade() + + def system_mask_parser(self, system_settings_instance): + + parser_data = {} + # match installed packages of system_mask + mask_installed = [] + mask_installed_keys = {} + while (self._helper.installed_repository() != None): + try: + self._helper.installed_repository().validate() + except SystemDatabaseError: + break + mc_cache = set() + repos_mask_list = self.__repositories_system_mask( + system_settings_instance) + m_list = repos_mask_list + system_settings_instance['system_mask'] + for atom in m_list: + m_ids, m_r = self._helper.installed_repository().atomMatch(atom, + multiMatch = True) + if m_r != 0: + continue + mykey = entropy.dep.dep_getkey(atom) + obj = mask_installed_keys.setdefault(mykey, set()) + for m_id in m_ids: + if m_id in mc_cache: + continue + mc_cache.add(m_id) + mask_installed.append(m_id) + obj.add(m_id) + break + + parser_data.update({ + 'repos_installed': mask_installed, + 'repos_installed_keys': mask_installed_keys, + }) + return parser_data + + def masking_validation_parser(self, system_settings_instance): + data = { + 'cache': {}, # package masking validation cache + } + return data + + def __repositories_repos_keywords(self, repo_keywords_path): + """ + Parser returning system packages mask metadata read from + packages.db.keywords file inside the repository directory. + This file contains maintainer supplied per-repository extra + package keywords. + """ + root = etpConst['systemroot'] + try: + mtime = os.path.getmtime(repo_keywords_path) + except (OSError, IOError): + mtime = 0.0 + + cache_key = (root, repo_keywords_path) + cache_obj = self._mtime_cache.get(cache_key) + if cache_obj is not None: + if cache_obj['mtime'] == mtime: + return cache_obj['data'] + + cache_obj = {'mtime': mtime,} + + data = { + # universal keywords: keywords added repository-wide to all + # the available packages (in repo). + 'universal': set(), + # per-package keywording, keys are atoms/dep (first line argument) + # values are provided keywords + 'packages': {}, + 'packages_ids': None, # reserved for entropy.db package validation + } + + enc = etpConst['conf_encoding'] + entries = entropy.tools.generic_file_content_parser( + repo_keywords_path, encoding = enc) + + # iterate over config file data + for entry in entries: + entry = entry.split() + if len(entry) == 1: + # universal keyword + item = entry[0] + if item == "**": + item = '' + data['universal'].add(item) + + elif len(entry) > 1: + # per package keyword + pkg = entry[0] + keywords = entry[1:] + obj = data['packages'].setdefault(pkg, set()) + obj.update(keywords) + + if SystemSettings.DISK_DATA_CACHE: + cache_obj['data'] = data + self._mtime_cache[cache_key] = cache_obj + return data + + def __repositories_system_mask(self, sys_settings_instance): + """ + Parser returning system packages mask metadata read from + packages.db.system_mask file inside the repository directory. + This file contains packages that should be always kept + installed, extending the already defined (in repository database) + set of atoms. + """ + system_mask = [] + enc = etpConst['conf_encoding'] + for repoid in self.__repos_files['repos_system_mask']: + filepath = self.__repos_files['repos_system_mask'][repoid] + mtimepath = self.__repos_mtime['repos_system_mask'][repoid] + sys_settings_instance.validate_entropy_cache( + filepath, mtimepath, repoid = repoid) + + entries = self.__generic_parser(filepath) + system_mask += [x for x in entries if x not in system_mask] + + return system_mask + + def repositories_parser(self, sys_settings_instance): + """ + Parser that generates repository settings metadata. + + @param sys_settings_instance: SystemSettings instance + @type sys_settings_instance: instance of SystemSettings + @return: parsed metadata + @rtype: dict + """ + + # add back repository metadata to SystemSettings['repositories'] + avail_data = sys_settings_instance['repositories']['available'] + for repository_id in self.__package_repositories: + if repository_id not in avail_data: + repodata = self.__package_repositories_meta[repository_id] + # if correct, this won't trigger a stack overflow + # add_repository calling SystemSettings.clear() I mean + added = self._helper.add_repository(repodata) + if not added: + raise ValueError("wtf? cannot add repository") + + # fill repositories metadata dictionaries + self.__setup_repos_files(sys_settings_instance) + + data = { + 'license_whitelist': {}, + 'mask': {}, + 'system_mask': [], + 'critical_updates': {}, + 'repos_keywords': {}, + } + + # parse license whitelist + # Parser returning licenses considered accepted by default + # (= GPL compatibles) read from package.lic_whitelist. + for repoid in self.__repos_files['repos_license_whitelist']: + sys_settings_instance.validate_entropy_cache( + self.__repos_files['repos_license_whitelist'][repoid], + self.__repos_mtime['repos_license_whitelist'][repoid], + repoid = repoid) + + data['license_whitelist'][repoid] = self.__generic_parser( + self.__repos_files['repos_license_whitelist'][repoid]) + + # package masking + # Parser returning packages masked at repository level read from + # packages.db.mask inside the repository database directory. + for repoid in self.__repos_files['repos_mask']: + sys_settings_instance.validate_entropy_cache( + self.__repos_files['repos_mask'][repoid], + self.__repos_mtime['repos_mask'][repoid], repoid = repoid) + + data['mask'][repoid] = self.__generic_parser( + self.__repos_files['repos_mask'][repoid]) + + # keywords masking + # Parser returning packages masked at repository level read from + # packages.db.keywords inside the repository database directory. + for repoid in self.__repos_files['repos_keywords']: + sys_settings_instance.validate_entropy_cache( + self.__repos_files['repos_keywords'][repoid], + self.__repos_mtime['repos_keywords'][repoid], + repoid = repoid) + + data['repos_keywords'][repoid] = \ + self.__repositories_repos_keywords( + self.__repos_files['repos_keywords'][repoid]) + + # system masking + data['system_mask'] = self.__repositories_system_mask( + sys_settings_instance) + + # critical updates + # Parser returning critical packages list metadata read from + # packages.db.critical file inside the repository directory. + # This file contains packages that should be always updated + # before anything else. + for repoid in self.__repos_files['repos_critical_updates']: + sys_settings_instance.validate_entropy_cache( + self.__repos_files['repos_critical_updates'][repoid], + self.__repos_mtime['repos_critical_updates'][repoid], + repoid = repoid) + + data['critical_updates'][repoid] = self.__generic_parser( + self.__repos_files['repos_critical_updates'][repoid]) + + return data + + def misc_parser(self, sys_settings_instance): + + """ + Parses Entropy client system configuration file. + + @return dict data + """ + + data = { + 'filesbackup': etpConst['filesbackup'], + 'forcedupdates': etpConst['forcedupdates'], + 'packagehashes': etpConst['packagehashes'], + 'gpg': etpConst['client_gpg'], + 'ignore_spm_downgrades': False, + 'splitdebug': etpConst['splitdebug'], + 'splitdebug_dirs': etpConst['splitdebug_dirs'], + 'multifetch': 1, + 'collisionprotect': etpConst['collisionprotect'], + 'configprotect': set(), + 'configprotectmask': set(), + 'configprotectskip': set(), + 'autoprune_days': None, # disabled by default + 'edelta_support': False, # disabled by default + } + + cli_conf = ClientSystemSettingsPlugin.client_conf_path() + root = etpConst['systemroot'] + try: + mtime = os.path.getmtime(cli_conf) + except (OSError, IOError): + mtime = 0.0 + + cache_key = (root, cli_conf) + cache_obj = self._mtime_cache.get(cache_key) + if cache_obj is not None: + if cache_obj['mtime'] == mtime: + return cache_obj['data'] + + cache_obj = {'mtime': mtime,} + + if not const_file_readable(cli_conf): + if SystemSettings.DISK_DATA_CACHE: + cache_obj['data'] = data + self._mtime_cache[cache_key] = cache_obj + return data + + def _filesbackup(setting): + bool_setting = entropy.tools.setting_to_bool(setting) + if bool_setting is not None: + data['filesbackup'] = bool_setting + + def _forcedupdates(setting): + bool_setting = entropy.tools.setting_to_bool(setting) + if bool_setting is not None: + data['forcedupdates'] = bool_setting + + def _autoprune(setting): + int_setting = entropy.tools.setting_to_int(setting, 0, 365) + if int_setting is not None: + data['autoprune_days'] = int_setting + + def _packagesdelta(setting): + bool_setting = entropy.tools.setting_to_bool(setting) + if bool_setting is not None: + data['edelta_support'] = bool_setting + + def _packagehashes(setting): + setting = setting.lower().split() + hashes = set() + for opt in setting: + if opt in etpConst['packagehashes']: + hashes.add(opt) + if hashes: + data['packagehashes'] = tuple(sorted(hashes)) + + def _multifetch(setting): + int_setting = entropy.tools.setting_to_int(setting, None, None) + bool_setting = entropy.tools.setting_to_bool(setting) + if int_setting is not None: + if int_setting not in range(2, 11): + int_setting = 10 + data['multifetch'] = int_setting + if bool_setting is not None: + if bool_setting: + data['multifetch'] = 3 + + def _gpg(setting): + bool_setting = entropy.tools.setting_to_bool(setting) + if bool_setting is not None: + data['gpg'] = bool_setting + + def _spm_downgrades(setting): + bool_setting = entropy.tools.setting_to_bool(setting) + if bool_setting is not None: + data['ignore_spm_downgrades'] = bool_setting + + def _splitdebug(setting): + bool_setting = entropy.tools.setting_to_bool(setting) + if bool_setting is not None: + data['splitdebug'] = bool_setting + + def _collisionprotect(setting): + int_setting = entropy.tools.setting_to_int(setting, 0, 2) + if int_setting is not None: + data['collisionprotect'] = int_setting + + def _configprotect(setting): + for opt in setting.split(): + data['configprotect'].add(const_convert_to_unicode(opt)) + + def _configprotectmask(setting): + for opt in setting.split(): + data['configprotectmask'].add(const_convert_to_unicode(opt)) + + def _configprotectskip(setting): + for opt in setting.split(): + data['configprotectskip'].add( + etpConst['systemroot'] + const_convert_to_unicode(opt)) + + settings_map = { + # backward compatibility + 'filesbackup': _filesbackup, + 'files-backup': _filesbackup, + # backward compatibility + 'forcedupdates': _forcedupdates, + 'forced-updates': _forcedupdates, + 'packages-autoprune-days': _autoprune, + 'packages-delta': _packagesdelta, + # backward compatibility + 'packagehashes': _packagehashes, + 'package-hashes': _packagehashes, + 'multifetch': _multifetch, + 'gpg': _gpg, + 'ignore-spm-downgrades': _spm_downgrades, + 'splitdebug': _splitdebug, + # backward compatibility + 'collisionprotect': _collisionprotect, + 'collision-protect': _collisionprotect, + # backward compatibility + 'configprotect': _configprotect, + 'config-protect': _configprotect, + # backward compatibility + 'configprotectmask': _configprotectmask, + 'config-protect-mask': _configprotectmask, + # backward compatibility + 'configprotectskip': _configprotectskip, + 'config-protect-skip': _configprotectskip, + } + + enc = etpConst['conf_encoding'] + with codecs.open(cli_conf, "r", encoding=enc) as client_f: + clientconf = [x.strip() for x in client_f.readlines() if \ + x.strip() and not x.strip().startswith("#")] + for line in clientconf: + + key, value = entropy.tools.extract_setting(line) + if key is None: + continue + + func = settings_map.get(key) + if func is None: + continue + func(value) + + # completely disable GPG feature + if not data['gpg'] and ("gpg" in data['packagehashes']): + data['packagehashes'] = tuple((x for x in data['packagehashes'] \ + if x != "gpg")) + + # support ETP_SPLITDEBUG + split_debug = os.getenv("ETP_SPLITDEBUG") + if split_debug is not None: + _splitdebug(split_debug) + + if SystemSettings.DISK_DATA_CACHE: + cache_obj['data'] = data + self._mtime_cache[cache_key] = cache_obj + return data + + def post_setup(self, system_settings_instance): + """ + Reimplemented from SystemSettingsPlugin. + """ + + if self._helper._can_run_sys_set_hooks: + # run post-branch migration scripts if branch setting got changed + self.__run_post_branch_migration_hooks(system_settings_instance) + # run post-branch upgrade migration scripts if the function + # above created migration files to handle + self.__run_post_branch_upgrade_hooks(system_settings_instance) diff --git a/lib/tests/security.py b/lib/tests/security.py index e2387bf14..e930756d0 100644 --- a/lib/tests/security.py +++ b/lib/tests/security.py @@ -56,7 +56,8 @@ class SecurityTest(unittest.TestCase): def test_security_cache(self): - cacher = EntropyCacher() + cacher = self._entropy._cacher + # this starts the cacher as well cache_key = "zomg" data = {"1": 1, "2": 2}