Files
entropy/libraries/entropy/core.py
2009-05-29 17:08:17 +02:00

1428 lines
49 KiB
Python

# -*- coding: utf-8 -*-
'''
# DESCRIPTION:
# Entropy Object Oriented Interface
Copyright (C) 2007-2009 Fabio Erculiani
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
'''
# pylint ~ ok
from __future__ import with_statement
import os
from entropy.exceptions import IncorrectParameter, SystemDatabaseError
from entropy.const import etpConst, etpUi, etpSys, const_setup_perms, \
const_secure_config_file, const_set_nice_level, \
const_extract_cli_repo_params, etpCache
from entropy.i18n import _
class Singleton(object):
"""
If your class wants to become a sexy Singleton,
subclass this and replace __init__ with init_singleton
"""
__is_destroyed = False
__is_singleton = True
def __new__(cls, *args, **kwds):
instance = cls.__dict__.get("__it__")
if instance != None:
if not instance.is_destroyed():
return instance
cls.__it__ = instance = object.__new__(cls)
instance.init_singleton(*args, **kwds)
return instance
def is_destroyed(self):
"""
In our world, Singleton instances may be destroyed,
this is done by setting a private bool var __is_destroyed
@return bool
"""
return self.__is_destroyed
def is_singleton(self):
"""
Return if the instance is a singleton
@return bool
"""
return self.__is_singleton
class SystemSettingsPlugin:
"""
This is a plugin base class for all SystemSettings plugins.
It allows to add extra parsers (though metadata) to
SystemSettings.
Just inherit from this class and call add_parser to add
your custom parsers.
SystemSettings will call the parse method, as explained below.
"""
def __init__(self, plugin_id, helper_interface):
"""
SystemSettingsPlugin constructor.
@param plugin_id -- plugin identifier, must be unique
@type plugin_id basestring
@param helper_interface -- any Python instance that could
be of help to your parsers
@type handler_instance instance
"""
self.__parsers = []
self.__plugin_id = plugin_id
self._helper = helper_interface
parser_postfix = "_parser"
for method in sorted(dir(self)):
if method == "add_parser":
continue
elif method.endswith(parser_postfix) and (method != parser_postfix):
parser_id = method[:len(parser_postfix)*-1]
self.__parsers.append((parser_id, getattr(self, method),))
def get_id(self):
"""
Returns the unique plugin id passed at construction time.
@return plugin identifier
"""
return self.__plugin_id
def add_parser(self, parser_id, parser_callable):
"""
You must call this method in order to add your custom
parsers to the plugin.
Please note, if your parser method ends with "_parser"
it will be automatically added this way:
method: foo_parser
parser_id => foo
method: another_fabulous_parser
parser_id => another_fabulous
@param parser_id -- parser identifier, must be unique
@type parser_id basestring
@param parser_callable -- any callable function which has
the following signature: callable(system_settings_instance)
can return True to stop further parsers calls
@type parser_callable callable
"""
self.__parsers.append((parser_id, parser_callable,))
def parse(self, system_settings_instance):
"""
This method is called by SystemSettings instance
when building its settings metadata.
Returned data from parser will be put into the SystemSettings
dict using plugin_id and parser_id keys.
If returned data is None, SystemSettings dict won't be changed.
@param system_settings_instance -- SystemSettings instance
@type system_settings_instance SystemSettings instance
"""
plugin_id = self.get_id()
for parser_id, parser in self.__parsers:
data = parser(system_settings_instance)
if data == None:
continue
if not system_settings_instance.has_key(plugin_id):
system_settings_instance[plugin_id] = {}
system_settings_instance[plugin_id][parser_id] = data
class SystemSettings(Singleton):
"""
This is the place where all the Entropy settings are stored if
they are not considered instance constants (etpConst).
For example, here we store package masking cache information and
settings.
Also, this class mimics a dictionary (even if not inheriting it
due to issues with the Singleton class).
"""
import entropy.tools as entropyTools
def init_singleton(self):
"""
Replaces __init__ because SystemSettings is a Singleton.
see Singleton API reference for more information.
"""
from entropy.cache import EntropyCacher
self.__cacher = EntropyCacher()
self.__data = {}
self.__is_destroyed = False
self.__plugins = {}
self.__setting_files_order = []
self.__setting_files_pre_run = []
self.__setting_files = {}
self.__mtime_files = {}
self.__persistent_settings = {
'pkg_masking_reasons': {
0: _('reason not available'),
1: _('user package.mask'),
2: _('system keywords'),
3: _('user package.unmask'),
4: _('user repo package.keywords (all packages)'),
5: _('user repo package.keywords'),
6: _('user package.keywords'),
7: _('completely masked'),
8: _('repository general packages.db.mask'),
10: _('user license.mask'),
11: _('user live unmask'),
12: _('user live mask'),
},
'pkg_masking_reference': {
'reason_not_avail': 0,
'user_package_mask': 1,
'system_keyword': 2,
'user_package_unmask': 3,
'user_repo_package_keywords_all': 4,
'user_repo_package_keywords': 5,
'user_package_keywords': 6,
'completely_masked': 7,
'repository_packages_db_mask': 8,
'repository_in_branch_pacakges_db_mask': 9,
'user_license_mask': 10,
'user_live_unmask': 11,
'user_live_mask': 12,
},
'backed_up': {},
# package masking, live
'live_packagemasking': {
'unmask_matches': set(),
'mask_matches': set(),
},
}
self.__setup_const()
self.__scan()
def destroy(self):
"""
Overloaded method from Singleton.
"Destroys" the instance.
@return None
"""
self.__is_destroyed = True
def add_plugin(self, system_settings_plugin_instance):
"""
This method lets you add custom parsers to SystemSettings.
Mind that you are responsible of handling your plugin instance
and remove it before it is destroyed. You can remove the plugin
instance at any time by issuing remove_plugin.
Every add_plugin or remove_plugin method will also issue clear()
for you. This could be bad and it might be removed in future.
@param plugin_id -- plugin identifier
@type plugin_id basestring
@param system_settings_plugin_instance -- valid SystemSettingsPlugin
instance
@type system_settings_plugin_instance SystemSettingsPlugin instance
"""
inst = system_settings_plugin_instance
if not isinstance(inst,SystemSettingsPlugin):
raise AttributeError("SystemSettings: expected valid " + \
"SystemSettingsPlugin instance")
self.__plugins[inst.get_id()] = inst
self.clear()
def remove_plugin(self, plugin_id):
"""
This method lets you remove previously added custom parsers from
SystemSettings through its plugin identifier. If plugin_id is not
available, KeyError exception will be raised.
Every add_plugin or remove_plugin method will also issue clear()
for you. This could be bad and it might be removed in future.
@param plugin_id -- plugin identifier
@type plugin_id basestring
"""
del self.__plugins[plugin_id]
self.clear()
def __setup_const(self):
"""
Internal method. Does the constants initialization.
@return None
"""
del self.__setting_files_order[:]
del self.__setting_files_pre_run[:]
self.__setting_files.clear()
self.__mtime_files.clear()
self.__setting_files.update({
# keywording configuration files
'keywords': etpConst['confpackagesdir']+"/package.keywords",
# unmasking configuration files
'unmask': etpConst['confpackagesdir']+"/package.unmask",
# masking configuration files
'mask': etpConst['confpackagesdir']+"/package.mask",
# masking configuration files
'license_mask': etpConst['confpackagesdir']+"/license.mask",
'repos_system_mask': {},
'system_mask': etpConst['confpackagesdir']+"/system.mask",
'repos_mask': {},
'repos_license_whitelist': {},
'system_package_sets': {},
'conflicting_tagged_packages': {},
'system_dirs': etpConst['confdir']+"/fsdirs.conf",
'system_dirs_mask': etpConst['confdir']+"/fsdirsmask.conf",
'system_rev_symlinks': etpConst['confdir']+"/fssymlinks.conf",
'hw_hash': etpConst['confdir']+"/.hw.hash",
'socket_service': etpConst['socketconf'],
'system': etpConst['entropyconf'],
'repositories': etpConst['repositoriesconf'],
})
self.__setting_files_order.extend([
'keywords', 'unmask', 'mask', 'license_mask',
'repos_system_mask', 'system_mask', 'repos_mask',
'repos_license_whitelist', 'system_package_sets',
'conflicting_tagged_packages', 'system_dirs',
'system_dirs_mask', 'socket_service', 'system',
'system_rev_symlinks', 'hw_hash'
])
self.__setting_files_pre_run.extend(['repositories'])
## XXX trunk support, for a while - exp. date 10/10/2009
trunk_fsdirs_conf = "../conf/fsdirs.conf"
trunk_fsdirsmask_conf = "../conf/fsdirsmask.conf"
trunk_fssymlinks_conf = "../conf/fssymlinks.conf"
if os.path.isfile(trunk_fsdirs_conf):
self.__setting_files['system_dirs'] = trunk_fsdirs_conf
if os.path.isfile(trunk_fsdirsmask_conf):
self.__setting_files['system_dirs_mask'] = trunk_fsdirsmask_conf
if os.path.isfile(trunk_fssymlinks_conf):
self.__setting_files['system_rev_symlinks'] = trunk_fssymlinks_conf
dmp_dir = etpConst['dumpstoragedir']
self.__mtime_files.update({
'keywords_mtime': dmp_dir+"/keywords.mtime",
'unmask_mtime': dmp_dir+"/unmask.mtime",
'mask_mtime': dmp_dir+"/mask.mtime",
'license_mask_mtime': dmp_dir+"/license_mask.mtime",
'system_mask_mtime': dmp_dir+"/system_mask.mtime",
'repos_system_mask': {},
'repos_mask': {},
'repos_license_whitelist': {},
})
def __scan(self):
"""
Internal method. Scan settings and fill variables.
@return None
"""
self.__parse()
# merge universal keywords
for keyword in self.__data['keywords']['universal']:
etpConst['keywords'].add(keyword)
# plugins support
for plugin_id in sorted(self.__plugins):
self.__plugins[plugin_id].parse(self)
# merge persistent settings back
self.__data.update(self.__persistent_settings)
# restore backed-up settings
self.__data.update(self.__persistent_settings['backed_up'].copy())
def __setitem__(self, mykey, myvalue):
"""
dict method. See Python dict API reference.
"""
if self.__persistent_settings.has_key(mykey): # backup here too
self.__persistent_settings[mykey] = myvalue
self.__data[mykey] = myvalue
def __getitem__(self, mykey):
"""
dict method. See Python dict API reference.
"""
return self.__data[mykey]
def __delitem__(self, mykey):
"""
dict method. See Python dict API reference.
"""
del self.__data[mykey]
def __iter__(self):
"""
dict method. See Python dict API reference.
"""
return iter(self.__data)
def __contains__(self, item):
"""
dict method. See Python dict API reference.
"""
return item in self.__data
def __cmp__(self, other):
"""
dict method. See Python dict API reference.
"""
return cmp(self.__data, other)
def __hash__(self):
"""
dict method. See Python dict API reference.
"""
return hash(self.__data)
def __len__(self):
"""
dict method. See Python dict API reference.
"""
return len(self.__data)
def get(self, mykey):
"""
dict method. See Python dict API reference.
"""
return self.__data.get(mykey)
def has_key(self, mykey):
"""
dict method. See Python dict API reference.
"""
return self.__data.has_key(mykey)
def copy(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.copy()
def fromkeys(self, seq, val = None):
"""
dict method. See Python dict API reference.
"""
return self.__data.fromkeys(seq, val)
def items(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.items()
def iteritems(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.iteritems()
def iterkeys(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.iterkeys()
def keys(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.keys()
def pop(self, mykey, default = None):
"""
dict method. See Python dict API reference.
"""
return self.__data.pop(mykey, default)
def popitem(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.popitem()
def setdefault(self, mykey, default = None):
"""
dict method. See Python dict API reference.
"""
return self.__data.setdefault(mykey, default)
def update(self, kwargs):
"""
dict method. See Python dict API reference.
"""
return self.__data.update(kwargs)
def values(self):
"""
dict method. See Python dict API reference.
"""
return self.__data.values()
def clear(self):
"""
dict method. See Python dict API reference.
Settings are also re-initialized here.
@return None
"""
self.__data.clear()
self.__setup_const()
self.__scan()
def set_persistent_setting(self, persistent_dict):
"""
Make metadata persistent, the input dict will be merged
with the base one at every reset call (clear()).
@param persistent_dict -- dictionary to merge
@type persistent_dict dict
@return None
"""
self.__persistent_settings.update(persistent_dict)
def unset_persistent_setting(self, persistent_key):
"""
Remove dict key from persistent dictionary
@param persistent_key -- key to remove
@type persistent_dict dict
@return None
"""
del self.__persistent_settings[persistent_key]
del self.__data[persistent_key]
def __setup_setting_vars(self):
"""
This function setups the *mtimes* and *files* dictionaries
that will be read and parsed afterwards by respective
internal parsers.
@return None
"""
dmp_dir = etpConst['dumpstoragedir']
for repoid in self['repositories']['order']:
repos_mask_setting = {}
repos_mask_mtime = {}
repos_lic_wl_setting = {}
repos_lic_wl_mtime = {}
repo_data = self['repositories']['available'][repoid]
repos_sm_mask_setting = {}
repos_sm_mask_mtime = {}
confl_tagged = {}
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'])
if os.path.isfile(maskpath) and os.access(maskpath, os.R_OK):
repos_mask_setting[repoid] = maskpath
repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \
repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime"
if os.path.isfile(wlpath) and os.access(wlpath, os.R_OK):
repos_lic_wl_setting[repoid] = wlpath
repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \
repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \
".mtime"
if os.path.isfile(sm_path) and os.access(sm_path, os.R_OK):
repos_sm_mask_setting[repoid] = sm_path
repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \
repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \
".mtime"
if os.path.isfile(ct_path) and os.access(ct_path, os.R_OK):
confl_tagged[repoid] = ct_path
self.__setting_files['repos_mask'].update(repos_mask_setting)
self.__mtime_files['repos_mask'].update(repos_mask_mtime)
self.__setting_files['repos_license_whitelist'].update(
repos_lic_wl_setting)
self.__mtime_files['repos_license_whitelist'].update(
repos_lic_wl_mtime)
self.__setting_files['repos_system_mask'].update(
repos_sm_mask_setting)
self.__mtime_files['repos_system_mask'].update(
repos_sm_mask_mtime)
self.__setting_files['conflicting_tagged_packages'].update(
confl_tagged)
def __setup_package_sets_vars(self):
"""
This function setups the *files* dictionary about package sets
that will be read and parsed afterwards by the respective
internal parser.
@return None
"""
# user defined package sets
sets_dir = etpConst['confsetsdir']
pkg_set_data = {}
if (os.path.isdir(sets_dir) and os.access(sets_dir, os.R_OK)):
set_files = [x for x in os.listdir(sets_dir) if \
(os.path.isfile(os.path.join(sets_dir, x)) and \
os.access(os.path.join(sets_dir, x),os.R_OK))]
for set_file in set_files:
try:
set_file = str(set_file)
except (UnicodeDecodeError, UnicodeEncodeError,):
continue
pkg_set_data[set_file] = os.path.join(sets_dir, set_file)
self.__setting_files['system_package_sets'].update(pkg_set_data)
def __parse(self):
"""
This is the main internal parsing method.
*files* and *mtimes* dictionaries are prepared and
parsed just a few lines later.
@return dict settings metadata
"""
# some parsers must be run BEFORE everything:
for item in self.__setting_files_pre_run:
myattr = '%s_parser' % (item,)
if not hasattr(self, myattr):
continue
func = getattr(self, myattr)
self.__data[item] = func()
# parse main settings
self.__setup_setting_vars()
self.__setup_package_sets_vars()
data = {}
for item in self.__setting_files_order:
myattr = '%s_parser' % (item,)
if not hasattr(self, myattr):
continue
func = getattr(self, myattr)
self.__data[item] = func()
def get_setting_files_data(self):
"""
Return a copy of the internal *files* dictionary.
This dict contains config file paths and their identifiers.
@return dict __setting_files
"""
return self.__setting_files.copy()
def get_mtime_files_data(self):
"""
Return a copy of the internal *mtime* dictionary.
This dict contains config file paths and their current mtime.
@return dict __mtime_files
"""
return self.__mtime_files.copy()
def keywords_parser(self):
"""
Parser returning package keyword masking metadata
read from package.keywords file.
This file contains package mask or unmask directives
based on package keywords.
@return dict data
"""
data = {
'universal': set(),
'packages': {},
'repositories': {},
}
self.__validate_entropy_cache(self.__setting_files['keywords'],
self.__mtime_files['keywords_mtime'])
content = [x.split() for x in \
self.__generic_parser(self.__setting_files['keywords']) \
if len(x.split()) < 4]
for keywordinfo in content:
# skip wrong lines
if len(keywordinfo) > 3:
continue
# inversal keywording, check if it's not repo=
if len(keywordinfo) == 1:
if keywordinfo[0].startswith("repo="):
continue
# convert into entropy format
if keywordinfo[0] == "**":
keywordinfo[0] = ""
data['universal'].add(keywordinfo[0])
continue
# inversal keywording, check if it's not repo=
if len(keywordinfo) in (2, 3,):
# repo=?
if keywordinfo[0].startswith("repo="):
continue
# add to repo?
items = keywordinfo[1:]
# convert into entropy format
if keywordinfo[0] == "**":
keywordinfo[0] = ""
reponame = [x for x in items if x.startswith("repo=") \
and (len(x.split("=")) == 2)]
if reponame:
reponame = reponame[0].split("=")[1]
if reponame not in data['repositories']:
data['repositories'][reponame] = {}
# repository unmask or package in repository unmask?
if keywordinfo[0] not in data['repositories'][reponame]:
data['repositories'][reponame][keywordinfo[0]] = set()
if len(items) == 1:
# repository unmask
data['repositories'][reponame][keywordinfo[0]].add('*')
elif "*" not in \
data['repositories'][reponame][keywordinfo[0]]:
item = [x for x in items if not x.startswith("repo=")]
data['repositories'][reponame][keywordinfo[0]].add(
item[0])
elif len(items) == 2:
# it's going to be a faulty line!!??
# can't have two items and no repo=
continue
else:
# add keyword to packages
if keywordinfo[0] not in data['packages']:
data['packages'][keywordinfo[0]] = set()
data['packages'][keywordinfo[0]].add(items[0])
return data
def unmask_parser(self):
"""
Parser returning package unmasking metadata read from
package.unmask file.
This file contains package unmask directives, allowing
to enable experimental or *secret* packages.
@return list parsed data
"""
self.__validate_entropy_cache(self.__setting_files['unmask'],
self.__mtime_files['unmask_mtime'])
return self.__generic_parser(self.__setting_files['unmask'])
def mask_parser(self):
"""
Parser returning package masking metadata read from
package.mask file.
This file contains package mask directives, allowing
to disable experimental or *secret* packages.
@return list parsed data
"""
self.__validate_entropy_cache(self.__setting_files['mask'],
self.__mtime_files['mask_mtime'])
return self.__generic_parser(self.__setting_files['mask'])
def system_mask_parser(self):
"""
Parser returning system packages mask metadata read from
package.system_mask file.
This file contains packages that should be always kept
installed, extending the already defined (in repository database)
set of atoms.
@return list parsed data
"""
self.__validate_entropy_cache(self.__setting_files['system_mask'],
self.__mtime_files['system_mask_mtime'])
return self.__generic_parser(self.__setting_files['system_mask'])
def license_mask_parser(self):
"""
Parser returning packages masked by license metadata read from
license.mask file.
Packages shipped with licenses listed there will be masked.
@return list parsed data
"""
self.__validate_entropy_cache(self.__setting_files['license_mask'],
self.__mtime_files['license_mask_mtime'])
return self.__generic_parser(self.__setting_files['license_mask'])
def repos_license_whitelist_parser(self):
"""
Parser returning licenses considered accepted by default
(= GPL compatibles) read from package.lic_whitelist.
@return dict parsed data
"""
data = {}
for repoid in self.__setting_files['repos_license_whitelist']:
self.__validate_entropy_cache(
self.__setting_files['repos_license_whitelist'][repoid],
self.__mtime_files['repos_license_whitelist'][repoid],
repoid = repoid)
data[repoid] = self.__generic_parser(
self.__setting_files['repos_license_whitelist'][repoid])
return data
def repos_mask_parser(self):
"""
Parser returning packages masked at repository level read from
packages.db.mask inside the repository database directory.
@return dict parsed data
"""
data = {}
for repoid in self.__setting_files['repos_mask']:
self.__validate_entropy_cache(
self.__setting_files['repos_mask'][repoid],
self.__mtime_files['repos_mask'][repoid], repoid = repoid)
data[repoid] = self.__generic_parser(
self.__setting_files['repos_mask'][repoid])
# why ? line = line.split()[0] in the previous one?
return data
def repos_system_mask_parser(self):
"""
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.
@return dict parsed data
"""
data = []
for repoid in self.__setting_files['repos_system_mask']:
self.__validate_entropy_cache(
self.__setting_files['repos_system_mask'][repoid],
self.__mtime_files['repos_system_mask'][repoid],
repoid = repoid)
data += [x for x in self.__generic_parser(
self.__setting_files['repos_system_mask'][repoid]) if x \
not in data]
# why ? line = line.split()[0] in the previous one?
return data
def system_package_sets_parser(self):
"""
Parser returning system defined package sets read from
/etc/entropy/packages/sets.
@return dict parsed data
"""
data = {}
for set_name in self.__setting_files['system_package_sets']:
set_filepath = self.__setting_files['system_package_sets'][set_name]
set_elements = self.entropyTools.extract_packages_from_set_file(
set_filepath)
if set_elements:
data[set_name] = set_elements.copy()
return data
def system_dirs_parser(self):
"""
Parser returning directories considered part of the base system.
@return list parsed data
"""
return self.__generic_parser(self.__setting_files['system_dirs'])
def system_dirs_mask_parser(self):
"""
Parser returning directories NOT considered part of the base system.
Settings here overlay system_dirs_parser.
@return list parsed data
"""
return self.__generic_parser(self.__setting_files['system_dirs_mask'])
def hw_hash_parser(self):
"""
Hardware hash metadata parser and generator. It returns a theorically
unique SHA256 hash bound to the computer running this Framework.
@return basestring containing SHA256 hexdigest
"""
hw_hash_file = self.__setting_files['hw_hash']
if os.access(hw_hash_file, os.R_OK | os.F_OK):
hash_f = open(hw_hash_file, "r")
hash_data = hash_f.readline().strip()
hash_f.close()
return hash_data
hash_file_dir = os.path.dirname(hw_hash_file)
hw_hash_exec = etpConst['etp_hw_hash_gen']
if os.access(hash_file_dir, os.W_OK) and \
os.access(hw_hash_exec, os.X_OK | os.F_OK | os.R_OK):
pipe = os.popen('{ ' + hw_hash_exec + '; } 2>&1', 'r')
hash_data = pipe.read().strip()
sts = pipe.close()
if sts != None:
return None
hash_f = open(hw_hash_file, "w")
hash_f.write(hash_data)
hash_f.flush()
hash_f.close()
return hash_data
def system_rev_symlinks_parser(self):
"""
Parser returning important system symlinks mapping. For example:
{'/usr/lib': ['/usr/lib64']}
Useful for reverse matching files belonging to packages.
@return dict containing the mapping
"""
setting_file = self.__setting_files['system_rev_symlinks']
raw_data = self.__generic_parser(setting_file)
data = {}
for line in raw_data:
line = line.split()
if len(line) < 2:
continue
data[line[0]] = frozenset(line[1:])
return data
def conflicting_tagged_packages_parser(self):
"""
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.
@return dict parsed data
"""
data = {}
# keep priority order
repoids = [x for x in self['repositories']['order'] if x in \
self.__setting_files['conflicting_tagged_packages']]
for repoid in repoids:
filepath = self.__setting_files['conflicting_tagged_packages'].get(
repoid)
if os.path.isfile(filepath) and os.access(filepath, os.R_OK):
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[mydata[0]] = mydata[1:]
return data
def socket_service_parser(self):
"""
Parses socket service configuration file.
This file contains information about Entropy remote service ports
and SSL.
@return dict data
"""
data = etpConst['socket_service'].copy()
sock_conf = self.__setting_files['socket_service']
if not (os.path.isfile(sock_conf) and \
os.access(sock_conf,os.R_OK)):
return data
socket_f = open(sock_conf,"r")
socketconf = [x.strip() for x in socket_f.readlines() if \
x.strip() and not x.strip().startswith("#")]
socket_f.close()
for line in socketconf:
split_line = line.split("|")
split_line_len = len(split_line)
if line.startswith("listen|") and (split_line_len > 1):
item = split_line[1].strip()
if item:
data['hostname'] = item
elif line.startswith("listen-port|") and \
(split_line_len > 1):
item = split_line[1].strip()
try:
item = int(item)
data['port'] = item
except ValueError:
continue
elif line.startswith("listen-timeout|") and \
(split_line_len > 1):
item = split_line[1].strip()
try:
item = int(item)
data['timeout'] = item
except ValueError:
continue
elif line.startswith("listen-threads|") and \
(split_line_len > 1):
item = split_line[1].strip()
try:
item = int(item)
data['threads'] = item
except ValueError:
continue
elif line.startswith("session-ttl|") and \
(split_line_len > 1):
item = split_line[1].strip()
try:
item = int(item)
data['session_ttl'] = item
except ValueError:
continue
elif line.startswith("max-connections|") and \
(split_line_len > 1):
item = split_line[1].strip()
try:
item = int(item)
data['max_connections'] = item
except ValueError:
continue
elif line.startswith("ssl-port|") and \
(split_line_len > 1):
item = split_line[1].strip()
try:
item = int(item)
data['ssl_port'] = item
except ValueError:
continue
elif line.startswith("disabled-commands|") and \
(split_line_len > 1):
disabled_cmds = split_line[1].strip().split()
for disabled_cmd in disabled_cmds:
data['disabled_cmds'].add(disabled_cmd)
elif line.startswith("ip-blacklist|") and \
(split_line_len > 1):
ips_blacklist = split_line[1].strip().split()
for ip_blacklist in ips_blacklist:
data['ip_blacklist'].add(ip_blacklist)
return data
def system_parser(self):
"""
Parses Entropy system configuration file.
@return dict data
"""
data = {}
data['proxy'] = etpConst['proxy'].copy()
data['name'] = etpConst['systemname']
data['log_level'] = etpConst['entropyloglevel']
etp_conf = self.__setting_files['system']
if not os.path.isfile(etp_conf) and \
os.access(etp_conf,os.R_OK):
return data
const_secure_config_file(etp_conf)
entropy_f = open(etp_conf,"r")
entropyconf = [x.strip() for x in entropy_f.readlines() if \
x.strip() and not x.strip().startswith("#")]
entropy_f.close()
for line in entropyconf:
split_line = line.split("|")
split_line_len = len(split_line)
if line.startswith("loglevel|") and \
(len(line.split("loglevel|")) == 2):
loglevel = line.split("loglevel|")[1]
try:
loglevel = int(loglevel)
except ValueError:
pass
if (loglevel > -1) and (loglevel < 3):
data['log_level'] = loglevel
elif line.startswith("ftp-proxy|") and \
(split_line_len == 2):
ftpproxy = split_line[1].strip().split()
if ftpproxy:
data['proxy']['ftp'] = ftpproxy[-1]
elif line.startswith("http-proxy|") and \
(split_line_len == 2):
httpproxy = split_line[1].strip().split()
if httpproxy:
data['proxy']['http'] = httpproxy[-1]
elif line.startswith("proxy-username|") and \
(split_line_len == 2):
httpproxy = split_line[1].strip().split()
if httpproxy:
data['proxy']['username'] = httpproxy[-1]
elif line.startswith("proxy-password|") and \
(split_line_len == 2):
httpproxy = split_line[1].strip().split()
if httpproxy:
data['proxy']['password'] = httpproxy[-1]
elif line.startswith("system-name|") and \
(split_line_len == 2):
data['name'] = split_line[1].strip()
elif line.startswith("nice-level|") and \
(split_line_len == 2):
mylevel = split_line[1].strip()
try:
mylevel = int(mylevel)
if (mylevel >= -19) and (mylevel <= 19):
const_set_nice_level(mylevel)
except (ValueError,):
continue
return data
def repositories_parser(self):
"""
Setup Entropy Client repository settings reading them from
the relative config file specified in etpConst['repositoriesconf']
@return None
"""
data = {
'available': {},
'excluded': {},
'order': [],
'product': etpConst['product'],
'branch': etpConst['branch'],
'default_repository': etpConst['officialrepositoryid'],
'transfer_limit': etpConst['downloadspeedlimit'],
'security_advisories_url': etpConst['securityurl'],
}
repo_conf = etpConst['repositoriesconf']
if not (os.path.isfile(repo_conf) and os.access(repo_conf, os.R_OK)):
return data
repo_f = open(repo_conf,"r")
repositoriesconf = [x.strip() for x in repo_f.readlines() if x.strip()]
repo_f.close()
# setup product and branch first
for line in repositoriesconf:
split_line = line.split("|")
split_line_len = len(split_line)
if (line.find("product|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
data['product'] = split_line[1]
elif (line.find("branch|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
branch = split_line[1].strip()
data['branch'] = branch
if not os.path.isdir(etpConst['packagesbindir']+"/"+branch) \
and (etpConst['uid'] == 0):
try:
os.makedirs(etpConst['packagesbindir']+"/"+branch)
except (OSError, IOError,):
continue
for line in repositoriesconf:
split_line = line.split("|")
split_line_len = len(split_line)
# populate data['available']
if (line.find("repository|") != -1) and (split_line_len == 5):
excluded = False
my_repodata = data['available']
if line.startswith("##"):
continue
elif line.startswith("#"):
excluded = True
my_repodata = data['excluded']
line = line[1:]
reponame, repodata = const_extract_cli_repo_params(line,
data['branch'], data['product'])
if my_repodata.has_key(reponame):
my_repodata[reponame]['plain_packages'].extend(
repodata['plain_packages'])
my_repodata[reponame]['packages'].extend(
repodata['packages'])
if (not my_repodata[reponame]['plain_database']) and \
repodata['plain_database']:
my_repodata[reponame]['plain_database'] = \
repodata['plain_database']
my_repodata[reponame]['database'] = \
repodata['database']
my_repodata[reponame]['dbrevision'] = \
repodata['dbrevision']
my_repodata[reponame]['dbcformat'] = \
repodata['dbcformat']
else:
my_repodata[reponame] = repodata.copy()
if not excluded:
data['order'].append(reponame)
elif (line.find("officialrepositoryid|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
officialreponame = split_line[1]
data['default_repository'] = officialreponame
elif (line.find("downloadspeedlimit|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
try:
myval = int(split_line[1])
if myval > 0:
data['transfer_limit'] = myval
else:
data['transfer_limit'] = None
except (ValueError, IndexError,):
data['transfer_limit'] = None
elif (line.find("securityurl|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
try:
data['security_advisories_url'] = split_line[1]
except (IndexError, ValueError, TypeError,):
continue
return data
def _clear_repository_cache(self, repoid = None):
"""
Internal method, go away!
"""
self.__cacher.discard()
self._clear_dump_cache(etpCache['world_available'])
self._clear_dump_cache(etpCache['world_update'])
self._clear_dump_cache(etpCache['check_package_update'])
self._clear_dump_cache(etpCache['filter_satisfied_deps'])
self._clear_dump_cache(etpCache['atomMatch'])
self._clear_dump_cache(etpCache['dep_tree'])
if repoid != None:
self._clear_dump_cache("%s/%s%s/" % (
etpCache['dbMatch'],etpConst['dbnamerepoprefix'],repoid,))
self._clear_dump_cache("%s/%s%s/" % (
etpCache['dbSearch'],etpConst['dbnamerepoprefix'],repoid,))
def _clear_dump_cache(self, dump_name, skip = []):
"""
Internal method, go away!
"""
dump_path = os.path.join(etpConst['dumpstoragedir'],dump_name)
dump_dir = os.path.dirname(dump_path)
#dump_file = os.path.basename(dump_path)
for currentdir, subdirs, files in os.walk(dump_dir):
path = os.path.join(dump_dir,currentdir)
if skip:
found = False
for myskip in skip:
if path.find(myskip) != -1:
found = True
break
if found: continue
for item in files:
if item.endswith(etpConst['cachedumpext']):
item = os.path.join(path,item)
try:
os.remove(item)
except (OSError, IOError,):
pass
try:
if not os.listdir(path):
os.rmdir(path)
except (OSError, IOError,):
pass
def __generic_parser(self, filepath):
"""
Internal method. This is the generic file parser here.
@param filepath valid path
@type filepath basestring
@return list parsed data
"""
return self.entropyTools.generic_file_content_parser(filepath)
def __remove_repo_cache(self, repoid = None):
"""
Internal method. Remove repository cache, because not valid anymore.
@param repoid repository identifier or None
@type repoid basestring or None
@return None
"""
if os.path.isdir(etpConst['dumpstoragedir']):
if repoid:
self._clear_repository_cache(repoid = repoid)
return
for repoid in self['repositories']['order']:
self._clear_repository_cache(repoid = repoid)
else:
try:
os.makedirs(etpConst['dumpstoragedir'])
except IOError, e:
if e.errno == 30: # readonly filesystem
etpUi['pretend'] = True
return
except OSError:
return
def __save_file_mtime(self, toread, tosaveinto):
"""
Internal method. Save mtime of a file to another file.
@param toread file path to read
@type toread basestring
@param tosaveinto path where to save retrieved mtime information
@type tosaveinto basestring
@return None
"""
if not os.path.isfile(toread):
currmtime = 0.0
else:
currmtime = os.path.getmtime(toread)
if not os.path.isdir(etpConst['dumpstoragedir']):
try:
os.makedirs(etpConst['dumpstoragedir'], 0775)
const_setup_perms(etpConst['dumpstoragedir'],
etpConst['entropygid'])
except IOError, e:
if e.errno == 30: # readonly filesystem
etpUi['pretend'] = True
return
except (OSError,), e:
# unable to create the storage directory
# useless to continue
return
try:
mtime_f = open(tosaveinto,"w")
except IOError, e: # unable to write?
if e.errno == 30: # readonly filesystem
etpUi['pretend'] = True
return
else:
mtime_f.write(str(currmtime))
mtime_f.flush()
mtime_f.close()
os.chmod(tosaveinto, 0664)
if etpConst['entropygid'] != None:
os.chown(tosaveinto, 0, etpConst['entropygid'])
def __validate_entropy_cache(self, maskfile, mtimefile, repoid = None):
"""
Internal method. Validates Entropy Cache
@param maskfile path of the setting file
@type maskfile basestring
@param mtimefile path where to save retrieved mtime information
@type mtimefile basestring
@param repoid repository identifier or None
@type repoid basestring or None
@return None
"""
# can't validate if running as user, moreover
# users can't make changes, so...
if os.getuid() != 0:
return
# handle on-disk cache validation
# in this case, repositories cache
# if file is changed, we must destroy cache
if not os.path.isfile(mtimefile):
# we can't know if it has been updated
# remove repositories caches
self.__remove_repo_cache(repoid = repoid)
self.__save_file_mtime(maskfile, mtimefile)
else:
# check mtime
try:
mtime_f = open(mtimefile,"r")
mtime = mtime_f.readline().strip()
mtime_f.close()
# compare with current mtime
try:
currmtime = str(os.path.getmtime(maskfile))
except OSError:
currmtime = "0.0"
if mtime != currmtime:
self.__remove_repo_cache(repoid = repoid)
self.__save_file_mtime(maskfile, mtimefile)
except (OSError, IOError,):
self.__remove_repo_cache(repoid = repoid)
self.__save_file_mtime(maskfile, mtimefile)