800 lines
30 KiB
Python
800 lines
30 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
|
|
@author: Fabio Erculiani <lxnay@sabayonlinux.org>
|
|
@contact: lxnay@sabayonlinux.org
|
|
@copyright: Fabio Erculiani
|
|
@license: GPL-2
|
|
|
|
B{Entropy Package Manager Client Core Interface}.
|
|
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from entropy.core import Singleton
|
|
from entropy.output import TextInterface, bold, red, darkred, blue
|
|
from entropy.db import dbapi2
|
|
from entropy.client.interfaces.loaders import LoadersMixin
|
|
from entropy.client.interfaces.cache import CacheMixin
|
|
from entropy.client.interfaces.dep import CalculatorsMixin
|
|
from entropy.client.interfaces.methods import RepositoryMixin, MiscMixin, \
|
|
MatchMixin
|
|
from entropy.client.interfaces.fetch import FetchersMixin
|
|
from entropy.client.interfaces.noticeboard import NoticeBoardMixin
|
|
from entropy.const import etpConst, etpCache, etpUi, const_debug_write, \
|
|
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.i18n import _
|
|
|
|
class ClientSystemSettingsPlugin(SystemSettingsPlugin):
|
|
|
|
import entropy.tools as entropyTools
|
|
|
|
def __init__(self, plugin_id, helper_interface):
|
|
SystemSettingsPlugin.__init__(self, plugin_id, helper_interface)
|
|
self.__repos_files = {}
|
|
self.__repos_mtime = {}
|
|
|
|
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': {},
|
|
'conflicting_tagged_packages': {},
|
|
'repos_critical_updates': {},
|
|
'repos_keywords': {},
|
|
}
|
|
|
|
dmp_dir = etpConst['dumpstoragedir']
|
|
for repoid in system_settings['repositories']['order']:
|
|
|
|
repos_mask_setting = {}
|
|
repos_mask_mtime = {}
|
|
repos_lic_wl_setting = {}
|
|
repos_lic_wl_mtime = {}
|
|
repo_data = system_settings['repositories']['available'][repoid]
|
|
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'])
|
|
ct_path = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabaseconflictingtaggedfile'])
|
|
critical_path = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabasecriticalfile'])
|
|
keywords_path = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabasekeywordsfile'])
|
|
|
|
if os.access(maskpath, os.R_OK) and os.path.isfile(maskpath):
|
|
repos_mask_setting[repoid] = maskpath
|
|
repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime"
|
|
|
|
if os.access(wlpath, os.R_OK) and os.path.isfile(wlpath):
|
|
repos_lic_wl_setting[repoid] = wlpath
|
|
repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \
|
|
".mtime"
|
|
|
|
if os.access(sm_path, os.R_OK) and os.path.isfile(sm_path):
|
|
repos_sm_mask_setting[repoid] = sm_path
|
|
repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \
|
|
".mtime"
|
|
if os.access(ct_path, os.R_OK) and os.path.isfile(ct_path):
|
|
confl_tagged[repoid] = ct_path
|
|
|
|
if os.access(critical_path, os.R_OK) and \
|
|
os.path.isfile(critical_path):
|
|
repos_critical_updates_setting[repoid] = critical_path
|
|
repos_critical_updates_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabasecriticalfile'] + \
|
|
".mtime"
|
|
|
|
if os.access(keywords_path, os.R_OK) and \
|
|
os.path.isfile(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['conflicting_tagged_packages'].update(
|
|
confl_tagged)
|
|
|
|
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 __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']
|
|
|
|
def write_current_branch(branch):
|
|
old_brf = open(old_branch_path, "w")
|
|
old_brf.write(branch)
|
|
old_brf.flush()
|
|
old_brf.close()
|
|
|
|
def write_in_branch_upgrade(branch):
|
|
brf = open(in_branch_upgrade_path, "w")
|
|
brf.write("in branch upgrade: %s" % (branch,))
|
|
brf.flush()
|
|
brf.close()
|
|
|
|
if not os.path.isfile(old_branch_path):
|
|
write_current_branch(current_branch)
|
|
return
|
|
|
|
old_f = open(old_branch_path, "r")
|
|
old_branch = old_f.readline().strip()
|
|
old_f.close()
|
|
|
|
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:
|
|
update, remove, fine, spm_fine = \
|
|
self._helper.calculate_world_updates(
|
|
critical_updates = False)
|
|
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']
|
|
if os.access(br_path, os.W_OK) and os.path.isfile(br_path):
|
|
os.remove(br_path)
|
|
|
|
# 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.clientDbconn != None):
|
|
try:
|
|
self._helper.clientDbconn.validateDatabase()
|
|
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.clientDbconn.atomMatch(atom,
|
|
multiMatch = True)
|
|
if m_r != 0:
|
|
continue
|
|
mykey = self.entropyTools.dep_getkey(atom)
|
|
if mykey not in mask_installed_keys:
|
|
mask_installed_keys[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)
|
|
mask_installed_keys[mykey].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.
|
|
"""
|
|
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
|
|
}
|
|
|
|
entries = self.entropyTools.generic_file_content_parser(
|
|
repo_keywords_path)
|
|
|
|
# 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)
|
|
|
|
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 = []
|
|
for repoid in self.__repos_files['repos_system_mask']:
|
|
sys_settings_instance.validate_entropy_cache(
|
|
self.__repos_files['repos_system_mask'][repoid],
|
|
self.__repos_mtime['repos_system_mask'][repoid],
|
|
repoid = repoid)
|
|
system_mask += [x for x in \
|
|
self.entropyTools.generic_file_content_parser(
|
|
self.__repos_files['repos_system_mask'][repoid]) 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
|
|
"""
|
|
|
|
# fill repositories metadata dictionaries
|
|
self.__setup_repos_files(sys_settings_instance)
|
|
|
|
data = {
|
|
'license_whitelist': {},
|
|
'mask': {},
|
|
'system_mask': [],
|
|
'critical_updates': {},
|
|
'conflicting_tagged_packages': {},
|
|
'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.entropyTools.generic_file_content_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.entropyTools.generic_file_content_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.entropyTools.generic_file_content_parser(
|
|
self.__repos_files['repos_critical_updates'][repoid])
|
|
|
|
|
|
# conflicts map
|
|
"""
|
|
Parser returning packages that could have been installed because
|
|
they aren't in the same scope, but ending up creating critical
|
|
issues. You can see it as a configurable conflict map.
|
|
"""
|
|
# keep priority order
|
|
repoids = [x for x in sys_settings_instance['repositories']['order'] \
|
|
if x in self.__repos_files['conflicting_tagged_packages']]
|
|
for repoid in repoids:
|
|
filepath = self.__repos_files['conflicting_tagged_packages'].get(
|
|
repoid)
|
|
if os.access(filepath, os.R_OK) and os.path.isfile(filepath):
|
|
confl_f = open(filepath, "r")
|
|
content = confl_f.readlines()
|
|
confl_f.close()
|
|
content = [x.strip().rsplit("#", 1)[0].strip().split() for x \
|
|
in content if not x.startswith("#") and x.strip()]
|
|
for mydata in content:
|
|
if len(mydata) < 2:
|
|
continue
|
|
data['conflicting_tagged_packages'][mydata[0]] = mydata[1:]
|
|
|
|
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'],
|
|
'ignore_spm_downgrades': etpConst['spm']['ignore-spm-downgrades'],
|
|
'collisionprotect': etpConst['collisionprotect'],
|
|
'configprotect': etpConst['configprotect'][:],
|
|
'configprotectmask': etpConst['configprotectmask'][:],
|
|
'configprotectskip': etpConst['configprotectskip'][:],
|
|
}
|
|
|
|
cli_conf = etpConst['clientconf']
|
|
if not (os.path.isfile(cli_conf) and os.access(cli_conf, os.R_OK)):
|
|
return data
|
|
|
|
client_f = open(cli_conf, "r")
|
|
clientconf = [x.strip() for x in client_f.readlines() if \
|
|
x.strip() and not x.strip().startswith("#")]
|
|
client_f.close()
|
|
for line in clientconf:
|
|
|
|
split_line = line.split("|")
|
|
split_line_len = len(split_line)
|
|
|
|
if line.startswith("filesbackup|") and (split_line_len == 2):
|
|
|
|
compatopt = split_line[1].strip().lower()
|
|
if compatopt in ("disable", "disabled", "false", "0", "no",):
|
|
data['filesbackup'] = False
|
|
|
|
elif line.startswith("forcedupdates|") and (split_line_len == 2):
|
|
|
|
compatopt = split_line[1].strip().lower()
|
|
if compatopt in ("disable", "disabled", "false", "0", "no",):
|
|
data['forcedupdates'] = False
|
|
else:
|
|
data['forcedupdates'] = True
|
|
|
|
elif line.startswith("packagehashes|") and (split_line_len == 2):
|
|
|
|
opts = split_line[1].strip().lower().split()
|
|
hashes = []
|
|
for opt in opts:
|
|
if opt in etpConst['packagehashes']:
|
|
hashes.append(opt)
|
|
if hashes:
|
|
data['packagehashes'] = tuple(hashes)
|
|
|
|
elif line.startswith("ignore-spm-downgrades|") and \
|
|
(split_line_len == 2):
|
|
|
|
compatopt = split_line[1].strip().lower()
|
|
if compatopt in ("enable", "enabled", "true", "1", "yes"):
|
|
data['ignore_spm_downgrades'] = True
|
|
|
|
elif line.startswith("collisionprotect|") and (split_line_len == 2):
|
|
|
|
collopt = split_line[1].strip()
|
|
if collopt.lower() in ("0", "1", "2",):
|
|
data['collisionprotect'] = int(collopt)
|
|
|
|
elif line.startswith("configprotect|") and (split_line_len == 2):
|
|
|
|
configprotect = split_line[1].strip()
|
|
for myprot in configprotect.split():
|
|
data['configprotect'].append(
|
|
const_convert_to_unicode(myprot))
|
|
|
|
elif line.startswith("configprotectmask|") and \
|
|
(split_line_len == 2):
|
|
|
|
configprotect = split_line[1].strip()
|
|
for myprot in configprotect.split():
|
|
data['configprotectmask'].append(
|
|
const_convert_to_unicode(myprot))
|
|
|
|
elif line.startswith("configprotectskip|") and \
|
|
(split_line_len == 2):
|
|
|
|
configprotect = split_line[1].strip()
|
|
for myprot in configprotect.split():
|
|
data['configprotectskip'].append(
|
|
etpConst['systemroot'] + \
|
|
const_convert_to_unicode(myprot))
|
|
|
|
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, MatchMixin, FetchersMixin, NoticeBoardMixin):
|
|
|
|
def init_singleton(self, indexing = True, noclientdb = 0,
|
|
xcache = True, user_xcache = False, repo_validation = True,
|
|
load_ugc = True, url_fetcher = None,
|
|
multiple_url_fetcher = None):
|
|
|
|
self._treeupdates_repos = set()
|
|
self._can_run_sys_set_hooks = False
|
|
const_debug_write(__name__, "debug enabled")
|
|
self.sys_settings_client_plugin_id = \
|
|
etpConst['system_settings_plugins_ids']['client_plugin']
|
|
self.__instance_destroyed = False
|
|
self.atomMatchCacheKey = etpCache['atomMatch']
|
|
self.dbapi2 = dbapi2 # export for third parties
|
|
self.FileUpdates = None
|
|
self.validRepositories = []
|
|
self.UGC = None
|
|
# supporting external updateProgress stuff, you can point self.progress
|
|
# to your progress bar and reimplement updateProgress
|
|
self.progress = None
|
|
self.clientDbconn = None
|
|
self.safe_mode = 0
|
|
self.indexing = indexing
|
|
self.repo_validation = repo_validation
|
|
self.noclientdb = False
|
|
self.openclientdb = True
|
|
|
|
# setup package settings (masking and other stuff)
|
|
self.SystemSettings = SystemSettings()
|
|
const_debug_write(__name__, "SystemSettings loaded")
|
|
|
|
# modules import
|
|
import entropy.dump as dumpTools
|
|
import entropy.tools as entropyTools
|
|
self.dumpTools = dumpTools
|
|
self.entropyTools = entropyTools
|
|
self.clientLog = LogFile(level = self.SystemSettings['system']['log_level'],
|
|
filename = etpConst['equologfile'], header = "[client]")
|
|
|
|
self.MultipleUrlFetcher = multiple_url_fetcher
|
|
self.urlFetcher = url_fetcher
|
|
if self.urlFetcher == None:
|
|
from entropy.transceivers import UrlFetcher
|
|
self.urlFetcher = UrlFetcher
|
|
if self.MultipleUrlFetcher == None:
|
|
from entropy.transceivers import MultipleUrlFetcher
|
|
self.MultipleUrlFetcher = MultipleUrlFetcher
|
|
|
|
from entropy.cache import EntropyCacher
|
|
self.Cacher = EntropyCacher()
|
|
|
|
from entropy.client.misc import FileUpdates
|
|
self.FileUpdates = FileUpdates(self)
|
|
|
|
from entropy.client.mirrors import StatusInterface
|
|
# mirror status interface
|
|
self.MirrorStatus = StatusInterface()
|
|
|
|
if noclientdb in (False, 0):
|
|
self.noclientdb = False
|
|
elif noclientdb in (True, 1):
|
|
self.noclientdb = True
|
|
elif noclientdb == 2:
|
|
self.noclientdb = True
|
|
self.openclientdb = False
|
|
|
|
# load User Generated Content Interface
|
|
if load_ugc:
|
|
from entropy.client.services.ugc.interfaces import Client as ugc_cl
|
|
self.UGC = ugc_cl(self)
|
|
|
|
# class init
|
|
LoadersMixin.__init__(self)
|
|
|
|
self.xcache = xcache
|
|
shell_xcache = os.getenv("ETP_NOCACHE")
|
|
if shell_xcache:
|
|
self.xcache = False
|
|
|
|
do_validate_repo_cache = False
|
|
# now if we are on live, we should disable it
|
|
# are we running on a livecd? (/proc/cmdline has "cdroot")
|
|
if self.entropyTools.islive():
|
|
self.xcache = False
|
|
elif (not self.entropyTools.is_user_in_entropy_group()) and not user_xcache:
|
|
self.xcache = False
|
|
elif not user_xcache:
|
|
do_validate_repo_cache = True
|
|
|
|
if not self.xcache and (self.entropyTools.is_user_in_entropy_group()):
|
|
try:
|
|
self.purge_cache(False)
|
|
except:
|
|
pass
|
|
|
|
if self.openclientdb:
|
|
self.open_client_repository()
|
|
|
|
# create our SystemSettings plugin
|
|
self.sys_settings_client_plugin = ClientSystemSettingsPlugin(
|
|
self.sys_settings_client_plugin_id, self)
|
|
|
|
# needs to be started here otherwise repository cache will be
|
|
# always dropped
|
|
if self.xcache:
|
|
self.Cacher.start()
|
|
|
|
if do_validate_repo_cache:
|
|
self.validate_repositories_cache()
|
|
|
|
if self.repo_validation:
|
|
self.validate_repositories()
|
|
else:
|
|
self.validRepositories.extend(
|
|
self.SystemSettings['repositories']['order'])
|
|
|
|
# add our SystemSettings plugin
|
|
# Make sure we connect Entropy Client plugin AFTER client db init
|
|
self.SystemSettings.add_plugin(self.sys_settings_client_plugin)
|
|
|
|
# enable System Settings hooks
|
|
self._can_run_sys_set_hooks = True
|
|
const_debug_write(__name__, "singleton loaded")
|
|
|
|
|
|
def destroy(self):
|
|
self.__instance_destroyed = True
|
|
if hasattr(self, 'clientDbconn'):
|
|
if self.clientDbconn != None:
|
|
self.clientDbconn.closeDB()
|
|
del self.clientDbconn
|
|
if hasattr(self, 'FileUpdates'):
|
|
del self.FileUpdates
|
|
if hasattr(self, 'clientLog'):
|
|
self.clientLog.close()
|
|
if hasattr(self, 'SystemSettings') and \
|
|
hasattr(self, 'sys_settings_client_plugin_id'):
|
|
|
|
if hasattr(self.SystemSettings, 'remove_plugin'):
|
|
try:
|
|
self.SystemSettings.remove_plugin(
|
|
self.sys_settings_client_plugin_id)
|
|
except KeyError:
|
|
pass
|
|
|
|
self.close_all_repositories(mask_clear = False)
|
|
self.closeAllSecurity()
|
|
self.closeAllQA()
|
|
|
|
def repository_packages_spm_sync(self, repository_identifier, repo_db,
|
|
force = False):
|
|
"""
|
|
Service method used to sync package names with Source Package Manager
|
|
via metadata stored in Repository dbs collected at server-time.
|
|
Source Package Manager can change package names, categories or slot
|
|
and Entropy repositories must be kept in sync.
|
|
|
|
In other words, it checks for /usr/portage/profiles/updates changes,
|
|
of course indirectly, since there is no way entropy.client can directly
|
|
depend on Portage.
|
|
|
|
@param repository_identifier: repository identifier which repo_db
|
|
parameter is bound
|
|
@type repository_identifier: string
|
|
@param repo_db: repository database instance
|
|
@type repo_db: entropy.db.EntropyRepository
|
|
@return: bool stating if changes have been made
|
|
@rtype: bool
|
|
"""
|
|
if not self.clientDbconn:
|
|
# nothing to do if client db is not availabe
|
|
return False
|
|
|
|
self._treeupdates_repos.add(repository_identifier)
|
|
|
|
doRescan = False
|
|
shell_rescan = os.getenv("ETP_TREEUPDATES_RESCAN")
|
|
if shell_rescan:
|
|
doRescan = True
|
|
|
|
# check database digest
|
|
stored_digest = repo_db.retrieveRepositoryUpdatesDigest(
|
|
repository_identifier)
|
|
if stored_digest == -1:
|
|
doRescan = True
|
|
|
|
# check stored value in client database
|
|
client_digest = "0"
|
|
if not doRescan:
|
|
client_digest = self.clientDbconn.retrieveRepositoryUpdatesDigest(
|
|
repository_identifier)
|
|
|
|
if doRescan or (str(stored_digest) != str(client_digest)) or force:
|
|
|
|
# reset database tables
|
|
self.clientDbconn.clearTreeupdatesEntries(repository_identifier)
|
|
|
|
# load updates
|
|
update_actions = repo_db.retrieveTreeUpdatesActions(
|
|
repository_identifier)
|
|
# now filter the required actions
|
|
update_actions = self.clientDbconn.filterTreeUpdatesActions(
|
|
update_actions)
|
|
|
|
if update_actions:
|
|
|
|
mytxt = "%s: %s." % (
|
|
bold(_("ATTENTION")),
|
|
red(_("forcing packages metadata update")),
|
|
)
|
|
self.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "info",
|
|
header = darkred(" * ")
|
|
)
|
|
mytxt = "%s %s." % (
|
|
red(_("Updating system database using repository")),
|
|
blue(repository_identifier),
|
|
)
|
|
self.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "info",
|
|
header = darkred(" * ")
|
|
)
|
|
# run stuff
|
|
self.clientDbconn.runTreeUpdatesActions(update_actions)
|
|
|
|
# store new digest into database
|
|
self.clientDbconn.setRepositoryUpdatesDigest(repository_identifier,
|
|
stored_digest)
|
|
# store new actions
|
|
self.clientDbconn.addRepositoryUpdatesActions(etpConst['clientdbid'],
|
|
update_actions, self.SystemSettings['repositories']['branch'])
|
|
self.clientDbconn.commitChanges()
|
|
# clear client cache
|
|
self.clientDbconn.clearCache()
|
|
return True
|
|
|
|
def is_destroyed(self):
|
|
return self.__instance_destroyed
|
|
|
|
def __del__(self):
|
|
self.destroy()
|
|
|