Files
entropy/libraries/entropy/core.py
2009-04-06 14:29:04 +02:00

1345 lines
46 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, const_setup_perms, etpRepositories, \
etpRepositoriesOrder, const_secure_config_file, const_set_nice_level
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 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.
"""
self.__data = {}
self.__is_destroyed = False
self.Entropy = None
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': {},
}
self.__setup_const()
self.__scan()
def destroy(self):
"""
Overloaded method from Singleton.
"Destroys" the instance.
@return None
"""
self.__is_destroyed = True
def connect_entropy(self, entropy_instance):
"""
Connect an Entropy (client/server) instance to
this Singleton. Be warned, it could be very dangerous
if you don't know what you are doing.
Valid instances are:
entropy.client.interfaces.Client
entropy.server.interfaces.Server
"""
from entropy.client.interfaces import Client
from entropy.server.interfaces import Server
if not isinstance(entropy_instance,(Client,Server,)):
mytxt = _("A valid Client/Server interface instance is needed")
raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = entropy_instance
self.__scan() # do this again to re-fill settings
def disconnect_entropy(self):
"""
Remove an Entropy (client/server) instance to
this Singleton.
"""
self.Entropy = None
self.__scan()
def __setup_const(self):
"""
Internal method. Does the constants initialization.
@return None
"""
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",
'socket_service': etpConst['socketconf'],
'system': etpConst['entropyconf'],
'client': etpConst['clientconf'],
'server': etpConst['serverconf'],
})
## XXX trunk support, for a while - exp. date 10/10/2009
trunk_fsdirs_conf = "../conf/fsdirs.conf"
trunk_fsdirsmask_conf = "../conf/fsdirsmask.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
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.__data.update(self.__parse())
# merge universal keywords
for keyword in self.__data['keywords']['universal']:
etpConst['keywords'].add(keyword)
# live package masking / unmasking
self.__data.update(
{
'live_packagemasking': {
'unmask_matches': set(),
'mask_matches': set(),
},
}
)
# match installed packages of system_mask
mask_installed = []
mask_installed_keys = {}
if self.Entropy != None:
while (self.Entropy.clientDbconn != None):
try:
self.Entropy.clientDbconn.validateDatabase()
except SystemDatabaseError:
break
mc_cache = set()
m_list = self.__data['repos_system_mask'] + \
self.__data['system_mask']
for atom in m_list:
m_ids, m_r = self.Entropy.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
self.__data['repos_system_mask_installed'] = mask_installed
self.__data['repos_system_mask_installed_keys'] = mask_installed_keys
# Live package masking
self.__data.update(self.__persistent_settings)
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 __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 etpRepositoriesOrder:
repos_mask_setting = {}
repos_mask_mtime = {}
repos_lic_wl_setting = {}
repos_lic_wl_mtime = {}
repo_data = etpRepositories[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
"""
# parse main settings
self.__setup_setting_vars()
self.__setup_package_sets_vars()
data = {}
for item in self.__setting_files:
myattr = '%s_parser' % (item,)
if not hasattr(self, myattr):
continue
func = getattr(self, myattr)
data[item] = func()
return data
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 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 etpRepositoriesOrder 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 client_parser(self):
"""
Parses Entropy client system configuration file.
@return dict data
"""
data = {
'filesbackup': etpConst['filesbackup'],
'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("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(
unicode(myprot,'raw_unicode_escape'))
elif line.startswith("configprotectmask|") and \
(split_line_len == 2):
configprotect = split_line[1].strip()
for myprot in configprotect.split():
data['configprotectmask'].append(
unicode(myprot,'raw_unicode_escape'))
elif line.startswith("configprotectskip|") and \
(split_line_len == 2):
configprotect = split_line[1].strip()
for myprot in configprotect.split():
data['configprotectskip'].append(
etpConst['systemroot']+unicode(myprot,
'raw_unicode_escape'))
return data
def server_parser(self):
"""
Parses Entropy server system configuration file.
@return dict data
"""
data = {
'repositories': etpConst['server_repositories'].copy(),
'branches': etpConst['branches'][:],
'default_repository_id': etpConst['officialserverrepositoryid'],
'packages_expiration_days': etpConst['packagesexpirationdays'],
'database_file_format': etpConst['etpdatabasefileformat'],
'rss': {
'enabled': etpConst['rss-feed'],
'name': etpConst['rss-name'],
'base_url': etpConst['rss-base-url'],
'website_url': etpConst['rss-website-url'],
'editor': etpConst['rss-managing-editor'],
'max_entries': etpConst['rss-max-entries'],
'light_max_entries': etpConst['rss-light-max-entries'],
},
}
if not os.access(etpConst['serverconf'], os.R_OK):
return data
with open(etpConst['serverconf'],"r") as server_f:
serverconf = [x.strip() for x in server_f.readlines() if x.strip()]
for line in serverconf:
split_line = line.split("|")
split_line_len = len(split_line)
if line.startswith("branches|") and (split_line_len == 2):
branches = split_line[1]
data['branches'] = []
for branch in branches.split():
data['branches'].append(branch)
if etpConst['branch'] not in data['branches']:
data['branches'].append(etpConst['branch'])
data['branches'] = sorted(data['branches'])
elif (line.find("officialserverrepositoryid|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
data['default_repository_id'] = split_line[1].strip()
elif (line.find("expiration-days|") != -1) and \
(not line.startswith("#")) and (split_line_len == 2):
mydays = split_line[1].strip()
try:
mydays = int(mydays)
data['packages_expiration_days'] = mydays
except ValueError:
continue
elif line.startswith("repository|") and (split_line_len in [5, 6]):
repoid, repodata = const_extract_srv_repo_params(line)
if repoid in data['repositories']:
# just update mirrors
data['repositories'][repoid]['mirrors'].extend(
repodata['mirrors'])
else:
data['repositories'][repoid] = repodata.copy()
elif line.startswith("database-format|") and (split_line_len == 2):
fmt = split_line[1]
if fmt in etpConst['etpdatabasesupportedcformats']:
data['database_file_format'] = fmt
elif line.startswith("rss-feed|") and (split_line_len == 2):
feed = split_line[1]
if feed in ("enable", "enabled", "true", "1"):
data['rss']['enabled'] = True
elif feed in ("disable", "disabled", "false", "0", "no",):
data['rss']['enabled'] = False
elif line.startswith("rss-name|") and (split_line_len == 2):
feedname = line.split("rss-name|")[1].strip()
data['rss']['name'] = feedname
elif line.startswith("rss-base-url|") and (split_line_len == 2):
data['rss']['base_url'] = line.split("rss-base-url|")[1].strip()
if not data['rss']['base_url'][-1] == "/":
data['rss']['base_url'] += "/"
elif line.startswith("rss-website-url|") and (split_line_len == 2):
data['rss']['website_url'] = split_line[1].strip()
elif line.startswith("managing-editor|") and (split_line_len == 2):
data['rss']['editor'] = split_line[1].strip()
elif line.startswith("max-rss-entries|") and (split_line_len == 2):
try:
entries = int(split_line[1].strip())
data['rss']['max_entries'] = entries
except (ValueError, IndexError,):
continue
elif line.startswith("max-rss-light-entries|") and \
(split_line_len == 2):
try:
entries = int(split_line[1].strip())
data['rss']['light_max_entries'] = entries
except (ValueError, IndexError,):
continue
# expand paths
for repoid in data['repositories']:
data['repositories'][repoid]['packages_dir'] = \
os.path.join( etpConst['entropyworkdir'],
"server",
repoid,
"packages",
etpSys['arch']
)
data['repositories'][repoid]['store_dir'] = \
os.path.join( etpConst['entropyworkdir'],
"server",
repoid,
"store",
etpSys['arch']
)
data['repositories'][repoid]['upload_dir'] = \
os.path.join( etpConst['entropyworkdir'],
"server",
repoid,
"upload",
etpSys['arch']
)
data['repositories'][repoid]['database_dir'] = \
os.path.join( etpConst['entropyworkdir'],
"server",
repoid,
"database",
etpSys['arch']
)
data['repositories'][repoid]['packages_relative_path'] = \
os.path.join( etpConst['product'],
repoid,
"packages",
etpSys['arch']
)+"/"
data['repositories'][repoid]['database_relative_path'] = \
os.path.join( etpConst['product'],
repoid,
"database",
etpSys['arch']
)+"/"
# Support for shell variables
shell_repoid = os.getenv('ETP_REPO')
if shell_repoid:
data['default_repository_id'] = shell_repoid
expiration_days = os.getenv('ETP_EXPIRATION_DAYS')
if expiration_days:
try:
expiration_days = int(expiration_days)
data['packages_expiration_days'] = expiration_days
except ValueError:
pass
return data
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
"""
data = []
if os.path.isfile(filepath) and os.access(filepath, os.R_OK):
gen_f = open(filepath,"r")
content = gen_f.readlines()
gen_f.close()
# filter comments and white lines
content = [x.strip().rsplit("#", 1)[0].strip() for x in content \
if not x.startswith("#") and x.strip()]
for line in content:
if line in data:
continue
data.append(line)
return data
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 and (self.Entropy != None):
self.Entropy.repository_move_clear_cache(repoid)
return
if self.Entropy != None:
for repoid in etpRepositoriesOrder:
self.Entropy.repository_move_clear_cache(repoid)
else:
os.makedirs(etpConst['dumpstoragedir'])
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']):
os.makedirs(etpConst['dumpstoragedir'], 0775)
const_setup_perms(etpConst['dumpstoragedir'],
etpConst['entropygid'])
mtime_f = open(tosaveinto,"w")
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)