Files
entropy/libraries/entropy.py
lxnay 582eab8eae Entropy/TODO:
- update TODO
Entropy/entropyConstants:
- remove conflicting_tagged_packages from etpConst and move to a 'per-repo' config file
Entropy/SystemSettings:
- handle new conflicting tagged packages file per-repo
Entropy/RepoInterface:
- when syncing the repo, download the new conflicting tagged packages file
Entropy/ServerInterface:
- handle the new conflicting tagged packages file (upload/download)


git-svn-id: http://svn.sabayonlinux.org/projects/entropy/trunk@2911 cd1c1023-2f26-0410-ae45-c471fc1f0318
2009-01-16 15:36:54 +00:00

35177 lines
1.3 MiB

#!/usr/bin/python
'''
# DESCRIPTION:
# Entropy Object Oriented Interface
Copyright (C) 2007-2008 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
'''
from __future__ import with_statement
import shutil, commands, urllib2, time, thread, copy, subprocess
from entropyConstants import *
from outputTools import TextInterface, \
print_info, print_warning, print_error, \
red, brown, blue, green, purple, darkgreen, \
darkred, bold, darkblue, readtext, nocolor
import exceptionTools
from entropy_i18n import _
try: # try with sqlite3 from python 2.5 - default one
from sqlite3 import dbapi2
except ImportError: # fallback to embedded pysqlite
try:
from pysqlite2 import dbapi2
except ImportError, e:
raise exceptionTools.SystemError(
"%s. %s: %s" % (
_("Entropy needs a working sqlite+pysqlite or Python compiled with sqlite support"),
_("Error"),
e,
)
)
class urlFetcher:
def __init__(self, url, pathToSave, checksum = True, showSpeed = True, resume = True, abort_check_func = None, disallow_redirect = False):
import entropyTools, socket
self.entropyTools, self.socket = entropyTools, socket
self.url = url
self.resume = resume
self.url = self.encodeUrl(self.url)
self.pathToSave = pathToSave
self.checksum = checksum
self.showSpeed = showSpeed
self.initVars()
self.progress = None
self.abort_check_func = abort_check_func
self.disallow_redirect = disallow_redirect
self.user_agent = "Entropy/%s (compatible; %s; %s: %s %s %s)" % (
etpConst['entropyversion'],
"Entropy",
os.path.basename(self.url),
os.uname()[0],
os.uname()[4],
os.uname()[2],
)
self.extra_header_data = {}
self.setup_resume_support()
self.setup_proxy()
def setup_resume_support(self):
# resume support
if os.path.isfile(self.pathToSave) and os.access(self.pathToSave,os.R_OK) and self.resume:
self.localfile = open(self.pathToSave,"awb")
self.localfile.seek(0,2)
self.startingposition = int(self.localfile.tell())
self.resumed = True
else:
if os.path.lexists(self.pathToSave) and not self.entropyTools.is_valid_path(self.pathToSave):
try:
os.remove(self.pathToSave)
except OSError: # I won't stop you here
pass
self.localfile = open(self.pathToSave,"wb")
def setup_proxy(self):
# setup proxy, doing here because config is dynamic
mydict = {}
if etpConst['proxy']['ftp']:
mydict['ftp'] = etpConst['proxy']['ftp']
if etpConst['proxy']['http']:
mydict['http'] = etpConst['proxy']['http']
if mydict:
mydict['username'] = etpConst['proxy']['username']
mydict['password'] = etpConst['proxy']['password']
self.entropyTools.add_proxy_opener(urllib2, mydict)
else:
# unset
urllib2._opener = None
def encodeUrl(self, url):
import urllib
url = os.path.join(os.path.dirname(url),urllib.quote(os.path.basename(url)))
return url
def initVars(self):
self.resumed = False
self.bufferSize = 8192
self.status = None
self.remotefile = None
self.downloadedsize = 0
self.average = 0
self.remotesize = 0
self.oldaverage = 0.0
# transfer status data
self.startingposition = 0
self.datatransfer = 0
self.time_remaining = "(infinite)"
self.elapsed = 0.0
self.updatestep = 0.2
self.speedlimit = etpConst['downloadspeedlimit'] # kbytes/sec
self.transferpollingtime = float(1)/4
def download(self):
self.initVars()
self.speedUpdater = self.entropyTools.TimeScheduled(
self.updateSpeedInfo,
self.transferpollingtime
)
self.speedUpdater.setName("download::"+self.url+str(abs(hash(os.urandom(20))))) # set unique ID to thread, hopefully
self.speedUpdater.start()
# set timeout
self.socket.setdefaulttimeout(20)
if self.url.startswith("http://"):
headers = { 'User-Agent' : self.user_agent }
req = urllib2.Request(self.url, self.extra_header_data, headers)
else:
req = self.url
u_agent_error = False
while 1:
# get file size if available
try:
self.remotefile = urllib2.urlopen(req)
except KeyboardInterrupt:
self.close()
raise
except urllib2.HTTPError, e:
if (e.code == 405) and not u_agent_error:
# server doesn't like our user agent
req = self.url
u_agent_error = True
continue
self.close()
self.status = "-3"
return self.status
except:
self.close()
self.status = "-3"
return self.status
break
try:
self.remotesize = int(self.remotefile.headers.get("content-length"))
self.remotefile.close()
except KeyboardInterrupt:
self.close()
raise
except:
pass
# handle user stupidity
try:
request = self.url
if ((self.startingposition > 0) and (self.remotesize > 0)) and (self.startingposition < self.remotesize):
try:
request = urllib2.Request(
self.url,
headers = {
"Range" : "bytes=" + str(self.startingposition) + "-" + str(self.remotesize)
}
)
except KeyboardInterrupt:
self.close()
raise
except:
pass
elif (self.startingposition == self.remotesize):
return self.prepare_return()
else:
self.localfile = open(self.pathToSave,"wb")
self.remotefile = urllib2.urlopen(request)
except KeyboardInterrupt:
self.close()
raise
except:
self.close()
self.status = "-3"
return self.status
if self.remotesize > 0:
self.remotesize = float(int(self.remotesize))/1024
if self.disallow_redirect and (self.url != self.remotefile.geturl()):
self.close()
self.status = "-3"
return self.status
rsx = "x"
while rsx != '':
try:
rsx = self.remotefile.read(self.bufferSize)
if self.abort_check_func != None:
self.abort_check_func()
except KeyboardInterrupt:
self.close()
raise
except:
# python 2.4 timeouts go here
self.close()
self.status = "-3"
return self.status
self.commitData(rsx)
if self.showSpeed:
self.updateProgress()
self.oldaverage = self.average
if self.speedlimit:
while self.datatransfer > self.speedlimit*1024:
time.sleep(0.1)
if self.showSpeed:
self.updateProgress()
self.oldaverage = self.average
# kill thread
self.close()
return self.prepare_return()
def prepare_return(self):
if self.checksum:
self.status = self.entropyTools.md5sum(self.pathToSave)
return self.status
else:
self.status = "-2"
return self.status
def commitData(self, mybuffer):
# writing file buffer
self.localfile.write(mybuffer)
# update progress info
self.downloadedsize = self.localfile.tell()
kbytecount = float(self.downloadedsize)/1024
self.average = int((kbytecount/self.remotesize)*100)
def updateProgress(self):
mytxt = _("[F]")
eta_txt = _("ETA")
sec_txt = _("sec") # as in XX kb/sec
currentText = darkred(" %s: " % (mytxt,)) + \
darkgreen(str(round(float(self.downloadedsize)/1024,1))) + "/" + \
red(str(round(self.remotesize,1))) + " kB"
# create progress bar
barsize = 10
bartext = "["
curbarsize = 1
if self.average > self.oldaverage+self.updatestep:
averagesize = (self.average*barsize)/100
while averagesize > 0:
curbarsize += 1
bartext += "="
averagesize -= 1
bartext += ">"
diffbarsize = barsize-curbarsize
while diffbarsize > 0:
bartext += " "
diffbarsize -= 1
if self.showSpeed:
bartext += "] => %s" % (self.entropyTools.bytesIntoHuman(self.datatransfer),)
bartext += "/%s : %s: %s" % (sec_txt,eta_txt,self.time_remaining,)
else:
bartext += "]"
average = str(self.average)
if len(average) < 2:
average = " "+average
currentText += " <-> "+average+"% "+bartext
print_info(currentText,back = True)
def close(self):
try:
self.localfile.flush()
self.localfile.close()
except:
pass
try:
self.remotefile.close()
except:
pass
self.speedUpdater.kill()
self.socket.setdefaulttimeout(2)
def updateSpeedInfo(self):
self.elapsed += self.transferpollingtime
# we have the diff size
self.datatransfer = (self.downloadedsize-self.startingposition) / self.elapsed
try:
self.time_remaining = int(round((int(round(self.remotesize*1024,0))-int(round(self.downloadedsize,0)))/self.datatransfer,0))
self.time_remaining = self.entropyTools.convertSecondsToFancyOutput(self.time_remaining)
except:
self.time_remaining = "(%s)" % (_("infinite"),)
class EntropyCacher:
import entropyTools
import dumpTools
import threading
def __init__(self):
self.alive = False
self.CacheBuffer = self.entropyTools.lifobuffer()
self.CacheWriter = self.entropyTools.TimeScheduled(self.Cacher,0.5)
self.CacheLock = self.threading.Lock()
def start(self):
self.alive = True
self.CacheWriter.start()
def sync(self, wait = False):
if not self.alive: return
wd = 1000
while self.CacheBuffer.is_filled() and ((wd > 0) or wait):
if not wait: wd -= 1
time.sleep(0.01)
def push(self, key, data, async = True):
if not self.alive: return
if async:
with self.CacheLock:
self.CacheBuffer.push((key,data,))
else:
self.dumpTools.dumpobj(key,data)
def pop(self, key):
with self.CacheLock:
l_o = self.dumpTools.loadobj
if not l_o: return
return l_o(key)
def Cacher(self):
while 1:
if not self.alive: break
with self.CacheLock:
data = self.CacheBuffer.pop()
if data == None: break
key, data = data
d_o = self.dumpTools.dumpobj
if not d_o: break
d_o(key,data)
def __del__(self):
self.stop()
def stop(self):
if hasattr(self,"CacheBuffer"):
if self.CacheBuffer and self.alive:
wd = 500
while self.CacheBuffer.is_filled() and wd:
wd -= 1
time.sleep(0.01)
self.CacheBuffer.clear()
self.alive = False
if hasattr(self,"CacheWriter"):
self.CacheWriter.kill()
'''
Main Entropy (client side) package management class
'''
class EquoInterface(TextInterface):
def __init__(self, indexing = True, noclientdb = 0, xcache = True, user_xcache = False, repo_validation = True, load_ugc = True, url_fetcher = urlFetcher):
# modules import
import dumpTools, entropyTools
self.dumpTools = dumpTools
self.entropyTools = entropyTools
self.atomMatchCacheKey = etpCache['atomMatch']
self.dbapi2 = dbapi2 # export for third parties
self.FileUpdates = None
self.repoDbCache = {}
self.securityCache = {}
self.QACache = {}
self.spmCache = {}
self.mirrorDownloadFailures = {}
self.repo_error_messages_cache = set()
self.package_match_validator_cache = {}
self.memoryDbInstances = {}
self.validRepositories = []
self.clientLog = LogFile(level = etpConst['equologlevel'],filename = etpConst['equologfile'], header = "[client]")
self.urlFetcher = url_fetcher # in this way, can be reimplemented (so you can override updateProgress)
self.progress = None # supporting external updateProgress stuff, you can point self.progress to your progress bar
# and reimplement updateProgress
self.clientDbconn = None
self.safe_mode = 0
self.FtpInterface = FtpInterface # for convenience
self.indexing = indexing
self.repo_validation = repo_validation
self.noclientdb = False
self.openclientdb = True
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
self.xcache = xcache
shell_xcache = os.getenv("ETP_NOCACHE")
if shell_xcache:
self.xcache = False
self.Cacher = EntropyCacher()
# needs to be started here otherwise repository cache will be
# always dropped, trust me, it will be stopped later in the init
# if xcache is disabled.
self.Cacher.start()
# 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:
self.validate_repositories_cache()
if not self.xcache and (self.entropyTools.is_user_in_entropy_group()):
try: self.purge_cache(False)
except: pass
if not self.xcache: self.Cacher.stop()
if self.openclientdb:
self.openClientDatabase()
self.FileUpdates = self.FileUpdatesInterfaceLoader()
# setup package settings (masking and other stuff)
self.SystemSettings = SystemSettings(self)
# settle settings, especially package masking ones
# useful for third party softwares that use this interface directly
self.SystemSettings.scan()
if self.repo_validation:
self.validate_repositories()
# load User Generated Content Interface
self.UGC = None
if load_ugc:
self.UGC = UGCClientInterface(self)
def destroy(self):
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,'Cacher'):
self.Cacher.stop()
self.closeAllRepositoryDatabases(mask_clear = False)
self.closeAllSecurity()
self.closeAllQA()
def __del__(self):
self.destroy()
def reload_constants(self):
initConfig_entropyConstants(etpSys['rootdir'])
initConfig_clientConstants()
def validate_repositories(self):
self.mirrorDownloadFailures.clear()
self.repo_error_messages_cache.clear()
self.package_match_validator_cache.clear()
# valid repositories
del self.validRepositories[:]
for repoid in etpRepositoriesOrder:
# open database
try:
dbc = self.openRepositoryDatabase(repoid)
dbc.listConfigProtectDirectories()
dbc.validateDatabase()
self.validRepositories.append(repoid)
except exceptionTools.RepositoryError:
t = _("Repository") + " " + repoid + " " + _("is not available") + ". " + _("Cannot validate")
t2 = _("Please update your repositories now in order to remove this message!")
self.updateProgress(
darkred(t),
importance = 1,
type = "warning"
)
self.updateProgress(
purple(t2),
header = bold("!!! "),
importance = 1,
type = "warning"
)
continue # repo not available
except (dbapi2.OperationalError,dbapi2.DatabaseError,exceptionTools.SystemDatabaseError,):
t = _("Repository") + " " + repoid + " " + _("is corrupted") + ". " + _("Cannot validate")
self.updateProgress(
darkred(t),
importance = 1,
type = "warning"
)
continue
# to avoid having zillions of open files when loading a lot of EquoInterfaces
self.closeAllRepositoryDatabases(mask_clear = False)
def init_generic_memory_repository(self, repoid, description, package_mirrors = []):
dbc = self.openMemoryDatabase(dbname = repoid)
self.memoryDbInstances[repoid] = dbc
# add to etpRepositories
repodata = {
'repoid': repoid,
'in_memory': True,
'description': description,
'packages': package_mirrors,
'dbpath': ':memory:',
}
self.addRepository(repodata)
return dbc
def setup_default_file_perms(self, filepath):
# setup file permissions
os.chmod(filepath,0664)
if etpConst['entropygid'] != None:
os.chown(filepath,-1,etpConst['entropygid'])
def _resources_run_create_lock(self):
self.create_pid_file_lock(etpConst['locks']['using_resources'])
def _resources_run_remove_lock(self):
if os.path.isfile(etpConst['locks']['using_resources']):
os.remove(etpConst['locks']['using_resources'])
def _resources_run_check_lock(self):
rc = self.check_pid_file_lock(etpConst['locks']['using_resources'])
return rc
def check_pid_file_lock(self, pidfile):
if not os.path.isfile(pidfile):
return False # not locked
f = open(pidfile)
s_pid = f.readline().strip()
f.close()
try:
s_pid = int(s_pid)
except ValueError:
return False # not locked
# is it our pid?
mypid = os.getpid()
if (s_pid != mypid) and os.path.isdir("%s/proc/%s" % (etpConst['systemroot'],s_pid,)):
# is it running
return True # locked
return False
def create_pid_file_lock(self, pidfile, mypid = None):
lockdir = os.path.dirname(pidfile)
if not os.path.isdir(lockdir):
os.makedirs(lockdir,0775)
const_setup_perms(lockdir,etpConst['entropygid'])
if mypid == None:
mypid = os.getpid()
f = open(pidfile,"w")
f.write(str(mypid))
f.flush()
f.close()
def application_lock_check(self, silent = False):
# check if another instance is running
etpConst['applicationlock'] = False
const_setupEntropyPid(just_read = True)
locked = self.entropyTools.applicationLockCheck(option = None, gentle = True, silent = True)
if locked:
if not silent:
self.updateProgress(
red(_("Another Entropy instance is currently active, cannot satisfy your request.")),
importance = 1,
type = "error",
header = darkred(" @@ ")
)
return True
return False
def lock_check(self, check_function):
lock_count = 0
max_lock_count = 600
sleep_seconds = 0.5
# check lock file
while 1:
locked = check_function()
if not locked:
if lock_count > 0:
self.updateProgress(
blue(_("Resources unlocked, let's go!")),
importance = 1,
type = "info",
header = darkred(" @@ ")
)
break
if lock_count >= max_lock_count:
mycalc = max_lock_count*sleep_seconds/60
self.updateProgress(
blue(_("Resources still locked after %s minutes, giving up!")) % (mycalc,),
importance = 1,
type = "warning",
header = darkred(" @@ ")
)
return True # gave up
lock_count += 1
self.updateProgress(
blue(_("Resources locked, sleeping %s seconds, check #%s/%s")) % (
sleep_seconds,
lock_count,
max_lock_count,
),
importance = 1,
type = "warning",
header = darkred(" @@ "),
back = True
)
time.sleep(sleep_seconds)
return False # yay!
def validate_repositories_cache(self):
# is the list of repos changed?
cached = self.Cacher.pop(etpCache['repolist'])
if cached == None:
# invalidate matching cache
try: self.repository_move_clear_cache()
except IOError: pass
elif isinstance(cached,tuple):
difflist = [x for x in cached if x not in etpRepositoriesOrder]
for repoid in difflist:
try: self.repository_move_clear_cache(repoid)
except IOError: pass
self.store_repository_list_cache()
def store_repository_list_cache(self):
self.Cacher.push(etpCache['repolist'],tuple(etpRepositoriesOrder)[:], async = False)
def backup_setting(self, setting_name):
if etpConst.has_key(setting_name):
myinst = etpConst[setting_name]
if type(etpConst[setting_name]) in (list,tuple):
myinst = etpConst[setting_name][:]
elif type(etpConst[setting_name]) in (dict,set):
myinst = etpConst[setting_name].copy()
else:
myinst = etpConst[setting_name]
etpConst['backed_up'].update({setting_name: myinst})
else:
t = _("Nothing to backup in etpConst with %s key") % (setting_name,)
raise exceptionTools.InvalidData("InvalidData: %s" % (t,))
def set_priority(self, low = 0):
return const_setNiceLevel(low)
def reloadRepositoriesConfigProtect(self, repositories = None):
if repositories == None:
repositories = self.validRepositories
for repoid in repositories:
dbconn = self.openRepositoryDatabase(repoid)
self.loadRepositoryConfigProtect(repoid, dbconn)
def switchChroot(self, chroot = ""):
# clean caches
self.purge_cache()
self.closeAllRepositoryDatabases()
if chroot.endswith("/"):
chroot = chroot[:-1]
etpSys['rootdir'] = chroot
self.reload_constants()
self.validate_repositories()
self.reopenClientDbconn()
if chroot:
try: self.clientDbconn.resetTreeupdatesDigests()
except: pass
# I don't think it's safe to keep them open
# isn't it?
self.closeAllSecurity()
self.closeAllQA()
def Security(self):
chroot = etpConst['systemroot']
cached = self.securityCache.get(chroot)
if cached != None:
return cached
cached = SecurityInterface(self)
self.securityCache[chroot] = cached
return cached
def QA(self):
chroot = etpConst['systemroot']
cached = self.QACache.get(chroot)
if cached != None:
return cached
cached = QAInterface(self)
self.QACache[chroot] = cached
return cached
def closeAllQA(self):
self.QACache.clear()
def closeAllSecurity(self):
self.securityCache.clear()
def reopenClientDbconn(self):
self.clientDbconn.closeDB()
self.openClientDatabase()
def closeAllRepositoryDatabases(self, mask_clear = True):
for item in self.repoDbCache:
self.repoDbCache[item].closeDB()
self.repoDbCache.clear()
if mask_clear: self.SystemSettings.clear()
def openClientDatabase(self):
def load_db_from_ram():
self.safe_mode = etpConst['safemodeerrors']['clientdb']
mytxt = "%s, %s" % (_("System database not found or corrupted"),_("running in safe mode using empty database from RAM"),)
self.updateProgress(
darkred(mytxt),
importance = 1,
type = "warning",
header = bold("!!!"),
)
conn = self.openMemoryDatabase(dbname = etpConst['clientdbid'])
return conn
if not os.path.isdir(os.path.dirname(etpConst['etpdatabaseclientfilepath'])):
os.makedirs(os.path.dirname(etpConst['etpdatabaseclientfilepath']))
if (not self.noclientdb) and (not os.path.isfile(etpConst['etpdatabaseclientfilepath'])):
conn = load_db_from_ram()
self.entropyTools.printTraceback(f = self.clientLog)
else:
conn = EntropyDatabaseInterface(
readOnly = False,
dbFile = etpConst['etpdatabaseclientfilepath'],
clientDatabase = True,
dbname = etpConst['clientdbid'],
xcache = self.xcache,
indexing = self.indexing,
OutputInterface = self,
ServiceInterface = self
)
# validate database
if not self.noclientdb:
try:
conn.validateDatabase()
except exceptionTools.SystemDatabaseError:
try:
conn.closeDB()
except:
pass
self.entropyTools.printTraceback(f = self.clientLog)
conn = load_db_from_ram()
if not etpConst['dbconfigprotect']:
if conn.doesTableExist('configprotect') and conn.doesTableExist('configprotectreference'):
etpConst['dbconfigprotect'] = conn.listConfigProtectDirectories()
if conn.doesTableExist('configprotectmask') and conn.doesTableExist('configprotectreference'):
etpConst['dbconfigprotectmask'] = conn.listConfigProtectDirectories(mask = True)
etpConst['dbconfigprotect'] = [etpConst['systemroot']+x for x in etpConst['dbconfigprotect']]
etpConst['dbconfigprotectmask'] = [etpConst['systemroot']+x for x in etpConst['dbconfigprotect']]
etpConst['dbconfigprotect'] += [etpConst['systemroot']+x for x in etpConst['configprotect'] if etpConst['systemroot']+x not in etpConst['dbconfigprotect']]
etpConst['dbconfigprotectmask'] += [etpConst['systemroot']+x for x in etpConst['configprotectmask'] if etpConst['systemroot']+x not in etpConst['dbconfigprotectmask']]
self.clientDbconn = conn
return self.clientDbconn
def clientDatabaseSanityCheck(self):
self.updateProgress(
darkred(_("Sanity Check") + ": " + _("system database")),
importance = 2,
type = "warning"
)
idpkgs = self.clientDbconn.listAllIdpackages()
length = len(idpkgs)
count = 0
errors = False
scanning_txt = _("Scanning...")
for x in idpkgs:
count += 1
self.updateProgress(
darkgreen(scanning_txt),
importance = 0,
type = "info",
back = True,
count = (count,length),
percent = True
)
try:
self.clientDbconn.getPackageData(x)
except Exception ,e:
self.entropyTools.printTraceback()
errors = True
self.updateProgress(
darkred(_("Errors on idpackage %s, error: %s")) % (x,str(e)),
importance = 0,
type = "warning"
)
if not errors:
t = _("Sanity Check") + ": %s" % (bold(_("PASSED")),)
self.updateProgress(
darkred(t),
importance = 2,
type = "warning"
)
return 0
else:
t = _("Sanity Check") + ": %s" % (bold(_("CORRUPTED")),)
self.updateProgress(
darkred(t),
importance = 2,
type = "warning"
)
return -1
def openRepositoryDatabase(self, repoid):
t_ident = 1 # thread.get_ident() disabled for now
if not self.repoDbCache.has_key((repoid,etpConst['systemroot'],t_ident,)):
dbconn = self.loadRepositoryDatabase(repoid, xcache = self.xcache, indexing = self.indexing)
try:
dbconn.checkDatabaseApi()
except:
pass
self.repoDbCache[(repoid,etpConst['systemroot'],t_ident,)] = dbconn
return dbconn
else:
return self.repoDbCache.get((repoid,etpConst['systemroot'],t_ident,))
def is_installed_idpackage_in_system_mask(self, idpackage):
if idpackage in self.SystemSettings['repos_system_mask_installed']:
return True
return False
'''
@description: open the repository database
@input repositoryName: name of the client database
@input xcache: loads on-disk cache
@input indexing: indexes SQL tables
@output: database class instance
NOTE: DO NOT USE THIS DIRECTLY, BUT USE EquoInterface.openRepositoryDatabase
'''
def loadRepositoryDatabase(self, repoid, xcache = True, indexing = True):
if isinstance(repoid,basestring):
if repoid.endswith(etpConst['packagesext']):
xcache = False
if repoid not in etpRepositories:
t = _("bad repository id specified")
if repoid not in self.repo_error_messages_cache:
self.updateProgress(
darkred(t),
importance = 2,
type = "warning"
)
self.repo_error_messages_cache.add(repoid)
raise exceptionTools.RepositoryError("RepositoryError: %s" % (t,))
dbfile = etpRepositories[repoid]['dbpath']+"/"+etpConst['etpdatabasefile']
if not os.path.isfile(dbfile):
t = _("Repository %s hasn't been downloaded yet.") % (repoid,)
if repoid not in self.repo_error_messages_cache:
self.updateProgress(
darkred(t),
importance = 2,
type = "warning"
)
self.repo_error_messages_cache.add(repoid)
raise exceptionTools.RepositoryError("RepositoryError: %s" % (t,))
conn = EntropyDatabaseInterface(
readOnly = True,
dbFile = dbfile,
clientDatabase = True,
dbname = etpConst['dbnamerepoprefix']+repoid,
xcache = xcache,
indexing = indexing,
OutputInterface = self,
ServiceInterface = self
)
# initialize CONFIG_PROTECT
if (etpRepositories[repoid]['configprotect'] == None) or \
(etpRepositories[repoid]['configprotectmask'] == None):
self.loadRepositoryConfigProtect(repoid, conn)
if (repoid not in etpConst['client_treeupdatescalled']) and (self.entropyTools.is_user_in_entropy_group()) and (not repoid.endswith(etpConst['packagesext'])):
updated = False
try:
updated = conn.clientUpdatePackagesData(self.clientDbconn)
except (dbapi2.OperationalError, dbapi2.DatabaseError):
pass
if updated:
self.clear_dump_cache(etpCache['world_update'])
self.clear_dump_cache(etpCache['world'])
self.clear_dump_cache(etpCache['install'])
self.clear_dump_cache(etpCache['remove'])
self.calculate_world_updates(use_cache = False)
return conn
def loadRepositoryConfigProtect(self, repoid, dbconn):
try:
etpRepositories[repoid]['configprotect'] = dbconn.listConfigProtectDirectories()
except (dbapi2.OperationalError, dbapi2.DatabaseError):
etpRepositories[repoid]['configprotect'] = []
try:
etpRepositories[repoid]['configprotectmask'] = dbconn.listConfigProtectDirectories(mask = True)
except (dbapi2.OperationalError, dbapi2.DatabaseError):
etpRepositories[repoid]['configprotectmask'] = []
etpRepositories[repoid]['configprotect'] = [etpConst['systemroot']+x for x in etpRepositories[repoid]['configprotect']]
etpRepositories[repoid]['configprotectmask'] = [etpConst['systemroot']+x for x in etpRepositories[repoid]['configprotectmask']]
etpRepositories[repoid]['configprotect'] += [etpConst['systemroot']+x for x in etpConst['configprotect'] if etpConst['systemroot']+x not in etpRepositories[repoid]['configprotect']]
etpRepositories[repoid]['configprotectmask'] += [etpConst['systemroot']+x for x in etpConst['configprotectmask'] if etpConst['systemroot']+x not in etpRepositories[repoid]['configprotectmask']]
def openGenericDatabase(self, dbfile, dbname = None, xcache = None, readOnly = False, indexing_override = None, skipChecks = False):
if xcache == None:
xcache = self.xcache
if indexing_override != None:
indexing = indexing_override
else:
indexing = self.indexing
if dbname == None:
dbname = etpConst['genericdbid']
return EntropyDatabaseInterface(
readOnly = readOnly,
dbFile = dbfile,
clientDatabase = True,
dbname = dbname,
xcache = xcache,
indexing = indexing,
OutputInterface = self,
skipChecks = skipChecks
)
def openMemoryDatabase(self, dbname = None):
if dbname == None:
dbname = etpConst['genericdbid']
dbc = EntropyDatabaseInterface(
readOnly = False,
dbFile = ':memory:',
clientDatabase = True,
dbname = dbname,
xcache = False,
indexing = False,
OutputInterface = self,
skipChecks = True
)
dbc.initializeDatabase()
return dbc
def backupDatabase(self, dbpath, backup_dir = None, silent = False, compress_level = 9):
if compress_level not in range(1,10):
compress_level = 9
backup_dir = os.path.dirname(dbpath)
if not backup_dir: backup_dir = os.path.dirname(dbpath)
dbname = os.path.basename(dbpath)
bytes_required = 1024000*300
if not (os.access(backup_dir,os.W_OK) and \
os.path.isdir(backup_dir) and os.path.isfile(dbpath) and \
os.access(dbpath,os.R_OK) and self.entropyTools.check_required_space(backup_dir, bytes_required)):
if not silent:
mytxt = "%s: %s, %s" % (darkred(_("Cannot backup selected database")),blue(dbpath),darkred(_("permission denied")),)
self.updateProgress(
mytxt,
importance = 1,
type = "error",
header = red(" @@ ")
)
return False, mytxt
def get_ts():
from datetime import datetime
ts = datetime.fromtimestamp(time.time())
return "%s%s%s_%sh%sm%ss" % (ts.year,ts.month,ts.day,ts.hour,ts.minute,ts.second)
comp_dbname = "%s%s.%s.bz2" % (etpConst['dbbackupprefix'],dbname,get_ts(),)
comp_dbpath = os.path.join(backup_dir,comp_dbname)
if not silent:
mytxt = "%s: %s ..." % (darkgreen(_("Backing up database to")),blue(comp_dbpath),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
import bz2
try:
self.entropyTools.compress_file(dbpath, comp_dbpath, bz2.BZ2File, compress_level)
except:
if not silent:
self.entropyTools.printTraceback()
return False, _("Unable to compress")
if not silent:
mytxt = "%s: %s" % (darkgreen(_("Database backed up successfully")),blue(comp_dbpath),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
return True, _("All fine")
def restoreDatabase(self, backup_path, db_destination, silent = False):
bytes_required = 1024000*300
if not (os.access(os.path.dirname(db_destination),os.W_OK) and \
os.path.isdir(os.path.dirname(db_destination)) and os.path.isfile(backup_path) and \
os.access(backup_path,os.R_OK) and self.entropyTools.check_required_space(os.path.dirname(db_destination), bytes_required)):
if not silent:
mytxt = "%s: %s, %s" % (darkred(_("Cannot restore selected backup")),blue(backup_path),darkred(_("permission denied")),)
self.updateProgress(
mytxt,
importance = 1,
type = "error",
header = red(" @@ ")
)
return False, mytxt
if not silent:
mytxt = "%s: %s => %s ..." % (darkgreen(_("Restoring backed up database")),blue(os.path.basename(backup_path)),blue(db_destination),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
import bz2
try:
self.entropyTools.uncompress_file(backup_path, db_destination, bz2.BZ2File)
except:
if not silent:
self.entropyTools.printTraceback()
return False, _("Unable to unpack")
if not silent:
mytxt = "%s: %s" % (darkgreen(_("Database restored successfully")),blue(db_destination),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
self.purge_cache()
return True, _("All fine")
def list_backedup_client_databases(self, client_dbdir = None):
if not client_dbdir:
client_dbdir = os.path.dirname(etpConst['etpdatabaseclientfilepath'])
return [os.path.join(client_dbdir,x) for x in os.listdir(client_dbdir) \
if x.startswith(etpConst['dbbackupprefix']) and \
os.access(os.path.join(client_dbdir,x),os.R_OK)
]
def get_branch_from_download_relative_uri(self, db_download_uri):
return db_download_uri.split("/")[2]
'''
Cache stuff :: begin
'''
def purge_cache(self, showProgress = True, client_purge = True):
if self.entropyTools.is_user_in_entropy_group():
skip = set()
if not client_purge:
skip.add("/"+etpCache['dbMatch']+"/"+etpConst['clientdbid']) # it's ok this way
skip.add("/"+etpCache['dbSearch']+"/"+etpConst['clientdbid']) # it's ok this way
for key in etpCache:
if showProgress:
self.updateProgress(
darkred(_("Cleaning %s => dumps...")) % (etpCache[key],),
importance = 1,
type = "warning",
back = True
)
self.clear_dump_cache(etpCache[key], skip = skip)
if showProgress:
self.updateProgress(
darkgreen(_("Cache is now empty.")),
importance = 2,
type = "info"
)
def generate_cache(self, depcache = True, configcache = True, client_purge = True, install_queue = True):
# clean first of all
self.purge_cache(client_purge = client_purge)
if depcache:
self.do_depcache(do_install_queue = install_queue)
if configcache:
self.do_configcache()
def do_configcache(self):
self.updateProgress(
darkred(_("Configuration files")),
importance = 2,
type = "warning"
)
self.updateProgress(
red(_("Scanning hard disk")),
importance = 1,
type = "warning"
)
self.FileUpdates.scanfs(dcache = False, quiet = True)
self.updateProgress(
darkred(_("Cache generation complete.")),
importance = 2,
type = "info"
)
def do_depcache(self, do_install_queue = True):
self.updateProgress(
darkgreen(_("Resolving metadata")),
importance = 1,
type = "warning"
)
# we can barely ignore any exception from here
# especially cases where client db does not exist
try:
update, remove, fine = self.calculate_world_updates()
del fine, remove
if do_install_queue:
self.retrieveInstallQueue(update, False, False)
self.calculate_available_packages()
except:
pass
self.updateProgress(
darkred(_("Dependencies cache filled.")),
importance = 2,
type = "warning"
)
def clear_dump_cache(self, dump_name, skip = []):
self.Cacher.sync(wait = True)
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: pass
if not os.listdir(path):
os.rmdir(path)
'''
Cache stuff :: end
'''
def unused_packages_test(self, dbconn = None):
if dbconn == None: dbconn = self.clientDbconn
return [x for x in dbconn.retrieveUnusedIdpackages() if self.validatePackageRemoval(x)]
def dependencies_test(self, dbconn = None):
if dbconn == None:
dbconn = self.clientDbconn
# get all the installed packages
installedPackages = dbconn.listAllIdpackages()
deps_not_matched = set()
# now look
length = len(installedPackages)
count = 0
for xidpackage in installedPackages:
count += 1
atom = dbconn.retrieveAtom(xidpackage)
self.updateProgress(
darkgreen(_("Checking %s") % (bold(atom),)),
importance = 0,
type = "info",
back = True,
count = (count,length),
header = darkred(" @@ ")
)
xdeps = dbconn.retrieveDependencies(xidpackage)
needed_deps = set()
for xdep in xdeps:
xmatch = dbconn.atomMatch(xdep)
if xmatch[0] == -1:
needed_deps.add(xdep)
deps_not_matched |= needed_deps
return deps_not_matched
def find_belonging_dependency(self, matched_atoms):
crying_atoms = set()
for atom in matched_atoms:
for repo in self.validRepositories:
rdbconn = self.openRepositoryDatabase(repo)
riddep = rdbconn.searchDependency(atom)
if riddep != -1:
ridpackages = rdbconn.searchIdpackageFromIddependency(riddep)
for i in ridpackages:
i,r = rdbconn.idpackageValidator(i)
if i == -1:
continue
iatom = rdbconn.retrieveAtom(i)
crying_atoms.add((iatom,repo))
return crying_atoms
def get_licenses_to_accept(self, install_queue):
if not install_queue:
return {}
licenses = {}
for match in install_queue:
repoid = match[1]
dbconn = self.openRepositoryDatabase(repoid)
wl = self.SystemSettings['repos_license_whitelist'].get(repoid)
if not wl:
continue
keys = dbconn.retrieveLicensedataKeys(match[0])
for key in keys:
if key not in wl:
found = self.clientDbconn.isLicenseAccepted(key)
if found:
continue
if not licenses.has_key(key):
licenses[key] = set()
licenses[key].add(match)
return licenses
def get_text_license(self, license_name, repoid):
dbconn = self.openRepositoryDatabase(repoid)
text = dbconn.retrieveLicenseText(license_name)
tempfile = self.entropyTools.getRandomTempFile()
f = open(tempfile,"w")
f.write(text)
f.flush()
f.close()
return tempfile
def get_file_viewer(self):
viewer = None
if os.access("/usr/bin/less",os.X_OK):
viewer = "/usr/bin/less"
elif os.access("/bin/more",os.X_OK):
viewer = "/bin/more"
if not viewer:
viewer = self.get_file_editor()
return viewer
def get_file_editor(self):
editor = None
if os.getenv("EDITOR"):
editor = "$EDITOR"
elif os.access("/bin/nano",os.X_OK):
editor = "/bin/nano"
elif os.access("/bin/vi",os.X_OK):
editor = "/bin/vi"
elif os.access("/usr/bin/vi",os.X_OK):
editor = "/usr/bin/vi"
elif os.access("/usr/bin/emacs",os.X_OK):
editor = "/usr/bin/emacs"
elif os.access("/bin/emacs",os.X_OK):
editor = "/bin/emacs"
return editor
def libraries_test(self, dbconn = None, broken_symbols = False, task_bombing_func = None):
if dbconn == None:
dbconn = self.clientDbconn
self.updateProgress(
blue(_("Libraries test")),
importance = 2,
type = "info",
header = red(" @@ ")
)
if not etpConst['systemroot']:
myroot = "/"
else:
myroot = etpConst['systemroot']+"/"
# run ldconfig first
subprocess.call("ldconfig -r %s &> /dev/null" % (myroot,), shell = True)
# open /etc/ld.so.conf
if not os.path.isfile(etpConst['systemroot']+"/etc/ld.so.conf"):
self.updateProgress(
blue(_("Cannot find "))+red(etpConst['systemroot']+"/etc/ld.so.conf"),
importance = 1,
type = "error",
header = red(" @@ ")
)
return {},set(),-1
ldpaths = set(self.entropyTools.collectLinkerPaths())
ldpaths |= self.entropyTools.collectPaths()
# speed up when /usr/lib is a /usr/lib64 symlink
if "/usr/lib64" in ldpaths and "/usr/lib" in ldpaths:
if os.path.realpath("/usr/lib64") == "/usr/lib":
ldpaths.discard("/usr/lib")
# some crappy packages put shit here too
ldpaths.add("/usr/share")
# always force /usr/libexec too
ldpaths.add("/usr/libexec")
executables = set()
total = len(ldpaths)
count = 0
sys_root_len = len(etpConst['systemroot'])
for ldpath in ldpaths:
if callable(task_bombing_func): task_bombing_func()
count += 1
self.updateProgress(
blue("Tree: ")+red(etpConst['systemroot']+ldpath),
importance = 0,
type = "info",
count = (count,total),
back = True,
percent = True,
header = " "
)
ldpath = ldpath.encode(sys.getfilesystemencoding())
mywalk_iter = os.walk(etpConst['systemroot']+ldpath)
def mywimf(dt):
currentdir, subdirs, files = dt
def mymf(item):
filepath = os.path.join(currentdir,item)
if filepath in etpConst['libtest_files_blacklist']:
return 0
if not os.access(filepath,os.R_OK):
return 0
if not os.path.isfile(filepath):
return 0
if not self.entropyTools.is_elf_file(filepath):
return 0
return filepath[sys_root_len:]
return set([x for x in map(mymf,files) if type(x) != int])
for x in map(mywimf,mywalk_iter): executables |= x
self.updateProgress(
blue(_("Collecting broken executables")),
importance = 2,
type = "info",
header = red(" @@ ")
)
t = red(_("Attention")) + ": " + \
blue(_("don't worry about libraries that are shown here but not later."))
self.updateProgress(
t,
importance = 1,
type = "info",
header = red(" @@ ")
)
myQA = self.QA()
plain_brokenexecs = set()
total = len(executables)
count = 0
scan_txt = blue("%s ..." % (_("Scanning libraries"),))
for executable in executables:
if callable(task_bombing_func): task_bombing_func()
count += 1
if (count%10 == 0) or (count == total) or (count == 1):
self.updateProgress(
scan_txt,
importance = 0,
type = "info",
count = (count,total),
back = True,
percent = True,
header = " "
)
myelfs = self.entropyTools.read_elf_dynamic_libraries(etpConst['systemroot']+executable)
def mymf2(mylib):
return not myQA.resolve_dynamic_library(mylib, executable)
mylibs = set(filter(mymf2,myelfs))
broken_sym_found = set()
if broken_symbols and not mylibs: broken_sym_found |= self.entropyTools.read_elf_broken_symbols(etpConst['systemroot']+executable)
if not (mylibs or broken_sym_found):
continue
if mylibs:
alllibs = blue(' :: ').join(list(mylibs))
self.updateProgress(
red(etpConst['systemroot']+executable)+" [ "+alllibs+" ]",
importance = 1,
type = "info",
percent = True,
count = (count,total),
header = " "
)
elif broken_sym_found:
allsyms = darkred(' :: ').join([brown(x) for x in list(broken_sym_found)])
if len(allsyms) > 50: allsyms = brown(_('various broken symbols'))
self.updateProgress(
red(etpConst['systemroot']+executable)+" { "+allsyms+" }",
importance = 1,
type = "info",
percent = True,
count = (count,total),
header = " "
)
plain_brokenexecs.add(executable)
del executables
packagesMatched = {}
if not etpSys['serverside']:
self.updateProgress(
blue(_("Matching broken libraries/executables")),
importance = 1,
type = "info",
header = red(" @@ ")
)
matched = set()
for brokenlib in plain_brokenexecs:
idpackages = self.clientDbconn.searchBelongs(brokenlib)
for idpackage in idpackages:
key, slot = self.clientDbconn.retrieveKeySlot(idpackage)
mymatch = self.atomMatch(key, matchSlot = slot)
if mymatch[0] == -1:
matched.add(brokenlib)
continue
cmpstat = self.get_package_action(mymatch)
if cmpstat == 0:
continue
if not packagesMatched.has_key(brokenlib):
packagesMatched[brokenlib] = set()
packagesMatched[brokenlib].add(mymatch)
matched.add(brokenlib)
plain_brokenexecs -= matched
return packagesMatched,plain_brokenexecs,0
def move_to_branch(self, branch, pretend = False):
if pretend: return 0
if branch != etpConst['branch']:
# update configuration
self.entropyTools.writeNewBranch(branch)
# reset treeupdatesactions
self.clientDbconn.resetTreeupdatesDigests()
# clean cache
self.purge_cache(showProgress = False)
# reopen Client Database, this will make treeupdates to be re-read
self.closeAllRepositoryDatabases()
etpConst['branch'] = branch
self.reload_constants()
etpConst['branch'] = branch
self.validate_repositories()
self.reopenClientDbconn()
return 0
# tell if a new equo release is available, returns True or False
def check_equo_updates(self):
found, match = self.check_package_update("app-admin/equo", deep = True)
return found
'''
@input: matched atom (idpackage,repoid)
@output:
upgrade: int(2)
install: int(1)
reinstall: int(0)
downgrade: int(-1)
'''
def get_package_action(self, match):
dbconn = self.openRepositoryDatabase(match[1])
pkgkey, pkgslot = dbconn.retrieveKeySlot(match[0])
results = self.clientDbconn.searchKeySlot(pkgkey, pkgslot)
if not results: return 1
installed_idpackage = results[0][0]
pkgver, pkgtag, pkgrev = dbconn.getVersioningData(match[0])
installedVer, installedTag, installedRev = self.clientDbconn.getVersioningData(installed_idpackage)
pkgcmp = self.entropyTools.entropyCompareVersions((pkgver,pkgtag,pkgrev),(installedVer,installedTag,installedRev))
if pkgcmp == 0:
return 0
elif pkgcmp > 0:
return 2
return -1
def get_meant_packages(self, search_term, from_installed = False, valid_repos = []):
pkg_data = []
atom_srch = False
if "/" in search_term: atom_srch = True
if not valid_repos: valid_repos = self.validRepositories
if from_installed: valid_repos = [1]
for repo in valid_repos:
if isinstance(repo,basestring):
dbconn = self.openRepositoryDatabase(repo)
elif isinstance(repo,EntropyDatabaseInterface):
dbconn = repo
elif hasattr(self,'clientDbconn'):
dbconn = self.clientDbconn
else:
continue
pkg_data.extend([(x,repo,) for x in dbconn.searchSimilarPackages(search_term, atom = atom_srch)])
return pkg_data
# better to use key:slot
def check_package_update(self, atom, deep = False):
c_hash = "%s%s" % (etpCache['check_package_update'],hash("%s%s" % (atom,hash(deep),)),)
if self.xcache:
cached = self.Cacher.pop(c_hash)
if cached != None:
return cached
found = False
match = self.clientDbconn.atomMatch(atom)
matched = None
if match[0] != -1:
myatom = self.clientDbconn.retrieveAtom(match[0])
mytag = self.entropyTools.dep_gettag(myatom)
myatom = self.entropyTools.remove_tag(myatom)
myrev = self.clientDbconn.retrieveRevision(match[0])
pkg_match = "="+myatom+"~"+str(myrev)
if mytag != None:
pkg_match += "#%s" % (mytag,)
pkg_unsatisfied = self.get_unsatisfied_dependencies([pkg_match], deep_deps = deep)
if pkg_unsatisfied:
found = True
del pkg_unsatisfied
matched = self.atomMatch(pkg_match)
del match
if self.xcache:
self.Cacher.push(c_hash,(found,matched)[:])
return found, matched
# @returns -1 if the file does not exist or contains bad data
# @returns int>0 if the file exists
def get_repository_revision(self, reponame):
if os.path.isfile(etpRepositories[reponame]['dbpath']+"/"+etpConst['etpdatabaserevisionfile']):
f = open(etpRepositories[reponame]['dbpath']+"/"+etpConst['etpdatabaserevisionfile'],"r")
try:
revision = int(f.readline().strip())
except:
revision = -1
f.close()
else:
revision = -1
return revision
def update_repository_revision(self, reponame):
r = self.get_repository_revision(reponame)
etpRepositories[reponame]['dbrevision'] = "0"
if r != -1:
etpRepositories[reponame]['dbrevision'] = str(r)
# @returns -1 if the file does not exist
# @returns int>0 if the file exists
def get_repository_db_file_checksum(self, reponame):
if os.path.isfile(etpRepositories[reponame]['dbpath']+"/"+etpConst['etpdatabasehashfile']):
f = open(etpRepositories[reponame]['dbpath']+"/"+etpConst['etpdatabasehashfile'],"r")
try:
mhash = f.readline().strip().split()[0]
except:
mhash = "-1"
f.close()
else:
mhash = "-1"
return mhash
def update_ugc_cache(self, repository):
if not self.UGC.is_repository_eapi3_aware(repository):
return None
status = True
votes_dict, err_msg = self.UGC.get_all_votes(repository)
if isinstance(votes_dict,dict):
self.UGC.UGCCache.save_vote_cache(repository, votes_dict)
else:
status = False
downloads_dict, err_msg = self.UGC.get_all_downloads(repository)
if isinstance(downloads_dict,dict):
self.UGC.UGCCache.save_downloads_cache(repository, downloads_dict)
else:
status = False
return status
def __handle_multi_repo_matches(self, results, extended_results, valid_repos, open_db):
packageInformation = {}
versionInformation = {}
# .tbz2 repos have always the precedence, so if we find them,
# we should second what user wants, installing his tbz2
tbz2repos = [x for x in results if x.endswith(etpConst['packagesext'])]
if tbz2repos:
del tbz2repos
newrepos = results.copy()
for x in newrepos:
if x.endswith(etpConst['packagesext']):
continue
del results[x]
version_duplicates = set()
versions = set()
for repo in results:
packageInformation[repo] = {}
if extended_results:
version = results[repo][1]
packageInformation[repo]['versiontag'] = results[repo][2]
packageInformation[repo]['revision'] = results[repo][3]
else:
dbconn = open_db(repo)
packageInformation[repo]['versiontag'] = dbconn.retrieveVersionTag(results[repo])
packageInformation[repo]['revision'] = dbconn.retrieveRevision(results[repo])
version = dbconn.retrieveVersion(results[repo])
packageInformation[repo]['version'] = version
versionInformation[version] = repo
if version in versions:
version_duplicates.add(version)
versions.add(version)
newerVersion = self.entropyTools.getNewerVersion(list(versions))[0]
# if no duplicates are found or newer version is not in duplicates we're done
if (not version_duplicates) or (newerVersion not in version_duplicates):
reponame = versionInformation.get(newerVersion)
return (results[reponame],reponame)
# we have two repositories with >two packages with the same version
# check package tag
conflictingEntries = {}
tags_duplicates = set()
tags = set()
tagsInfo = {}
for repo in packageInformation:
if packageInformation[repo]['version'] != newerVersion:
continue
conflictingEntries[repo] = {}
versiontag = packageInformation[repo]['versiontag']
if versiontag in tags:
tags_duplicates.add(versiontag)
tags.add(versiontag)
tagsInfo[versiontag] = repo
conflictingEntries[repo]['versiontag'] = versiontag
conflictingEntries[repo]['revision'] = packageInformation[repo]['revision']
# tags will always be != []
newerTag = sorted(list(tags), reverse = True)[0]
if newerTag not in tags_duplicates:
reponame = tagsInfo.get(newerTag)
return (results[reponame],reponame)
# in this case, we have >two packages with the same version and tag
# check package revision
conflictingRevisions = {}
revisions = set()
revisions_duplicates = set()
revisionInfo = {}
for repo in conflictingEntries:
if conflictingEntries[repo]['versiontag'] == newerTag:
conflictingRevisions[repo] = {}
versionrev = conflictingEntries[repo]['revision']
if versionrev in revisions:
revisions_duplicates.add(versionrev)
revisions.add(versionrev)
revisionInfo[versionrev] = repo
conflictingRevisions[repo]['revision'] = versionrev
newerRevision = max(revisions)
if newerRevision not in revisions_duplicates:
reponame = revisionInfo.get(newerRevision)
return (results[reponame],reponame)
# final step, in this case we have >two packages with the same version, tag and revision
# get the repository with the biggest priority
for reponame in valid_repos:
if reponame in conflictingRevisions:
return (results[reponame],reponame)
def atomMatch(self, atom, caseSensitive = True, matchSlot = None, matchBranches = (), matchTag = None, packagesFilter = True,
multiMatch = False, multiRepo = False, matchRevision = None, matchRepo = None,
server_repos = [], serverInstance = None, extendedResults = False, useCache = True):
# support match in repository from shell
# atom@repo1,repo2,repo3
atom, repos = self.entropyTools.dep_get_match_in_repos(atom)
if (matchRepo == None) and (repos != None):
matchRepo = repos
u_hash = ""
m_hash = ""
k_ms = "//"
k_mt = "@#@"
k_mr = "-1"
if isinstance(matchRepo,(list,tuple,set,)): u_hash = hash(frozenset(matchRepo))
if isinstance(matchBranches,(list,tuple,set,)): m_hash = hash(frozenset(matchBranches))
if isinstance(matchSlot,basestring): k_ms = matchSlot
if isinstance(matchTag,basestring): k_mt = matchTag
if isinstance(matchRevision,basestring): k_mr = matchRevision
c_hash = "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" % (
atom,k_ms,k_mt,hash(packagesFilter),
hash(frozenset(self.validRepositories)),
hash(frozenset(etpRepositories)),
hash(multiMatch),hash(multiRepo),hash(caseSensitive),
k_mr,hash(extendedResults),
u_hash, m_hash
)
c_hash = "%s%s" % (self.atomMatchCacheKey,hash(c_hash),)
if self.xcache and useCache:
cached = self.Cacher.pop(c_hash)
if cached != None:
return cached
if server_repos:
if not serverInstance:
t = _("server_repos needs serverInstance")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (t,))
valid_repos = server_repos[:]
else:
valid_repos = self.validRepositories
if matchRepo and (type(matchRepo) in (list,tuple,set)):
valid_repos = list(matchRepo)
def open_db(repoid):
if server_repos:
dbconn = serverInstance.openServerDatabase(just_reading = True, repo = repoid)
else:
dbconn = self.openRepositoryDatabase(repoid)
return dbconn
repoResults = {}
for repo in valid_repos:
# search
dbconn = open_db(repo)
use_cache = useCache
while 1:
try:
query_data,query_rc = dbconn.atomMatch(
atom,
caseSensitive = caseSensitive,
matchSlot = matchSlot,
matchBranches = matchBranches,
matchTag = matchTag,
packagesFilter = packagesFilter,
matchRevision = matchRevision,
extendedResults = extendedResults,
useCache = use_cache
)
if query_rc == 0:
# package found, add to our dictionary
if extendedResults:
repoResults[repo] = (query_data[0],query_data[2],query_data[3],query_data[4])
else:
repoResults[repo] = query_data
except TypeError:
if not use_cache:
raise
use_cache = False
continue
break
if extendedResults:
dbpkginfo = ((-1,None,None,None),1)
else:
dbpkginfo = (-1,1)
if multiRepo and repoResults:
data = set()
for repoid in repoResults:
data.add((repoResults[repoid],repoid))
dbpkginfo = (data,0)
elif len(repoResults) == 1:
# one result found
repo = repoResults.keys()[0]
dbpkginfo = (repoResults[repo],repo)
elif len(repoResults) > 1:
# we have to decide which version should be taken
mypkginfo = self.__handle_multi_repo_matches(repoResults, extendedResults, valid_repos, open_db)
if mypkginfo != None: dbpkginfo = mypkginfo
# multimatch support
if multiMatch:
if dbpkginfo[1] != 1: # can be "0" or a string, but 1 means failure
if multiRepo:
data = set()
for q_id,q_repo in dbpkginfo[0]:
dbconn = open_db(q_repo)
query_data, query_rc = dbconn.atomMatch(
atom,
caseSensitive = caseSensitive,
matchSlot = matchSlot,
matchBranches = matchBranches,
matchTag = matchTag,
packagesFilter = packagesFilter,
multiMatch = True,
extendedResults = extendedResults
)
if extendedResults:
for item in query_data:
data.add(((item[0],item[2],item[3],item[4]),q_repo))
else:
for x in query_data: data.add((x,q_repo))
dbpkginfo = (data,0)
else:
dbconn = open_db(dbpkginfo[1])
query_data, query_rc = dbconn.atomMatch(
atom,
caseSensitive = caseSensitive,
matchSlot = matchSlot,
matchBranches = matchBranches,
matchTag = matchTag,
packagesFilter = packagesFilter,
multiMatch = True,
extendedResults = extendedResults
)
if extendedResults:
dbpkginfo = (set([((x[0],x[2],x[3],x[4]),dbpkginfo[1]) for x in query_data]),0)
else:
dbpkginfo = (set([(x,dbpkginfo[1]) for x in query_data]),0)
if self.xcache and useCache:
self.Cacher.push(c_hash,dbpkginfo[:])
return dbpkginfo
# expands package sets, and in future something more perhaps
def packagesExpand(self, packages):
new_packages = []
for pkg_id in range(len(packages)):
package = packages[pkg_id]
# expand package sets
if package.startswith(etpConst['packagesetprefix']):
set_pkgs = sorted(list(self.packageSetExpand(package, raise_exceptions = False)))
new_packages.extend([x for x in set_pkgs if x not in packages]) # atomMatch below will filter dupies
else:
new_packages.append(package)
return new_packages
def packageSetExpand(self, package_set, raise_exceptions = True):
max_recursion_level = 50
recursion_level = 0
def do_expand(myset, recursion_level, max_recursion_level):
recursion_level += 1
if recursion_level > max_recursion_level:
raise exceptionTools.InvalidPackageSet('InvalidPackageSet: corrupted, too many recursions: %s' % (myset,))
set_data, set_rc = self.packageSetMatch(myset[len(etpConst['packagesetprefix']):])
if not set_rc:
raise exceptionTools.InvalidPackageSet('InvalidPackageSet: not found: %s' % (myset,))
(set_from, package_set, mydata,) = set_data
mypkgs = set()
for fset in mydata: # recursively
if fset.startswith(etpConst['packagesetprefix']):
mypkgs |= do_expand(fset, recursion_level, max_recursion_level)
else:
mypkgs.add(fset)
return mypkgs
if not package_set.startswith(etpConst['packagesetprefix']):
package_set = "%s%s" % (etpConst['packagesetprefix'],package_set,)
try:
mylist = do_expand(package_set, recursion_level, max_recursion_level)
except exceptionTools.InvalidPackageSet:
if raise_exceptions: raise
mylist = set()
return mylist
def packageSetList(self, server_repos = [], serverInstance = None):
return self.packageSetMatch('', server_repos = server_repos, serverInstance = serverInstance, search = True)[0]
def packageSetSearch(self, package_set, server_repos = [], serverInstance = None):
# search support
if package_set == '*': package_set = ''
return self.packageSetMatch(package_set, server_repos = server_repos, serverInstance = serverInstance, search = True)[0]
def packageSetMatch(self, package_set, multiMatch = False, matchRepo = None, server_repos = [], serverInstance = None, search = False):
# support match in repository from shell
# set@repo1,repo2,repo3
package_set, repos = self.entropyTools.dep_get_match_in_repos(package_set)
if (matchRepo == None) and (repos != None):
matchRepo = repos
if server_repos:
if not serverInstance:
t = _("server_repos needs serverInstance")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (t,))
valid_repos = server_repos[:]
else:
valid_repos = self.validRepositories
if matchRepo and (type(matchRepo) in (list,tuple,set)):
valid_repos = list(matchRepo)
def open_db(repoid):
if server_repos:
dbconn = serverInstance.openServerDatabase(just_reading = True, repo = repoid)
else:
dbconn = self.openRepositoryDatabase(repoid)
return dbconn
# if we search, we return all the matches available
if search: multiMatch = True
set_data = []
while 1:
# check inside SystemSettings
if not server_repos:
if search:
mysets = [x for x in self.SystemSettings['system_package_sets'].keys() if (x.find(package_set) != -1)]
for myset in mysets:
mydata = self.SystemSettings['system_package_sets'].get(myset)
set_data.append((etpConst['userpackagesetsid'], unicode(myset), mydata.copy(),))
else:
mydata = self.SystemSettings['system_package_sets'].get(package_set)
if mydata != None:
set_data.append((etpConst['userpackagesetsid'], unicode(package_set), mydata,))
if not multiMatch: break
for repoid in valid_repos:
dbconn = open_db(repoid)
if search:
mysets = dbconn.searchSets(package_set)
for myset in mysets:
mydata = dbconn.retrievePackageSet(myset)
set_data.append((repoid, myset, mydata.copy(),))
else:
mydata = dbconn.retrievePackageSet(package_set)
if mydata: set_data.append((repoid, package_set, mydata,))
if not multiMatch: break
break
if not set_data: return (),False
if multiMatch: return set_data,True
return set_data.pop(0),True
def repository_move_clear_cache(self, repoid = None):
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(self.atomMatchCacheKey)
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 addRepository(self, repodata):
# update etpRepositories
try:
etpRepositories[repodata['repoid']] = {}
etpRepositories[repodata['repoid']]['description'] = repodata['description']
etpRepositories[repodata['repoid']]['configprotect'] = None
etpRepositories[repodata['repoid']]['configprotectmask'] = None
except KeyError:
t = _("repodata dictionary is corrupted")
raise exceptionTools.InvalidData("InvalidData: %s" % (t,))
if repodata['repoid'].endswith(etpConst['packagesext']) or repodata.get('in_memory'): # dynamic repository
try:
# no need # etpRepositories[repodata['repoid']]['plain_packages'] = repodata['plain_packages'][:]
etpRepositories[repodata['repoid']]['packages'] = repodata['packages'][:]
smart_package = repodata.get('smartpackage')
if smart_package != None: etpRepositories[repodata['repoid']]['smartpackage'] = smart_package
etpRepositories[repodata['repoid']]['dbpath'] = repodata.get('dbpath')
etpRepositories[repodata['repoid']]['pkgpath'] = repodata.get('pkgpath')
except KeyError:
raise exceptionTools.InvalidData("InvalidData: repodata dictionary is corrupted")
# put at top priority, shift others
etpRepositoriesOrder.insert(0,repodata['repoid'])
else:
# XXX it's boring to keep this in sync with entropyConstants stuff, solutions?
etpRepositories[repodata['repoid']]['plain_packages'] = repodata['plain_packages'][:]
etpRepositories[repodata['repoid']]['packages'] = [x+"/"+etpConst['product'] for x in repodata['plain_packages']]
etpRepositories[repodata['repoid']]['plain_database'] = repodata['plain_database']
etpRepositories[repodata['repoid']]['database'] = repodata['plain_database'] + \
"/" + etpConst['product'] + "/database/" + etpConst['currentarch'] + "/" + etpConst['branch']
if not repodata['dbcformat'] in etpConst['etpdatabasesupportedcformats']:
repodata['dbcformat'] = etpConst['etpdatabasesupportedcformats'][0]
etpRepositories[repodata['repoid']]['dbcformat'] = repodata['dbcformat']
etpRepositories[repodata['repoid']]['dbpath'] = etpConst['etpdatabaseclientdir'] + \
"/" + repodata['repoid'] + "/" + etpConst['product'] + "/" + etpConst['currentarch'] + "/" + etpConst['branch']
# set dbrevision
myrev = self.get_repository_revision(repodata['repoid'])
if myrev == -1:
myrev = 0
etpRepositories[repodata['repoid']]['dbrevision'] = str(myrev)
if repodata.has_key("position"):
etpRepositoriesOrder.insert(repodata['position'],repodata['repoid'])
else:
etpRepositoriesOrder.append(repodata['repoid'])
if not repodata.has_key("service_port"):
repodata['service_port'] = int(etpConst['socket_service']['port'])
if not repodata.has_key("ssl_service_port"):
repodata['ssl_service_port'] = int(etpConst['socket_service']['ssl_port'])
etpRepositories[repodata['repoid']]['service_port'] = repodata['service_port']
etpRepositories[repodata['repoid']]['ssl_service_port'] = repodata['ssl_service_port']
self.repository_move_clear_cache(repodata['repoid'])
# save new etpRepositories to file
self.entropyTools.saveRepositorySettings(repodata)
self.reload_constants()
self.validate_repositories()
def removeRepository(self, repoid, disable = False):
# ensure that all dbs are closed
self.closeAllRepositoryDatabases()
done = False
if etpRepositories.has_key(repoid):
del etpRepositories[repoid]
done = True
if etpRepositoriesExcluded.has_key(repoid):
del etpRepositoriesExcluded[repoid]
done = True
if done:
if repoid in etpRepositoriesOrder:
etpRepositoriesOrder.remove(repoid)
self.repository_move_clear_cache(repoid)
# save new etpRepositories to file
repodata = {}
repodata['repoid'] = repoid
if disable:
self.entropyTools.saveRepositorySettings(repodata, disable = True)
else:
self.entropyTools.saveRepositorySettings(repodata, remove = True)
self.reload_constants()
self.validate_repositories()
def shiftRepository(self, repoid, toidx):
# update etpRepositoriesOrder
etpRepositoriesOrder.remove(repoid)
etpRepositoriesOrder.insert(toidx,repoid)
self.entropyTools.writeOrderedRepositoriesEntries()
self.reload_constants()
self.repository_move_clear_cache(repoid)
self.validate_repositories()
def enableRepository(self, repoid):
self.repository_move_clear_cache(repoid)
# save new etpRepositories to file
repodata = {}
repodata['repoid'] = repoid
self.entropyTools.saveRepositorySettings(repodata, enable = True)
self.reload_constants()
self.validate_repositories()
def disableRepository(self, repoid):
# update etpRepositories
done = False
try:
del etpRepositories[repoid]
done = True
except:
pass
if done:
try:
etpRepositoriesOrder.remove(repoid)
except:
pass
# it's not vital to reset etpRepositoriesOrder counters
self.repository_move_clear_cache(repoid)
# save new etpRepositories to file
repodata = {}
repodata['repoid'] = repoid
self.entropyTools.saveRepositorySettings(repodata, disable = True)
self.reload_constants()
self.validate_repositories()
def get_unsatisfied_dependencies(self, dependencies, deep_deps = False, depcache = {}):
if self.xcache:
c_data = sorted(list(dependencies))
client_checksum = self.clientDbconn.database_checksum()
c_hash = str(hash(tuple(c_data)))+str(hash(deep_deps))+client_checksum
c_hash = "unsat_%s" % (hash(c_hash),)
cached = self.dumpTools.loadobj(etpCache['filter_satisfied_deps']+c_hash)
if cached != None: return cached
cdb_am = self.clientDbconn.atomMatch
am = self.atomMatch
open_repo = self.openRepositoryDatabase
intf_error = self.dbapi2.InterfaceError
cdb_getversioning = self.clientDbconn.getVersioningData
cdb_retrieveneededraw = self.clientDbconn.retrieveNeededRaw
etp_cmp = self.entropyTools.entropyCompareVersions
do_needed_check = False
def fm_dep(dependency):
cached = depcache.get(dependency)
if cached != None: return cached
### conflict
if dependency.startswith("!"):
idpackage,rc = cdb_am(dependency[1:])
if idpackage != -1:
depcache[dependency] = dependency
return dependency
depcache[dependency] = 0
return 0
c_id,c_rc = cdb_am(dependency)
if c_id == -1:
depcache[dependency] = dependency
return dependency
#if not deep_deps and not do_needed_check:
# depcache[dependency] = 0
# return 0
r_id,r_repo = am(dependency)
if r_id == -1:
depcache[dependency] = dependency
return dependency
if do_needed_check:
dbconn = open_repo(r_repo)
installed_needed = cdb_retrieveneededraw(c_id)
repo_needed = dbconn.retrieveNeededRaw(r_id)
if installed_needed != repo_needed:
return dependency
#elif not deep_deps:
# return 0
dbconn = open_repo(r_repo)
try:
repo_pkgver, repo_pkgtag, repo_pkgrev = dbconn.getVersioningData(r_id)
except (intf_error,TypeError,):
# package entry is broken
return dependency
try:
installedVer, installedTag, installedRev = cdb_getversioning(c_id)
except TypeError: # corrupted entry?
installedVer = "0"
installedTag = ''
installedRev = 0
vcmp = etp_cmp((repo_pkgver,repo_pkgtag,repo_pkgrev,), (installedVer,installedTag,installedRev,))
if vcmp != 0:
if not deep_deps and ((repo_pkgver,repo_pkgtag,) == (installedVer,installedTag,)) and (repo_pkgrev != installedRev):
depcache[dependency] = 0
return 0
depcache[dependency] = dependency
return dependency
depcache[dependency] = 0
return 0
unsatisfied = map(fm_dep,dependencies)
unsatisfied = set([x for x in unsatisfied if x != 0])
if self.xcache:
try:
self.dumpTools.dumpobj(etpCache['filter_satisfied_deps']+c_hash,unsatisfied)
except IOError:
pass
return unsatisfied
def get_masked_package_reason(self, match):
idpackage, repoid = match
dbconn = self.openRepositoryDatabase(repoid)
idpackage, idreason = dbconn.idpackageValidator(idpackage)
masked = False
if idpackage == -1: masked = True
return masked, idreason, self.SystemSettings['pkg_masking_reasons'].get(idreason)
def get_masked_packages_tree(self, match, atoms = False, flat = False, matchfilter = set()):
if not isinstance(matchfilter,set):
matchfilter = set()
maskedtree = {}
mybuffer = self.entropyTools.lifobuffer()
depcache = set()
treelevel = -1
match_id, match_repo = match
mydbconn = self.openRepositoryDatabase(match_repo)
myatom = mydbconn.retrieveAtom(match_id)
idpackage, idreason = mydbconn.idpackageValidator(match_id)
if idpackage == -1:
treelevel += 1
if atoms:
mydict = {myatom: idreason,}
else:
mydict = {match: idreason,}
if flat:
maskedtree.update(mydict)
else:
maskedtree[treelevel] = {}
maskedtree[treelevel].update(mydict)
mydeps = mydbconn.retrieveDependencies(match_id)
for mydep in mydeps: mybuffer.push(mydep)
mydep = mybuffer.pop()
open_db = self.openRepositoryDatabase
am = self.atomMatch
while mydep:
if mydep in depcache:
mydep = mybuffer.pop()
continue
depcache.add(mydep)
idpackage, repoid = am(mydep)
if (idpackage, repoid) in matchfilter:
mydep = mybuffer.pop()
continue
if idpackage != -1:
# doing even here because atomMatch with packagesFilter = False can pull
# something different
matchfilter.add((idpackage, repoid))
# collect masked
if idpackage == -1:
idpackage, repoid = am(mydep, packagesFilter = False)
if idpackage != -1:
treelevel += 1
if not maskedtree.has_key(treelevel) and not flat:
maskedtree[treelevel] = {}
dbconn = open_db(repoid)
vidpackage, idreason = dbconn.idpackageValidator(idpackage)
if atoms:
mydict = {dbconn.retrieveAtom(idpackage): idreason}
else:
mydict = {(idpackage,repoid): idreason}
if flat: maskedtree.update(mydict)
else: maskedtree[treelevel].update(mydict)
# push its dep into the buffer
if idpackage != -1:
matchfilter.add((idpackage, repoid))
dbconn = open_db(repoid)
owndeps = dbconn.retrieveDependencies(idpackage)
for owndep in owndeps:
mybuffer.push(owndep)
mydep = mybuffer.pop()
return maskedtree
def generate_dependency_tree(self, atomInfo, empty_deps = False, deep_deps = False, matchfilter = set(), flat = False, filter_unsat_cache = {}, treecache = set(), keyslotcache = set()):
if not isinstance(matchfilter,set):
matchfilter = set()
mydbconn = self.openRepositoryDatabase(atomInfo[1])
myatom = mydbconn.retrieveAtom(atomInfo[0])
# caches
# special events
deps_not_found = set()
conflicts = set()
mydep = (1,myatom)
mybuffer = self.entropyTools.lifobuffer()
deptree = set()
if atomInfo not in matchfilter:
deptree.add((1,atomInfo))
virgin = True
open_repo = self.openRepositoryDatabase
atom_match = self.atomMatch
cdb_atom_match = self.clientDbconn.atomMatch
lookup_conflict_replacement = self._lookup_conflict_replacement
lookup_library_breakages = self._lookup_library_breakages
lookup_inverse_dependencies = self._lookup_inverse_dependencies
get_unsatisfied_deps = self.get_unsatisfied_dependencies
def my_dep_filter(x):
if x in treecache: return False
if tuple(x.split(":")) in keyslotcache: return False
return True
while mydep:
dep_level, dep_atom = mydep
# already analyzed in this call
if dep_atom in treecache:
mydep = mybuffer.pop()
continue
treecache.add(dep_atom)
if dep_atom == None: # corrupted entry
mydep = mybuffer.pop()
continue
# conflicts
if dep_atom[0] == "!":
c_idpackage, xst = cdb_atom_match(dep_atom[1:])
if c_idpackage != -1:
myreplacement = lookup_conflict_replacement(dep_atom[1:], c_idpackage, deep_deps = deep_deps)
if (myreplacement != None) and (myreplacement not in treecache):
mybuffer.push((dep_level+1,myreplacement))
else:
conflicts.add(c_idpackage)
mydep = mybuffer.pop()
continue
# atom found?
if virgin:
virgin = False
m_idpackage, m_repo = atomInfo
dbconn = open_repo(m_repo)
myidpackage, idreason = dbconn.idpackageValidator(m_idpackage)
if myidpackage == -1: m_idpackage = -1
else:
m_idpackage, m_repo = atom_match(dep_atom)
if m_idpackage == -1:
deps_not_found.add(dep_atom)
mydep = mybuffer.pop()
continue
# check if atom has been already pulled in
matchdb = open_repo(m_repo)
matchatom = matchdb.retrieveAtom(m_idpackage)
matchkey, matchslot = matchdb.retrieveKeySlot(m_idpackage)
if (dep_atom != matchatom) and (matchatom in treecache):
mydep = mybuffer.pop()
continue
treecache.add(matchatom)
# check if key + slot has been already pulled in
if (matchslot,matchkey) in keyslotcache:
mydep = mybuffer.pop()
continue
else:
keyslotcache.add((matchslot,matchkey))
match = (m_idpackage, m_repo,)
# result already analyzed?
if match in matchfilter:
mydep = mybuffer.pop()
continue
# already analyzed by the calling function
if match in matchfilter:
mydep = mybuffer.pop()
continue
matchfilter.add(match)
treedepth = dep_level+1
# all checks passed, well done
matchfilter.add(match)
deptree.add((dep_level,match)) # add match
# extra hooks
cm_idpackage, cm_result = cdb_atom_match(matchkey, matchSlot = matchslot)
if cm_idpackage != -1:
broken_atoms = lookup_library_breakages(match, (cm_idpackage, cm_result,), deep_deps = deep_deps)
inverse_deps = lookup_inverse_dependencies(match, (cm_idpackage, cm_result,))
if inverse_deps:
deptree.remove((dep_level,match))
for ikey,islot in inverse_deps:
iks_str = '%s:%s' % (ikey,islot,)
if ((ikey,islot) not in keyslotcache) and (iks_str not in treecache):
mybuffer.push((dep_level,iks_str))
keyslotcache.add((ikey,islot))
deptree.add((treedepth,match))
treedepth += 1
for x in broken_atoms:
if (tuple(x.split(":")) not in keyslotcache) and (x not in treecache):
mybuffer.push((treedepth,x))
myundeps = filter(my_dep_filter,matchdb.retrieveDependenciesList(m_idpackage))
if not empty_deps:
myundeps = filter(my_dep_filter,get_unsatisfied_deps(myundeps, deep_deps, depcache = filter_unsat_cache))
# PDEPENDs support
if myundeps:
post_deps = [x for x in matchdb.retrievePostDependencies(m_idpackage) if x in myundeps]
myundeps = [x for x in myundeps if x not in post_deps]
for x in post_deps: mybuffer.push((-1,x)) # always after the package itself
for x in myundeps: mybuffer.push((treedepth,x))
mydep = mybuffer.pop()
if deps_not_found:
return list(deps_not_found),-2
if flat: return [x[1] for x in deptree],0
newdeptree = {}
for key,item in deptree:
if key not in newdeptree: newdeptree[key] = set()
newdeptree[key].add(item)
# conflicts
newdeptree[0] = conflicts
return newdeptree,0 # note: newtree[0] contains possible conflicts
def _lookup_system_mask_repository_deps(self):
data = self.SystemSettings['repos_system_mask']
if not data: return []
mydata = []
cached_items = set()
for atom in data:
mymatch = self.atomMatch(atom)
if mymatch[0] == -1: # ignore missing ones intentionally
continue
if mymatch in cached_items:
continue
if mymatch not in mydata:
# check if not found
myaction = self.get_package_action(mymatch)
if myaction != 0: mydata.append(mymatch) # upgrades, installs and downgrades allowed
cached_items.add(mymatch)
return mydata
def _lookup_conflict_replacement(self, conflict_atom, client_idpackage, deep_deps):
if self.entropyTools.isjustname(conflict_atom):
return None
conflict_match = self.atomMatch(conflict_atom)
mykey, myslot = self.clientDbconn.retrieveKeySlot(client_idpackage)
new_match = self.atomMatch(mykey, matchSlot = myslot)
if (conflict_match == new_match) or (new_match[1] == 1):
return None
action = self.get_package_action(new_match)
if (action == 0) and (not deep_deps):
return None
return "%s:%s" % (mykey,myslot,)
def _lookup_inverse_dependencies(self, match, clientmatch):
cmpstat = self.get_package_action(match)
if cmpstat == 0: return set()
keyslots = set()
mydepends = self.clientDbconn.retrieveDepends(clientmatch[0])
am = self.atomMatch
cdb_rdeps = self.clientDbconn.retrieveDependencies
cdb_rks = self.clientDbconn.retrieveKeySlot
gpa = self.get_package_action
keyslots_cache = set()
match_cache = {}
for idpackage in mydepends:
key, slot = cdb_rks(idpackage)
if (key,slot) in keyslots_cache: continue
keyslots_cache.add((key,slot))
if (key,slot) in keyslots: continue
# grab its deps
mydeps = cdb_rdeps(idpackage)
found = False
for mydep in mydeps:
mymatch = match_cache.get(mydep, 0)
if mymatch == 0:
mymatch = am(mydep)
match_cache[mydep] = mymatch
if mymatch == match:
found = True
break
if not found:
mymatch = am(key, matchSlot = slot)
if mymatch[0] == -1: continue
cmpstat = gpa(mymatch)
if cmpstat == 0: continue
keyslots.add((key,slot))
return keyslots
def _lookup_library_breakages(self, match, clientmatch, deep_deps = False):
# there is no need to update this cache when "match" will be installed, because at that point
# clientmatch[0] will differ.
c_hash = "%s|%s|%s" % (hash(tuple(match)),hash(deep_deps),hash(tuple(clientmatch)),)
c_hash = "%s%s" % (etpCache['library_breakage'],hash(c_hash),)
if self.xcache:
cached = self.Cacher.pop(c_hash)
if cached != None: return cached
# these should be pulled in before
repo_atoms = set()
# these can be pulled in after
client_atoms = set()
matchdb = self.openRepositoryDatabase(match[1])
reponeeded = matchdb.retrieveNeeded(match[0], extended = True, format = True)
clientneeded = self.clientDbconn.retrieveNeeded(clientmatch[0], extended = True, format = True)
repo_split = [x.split(".so")[0] for x in reponeeded]
client_split = [x.split(".so")[0] for x in clientneeded]
client_side = [x for x in clientneeded if (x not in reponeeded) and (x.split(".so")[0] in repo_split)]
repo_side = [x for x in reponeeded if (x not in clientneeded) and (x.split(".so")[0] in client_split)]
del clientneeded,client_split,repo_split
# all the packages in client_side should be pulled in and updated
client_idpackages = set()
for needed in client_side: client_idpackages |= self.clientDbconn.searchNeeded(needed)
client_keyslots = set()
def mymf(idpackage):
if idpackage == clientmatch[0]: return 0
return self.clientDbconn.retrieveKeySlot(idpackage)
client_keyslots = set([x for x in map(mymf,client_idpackages) if x != 0])
# all the packages in repo_side should be pulled in too
repodata = {}
for needed in repo_side:
repodata[needed] = reponeeded[needed]
del repo_side,reponeeded
repo_dependencies = matchdb.retrieveDependencies(match[0])
matched_deps = set()
matched_repos = set()
for dependency in repo_dependencies:
depmatch = self.atomMatch(dependency)
if depmatch[0] == -1:
continue
matched_repos.add(depmatch[1])
matched_deps.add(depmatch)
matched_repos = [x for x in etpRepositoriesOrder if x in matched_repos]
found_matches = set()
for needed in repodata:
for myrepo in matched_repos:
mydbc = self.openRepositoryDatabase(myrepo)
solved_needed = mydbc.resolveNeeded(needed, elfclass = repodata[needed])
found = False
for idpackage,myfile in solved_needed:
x = (idpackage,myrepo)
if x in matched_deps:
found_matches.add(x)
found = True
break
if found:
break
for idpackage,repo in found_matches:
if not deep_deps:
cmpstat = self.get_package_action((idpackage,repo))
if cmpstat == 0:
continue
mydbc = self.openRepositoryDatabase(repo)
repo_atoms.add(mydbc.retrieveAtom(idpackage))
for key, slot in client_keyslots:
idpackage, repo = self.atomMatch(key, matchSlot = slot)
if idpackage == -1:
continue
if not deep_deps:
cmpstat = self.get_package_action((idpackage, repo))
if cmpstat == 0:
continue
mydbc = self.openRepositoryDatabase(repo)
client_atoms.add(mydbc.retrieveAtom(idpackage))
client_atoms |= repo_atoms
if self.xcache:
self.Cacher.push(c_hash,client_atoms.copy())
return client_atoms
def get_required_packages(self, matched_atoms, empty_deps = False, deep_deps = False, quiet = False):
c_hash = "%s%s" % (etpCache['dep_tree'],hash("%s|%s|%s|%s" % (
hash(frozenset(sorted(matched_atoms))),hash(empty_deps),
hash(deep_deps),self.clientDbconn.database_checksum(),
)),)
if self.xcache:
cached = self.Cacher.pop(c_hash)
if cached != None: return cached
deptree = {}
deptree[0] = set()
atomlen = len(matched_atoms); count = 0
error_generated = 0
error_tree = set()
# check if there are repositories needing some mandatory packages
forced_matches = self._lookup_system_mask_repository_deps()
if forced_matches:
if isinstance(matched_atoms, list):
matched_atoms = forced_matches + [x for x in matched_atoms if x not in forced_matches]
elif isinstance(matched_atoms, set): # we cannot do anything about the order here
matched_atoms |= set(forced_matches)
sort_dep_text = _("Sorting dependencies")
filter_unsat_cache = {}
treecache = set()
keyslotcache = set()
matchfilter = set()
for atomInfo in matched_atoms:
if not quiet:
count += 1
if (count%10 == 0) or (count == atomlen) or (count == 1):
self.updateProgress(sort_dep_text, importance = 0, type = "info", back = True, header = ":: ", footer = " ::", percent = True, count = (count,atomlen))
if atomInfo in matchfilter: continue
newtree, result = self.generate_dependency_tree(
atomInfo, empty_deps, deep_deps,
matchfilter = matchfilter, filter_unsat_cache = filter_unsat_cache, treecache = treecache,
keyslotcache = keyslotcache
)
if result == -2: # deps not found
error_generated = -2
error_tree |= set(newtree) # it is a list, we convert it into set and update error_tree
elif (result != 0):
return newtree, result
elif newtree:
# add conflicts
max_parent_key = max(deptree)
deptree[0] |= newtree.pop(0)
levelcount = 0
for mylevel in sorted(newtree.keys(), reverse = True):
levelcount += 1
deptree[max_parent_key+levelcount] = newtree.get(mylevel)
if error_generated != 0:
return error_tree,error_generated
if self.xcache:
self.Cacher.push(c_hash,(deptree,0)[:])
return deptree,0
def _filter_depends_multimatched_atoms(self, idpackage, depends, monotree):
remove_depends = set()
for d_idpackage in depends:
mydeps = self.clientDbconn.retrieveDependencies(d_idpackage)
for mydep in mydeps:
matches, rslt = self.clientDbconn.atomMatch(mydep, multiMatch = True)
if rslt == 1: continue
if idpackage in matches and len(matches) > 1:
# are all in depends?
for mymatch in matches:
if mymatch not in depends and mymatch not in monotree:
remove_depends.add(d_idpackage)
break
depends -= remove_depends
return depends
def generate_depends_tree(self, idpackages, deep = False):
c_hash = "%s%s" % (etpCache['depends_tree'],hash("%s|%s" % (hash(tuple(sorted(idpackages))),hash(deep),),),)
if self.xcache:
cached = self.Cacher.pop(c_hash)
if cached != None: return cached
dependscache = set()
treeview = set(idpackages)
treelevel = set(idpackages)
tree = {}
treedepth = 0 # I start from level 1 because level 0 is idpackages itself
tree[treedepth] = set(idpackages)
monotree = set(idpackages) # monodimensional tree
# check if dependstable is sane before beginning
self.clientDbconn.retrieveDepends(idpackages[0])
count = 0
rem_dep_text = _("Calculating removable depends of")
while 1:
treedepth += 1
tree[treedepth] = set()
for idpackage in treelevel:
count += 1
p_atom = self.clientDbconn.retrieveAtom(idpackage)
self.updateProgress(
blue(rem_dep_text + " %s" % (red(p_atom),)),
importance = 0,
type = "info",
back = True,
header = '|/-\\'[count%4]+" "
)
systempkg = not self.validatePackageRemoval(idpackage)
if (idpackage in dependscache) or systempkg:
if idpackage in treeview:
treeview.remove(idpackage)
continue
# obtain its depends
depends = self.clientDbconn.retrieveDepends(idpackage)
# filter already satisfied ones
depends = set([x for x in depends if x not in monotree])
depends = set([x for x in depends if self.validatePackageRemoval(x)])
if depends:
depends = self._filter_depends_multimatched_atoms(idpackage, depends, monotree)
if depends: # something depends on idpackage
tree[treedepth] |= depends
monotree |= depends
treeview |= depends
elif deep: # if deep, grab its dependencies and check
mydeps = set()
for x in self.clientDbconn.retrieveDependencies(idpackage):
match = self.clientDbconn.atomMatch(x)
if match[0] != -1:
mydeps.add(match[0])
# now filter them
mydeps = [x for x in mydeps if x not in monotree and not (self.clientDbconn.isSystemPackage(x) or self.is_installed_idpackage_in_system_mask(x) )]
for x in mydeps:
mydepends = self.clientDbconn.retrieveDepends(x)
mydepends -= set([y for y in mydepends if y not in monotree])
if not mydepends:
tree[treedepth].add(x)
monotree.add(x)
treeview.add(x)
dependscache.add(idpackage)
if idpackage in treeview:
treeview.remove(idpackage)
treelevel = treeview.copy()
if not treelevel:
if not tree[treedepth]:
del tree[treedepth] # probably the last one is empty then
break
# now filter newtree
for count in sorted(tree.keys(), reverse = True):
x = 0
while x < count:
tree[x] -= tree[count]
x += 1
if self.xcache:
self.Cacher.push(c_hash,(tree,0)[:])
return tree,0 # treeview is used to show deps while tree is used to run the dependency code.
def list_repo_categories(self):
categories = set()
for repo in self.validRepositories:
dbconn = self.openRepositoryDatabase(repo)
catsdata = dbconn.listAllCategories()
categories.update(set([x[1] for x in catsdata]))
return categories
def list_repo_packages_in_category(self, category):
pkg_matches = []
for repo in self.validRepositories:
dbconn = self.openRepositoryDatabase(repo)
catsdata = dbconn.searchPackagesByCategory(category, branch = etpConst['branch'])
pkg_matches.extend([(x[1],repo,) for x in catsdata if (x[1],repo,) not in pkg_matches])
return pkg_matches
def get_category_description_data(self, category):
data = {}
for repo in self.validRepositories:
try:
dbconn = self.openRepositoryDatabase(repo)
except exceptionTools.RepositoryError:
continue
try:
data = dbconn.retrieveCategoryDescription(category)
except (dbapi2.OperationalError, dbapi2.IntegrityError,):
continue
if data: break
return data
def list_installed_packages_in_category(self, category):
pkg_matches = set([x[1] for x in self.clientDbconn.searchPackagesByCategory(category)])
return pkg_matches
def all_repositories_checksum(self):
sum_hashes = ''
for repo in self.validRepositories:
try:
dbconn = self.openRepositoryDatabase(repo)
except (exceptionTools.RepositoryError):
continue # repo not available
try:
sum_hashes += dbconn.database_checksum()
except dbapi2.OperationalError:
pass
return sum_hashes
def get_available_packages_chash(self, branch):
repo_digest = self.all_repositories_checksum()
# client digest not needed, cache is kept updated
c_hash = "%s%s%s" % (hash(repo_digest),hash(branch),hash(tuple(self.validRepositories)),)
return str(hash(c_hash))
def get_available_packages_cache(self, branch = etpConst['branch'], myhash = None):
if myhash == None:
myhash = self.get_available_packages_chash(branch)
disk_cache = self.Cacher.pop(etpCache['world_available'])
try:
if disk_cache['chash'] == myhash:
return disk_cache['available']
except (KeyError,TypeError,):
return None
# this function searches all the not installed packages available in the repositories
def calculate_available_packages(self, use_cache = True):
c_hash = self.get_available_packages_chash(etpConst['branch'])
if use_cache and self.xcache:
cached = self.get_available_packages_cache(myhash = c_hash)
if cached != None:
return cached
available = []
self.setTotalCycles(len(self.validRepositories))
avail_dep_text = _("Calculating available packages for")
for repo in self.validRepositories:
try:
dbconn = self.openRepositoryDatabase(repo)
dbconn.validateDatabase()
except (exceptionTools.RepositoryError,exceptionTools.SystemDatabaseError):
self.cycleDone()
continue
idpackages = [ x for x in dbconn.listAllIdpackages(branch = etpConst['branch'], branch_operator = "<=", order_by = 'atom') \
if dbconn.idpackageValidator(x)[0] != -1 ]
count = 0
maxlen = len(idpackages)
myavailable = []
do_break = False
for idpackage in idpackages:
if do_break:
break
count += 1
if (count % 10 == 0) or (count == 1) or (count == maxlen):
self.updateProgress(
avail_dep_text + " %s" % (repo,),
importance = 0,
type = "info",
back = True,
header = "::",
count = (count,maxlen),
percent = True,
footer = " ::"
)
# get key + slot
try:
key, slot = dbconn.retrieveKeySlot(idpackage)
matches = self.clientDbconn.searchKeySlot(key, slot)
except (self.dbapi2.DatabaseError,self.dbapi2.IntegrityError,self.dbapi2.OperationalError,):
self.cycleDone()
do_break = True
continue
if not matches: myavailable.append((idpackage,repo))
available += myavailable[:]
self.cycleDone()
if self.xcache:
data = {}
data['chash'] = c_hash
data['available'] = available
self.Cacher.push(etpCache['world_available'],data.copy())
return available
def get_world_update_cache(self, empty_deps, branch = etpConst['branch'], db_digest = None, ignore_spm_downgrades = False):
if self.xcache:
if db_digest == None: db_digest = self.all_repositories_checksum()
c_hash = "%s%s" % (etpCache['world_update'],self.get_world_update_cache_hash(db_digest, empty_deps, branch, ignore_spm_downgrades),)
disk_cache = self.Cacher.pop(c_hash)
if disk_cache != None:
try:
return disk_cache['r']
except (KeyError, TypeError):
return None
def get_world_update_cache_hash(self, db_digest, empty_deps, branch, ignore_spm_downgrades):
c_hash = "%s%s%s%s%s%s" % (
hash(db_digest),empty_deps,hash(tuple(self.validRepositories)),
hash(tuple(etpRepositoriesOrder)), branch, ignore_spm_downgrades,
)
return str(hash(c_hash))
def calculate_world_updates(
self,
empty_deps = False,
branch = etpConst['branch'],
ignore_spm_downgrades = etpConst['spm']['ignore-spm-downgrades'],
use_cache = True
):
db_digest = self.all_repositories_checksum()
if use_cache and self.xcache:
cached = self.get_world_update_cache(empty_deps = empty_deps, branch = branch, db_digest = db_digest, ignore_spm_downgrades = ignore_spm_downgrades)
if cached != None: return cached
update = []
remove = []
fine = []
# get all the installed packages
idpackages = self.clientDbconn.listAllIdpackages(order_by = 'atom')
maxlen = len(idpackages)
count = 0
mytxt = _("Calculating world packages")
for idpackage in idpackages:
count += 1
if (count%10 == 0) or (count == maxlen) or (count == 1):
self.updateProgress(
mytxt,
importance = 0,
type = "info",
back = True,
header = ":: ",
count = (count,maxlen),
percent = True,
footer = " ::"
)
mystrictdata = self.clientDbconn.getStrictData(idpackage)
# check against broken entries, or removed during iteration
if mystrictdata == None:
continue
use_match_cache = True
do_continue = False
while 1:
try:
match = self.atomMatch(
mystrictdata[0],
matchSlot = mystrictdata[1],
matchBranches = (branch,),
extendedResults = True,
useCache = use_match_cache
)
except dbapi2.OperationalError:
# ouch, but don't crash here
do_continue = True
break
try:
m_idpackage = match[0][0]
except TypeError:
if not use_match_cache: raise
use_match_cache = False
continue
break
if do_continue: continue
# now compare
# version: mystrictdata[2]
# tag: mystrictdata[3]
# revision: mystrictdata[4]
if (m_idpackage != -1):
repoid = match[1]
version = match[0][1]
tag = match[0][2]
revision = match[0][3]
if empty_deps:
if (m_idpackage,repoid) not in update:
update.append((m_idpackage,repoid))
continue
elif (mystrictdata[2] != version):
# different versions
if (m_idpackage,repoid) not in update:
update.append((m_idpackage,repoid))
continue
elif (mystrictdata[3] != tag):
# different tags
if (m_idpackage,repoid) not in update:
update.append((m_idpackage,repoid))
continue
elif (mystrictdata[4] != revision):
# different revision
if mystrictdata[4] == 9999 and ignore_spm_downgrades:
# no difference, we're ignoring revision 9999
fine.append(mystrictdata[5])
continue
else:
if (m_idpackage,repoid) not in update:
update.append((m_idpackage,repoid))
continue
else:
# no difference
fine.append(mystrictdata[5])
continue
# don't take action if it's just masked
maskedresults = self.atomMatch(mystrictdata[0], matchSlot = mystrictdata[1], matchBranches = (branch,), packagesFilter = False)
if maskedresults[0] == -1:
remove.append(idpackage)
# look for packages that would match key with any slot (for eg: gcc, kernel updates)
matchresults = self.atomMatch(mystrictdata[0], matchBranches = (branch,))
if matchresults[0] != -1:
m_action = self.get_package_action(matchresults)
if m_action > 0 and (matchresults not in update):
update.append(matchresults)
if self.xcache:
c_hash = self.get_world_update_cache_hash(db_digest, empty_deps, branch, ignore_spm_downgrades)
data = {
'r': (update, remove, fine,),
'empty_deps': empty_deps,
}
self.Cacher.push("%s%s" % (etpCache['world_update'],c_hash,), data.copy())
return update, remove, fine
def get_match_conflicts(self, match):
m_id, m_repo = match
dbconn = self.openRepositoryDatabase(m_repo)
conflicts = dbconn.retrieveConflicts(m_id)
found_conflicts = set()
for conflict in conflicts:
my_m_id, my_m_rc = self.clientDbconn.atomMatch(conflict)
if my_m_id != -1:
# check if the package shares the same slot
match_data = dbconn.retrieveKeySlot(m_id)
installed_match_data = self.clientDbconn.retrieveKeySlot(my_m_id)
if match_data != installed_match_data:
found_conflicts.add(my_m_id)
return found_conflicts
def is_match_masked(self, match, live_check = True):
m_id, m_repo = match
dbconn = self.openRepositoryDatabase(m_repo)
idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check)
if idpackage != -1:
return False
return True
def is_match_masked_by_user(self, match, live_check = True):
# (query_status,masked?,)
m_id, m_repo = match
if m_repo not in self.validRepositories: return False
dbconn = self.openRepositoryDatabase(m_repo)
idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check)
if idpackage != -1: return False #,False
myr = self.SystemSettings['pkg_masking_reference']
user_masks = [myr['user_package_mask'],myr['user_license_mask'],myr['user_live_mask']]
if idreason in user_masks:
return True #,True
return False #,True
def is_match_unmasked_by_user(self, match, live_check = True):
# (query_status,unmasked?,)
m_id, m_repo = match
if m_repo not in self.validRepositories: return False
dbconn = self.openRepositoryDatabase(m_repo)
idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check)
if idpackage == -1: return False #,False
myr = self.SystemSettings['pkg_masking_reference']
user_masks = [
myr['user_package_unmask'],myr['user_live_unmask'],myr['user_package_keywords'],
myr['user_repo_package_keywords_all'], myr['user_repo_package_keywords']
]
if idreason in user_masks:
return True #,True
return False #,True
def mask_match(self, match, method = 'atom', dry_run = False, clean_all_cache = False):
if self.is_match_masked(match, live_check = False): return True
methods = {
'atom': self.mask_match_by_atom,
'keyslot': self.mask_match_by_keyslot,
}
rc = self._mask_unmask_match(match, method, methods, dry_run = dry_run, clean_all_cache = clean_all_cache)
if dry_run: # inject if done "live"
self.SystemSettings['live_packagemasking']['unmask_matches'].discard(match)
self.SystemSettings['live_packagemasking']['mask_matches'].add(match)
return rc
def unmask_match(self, match, method = 'atom', dry_run = False, clean_all_cache = False):
if not self.is_match_masked(match, live_check = False): return True
methods = {
'atom': self.unmask_match_by_atom,
'keyslot': self.unmask_match_by_keyslot,
}
rc = self._mask_unmask_match(match, method, methods, dry_run = dry_run, clean_all_cache = clean_all_cache)
if dry_run: # inject if done "live"
self.SystemSettings['live_packagemasking']['unmask_matches'].add(match)
self.SystemSettings['live_packagemasking']['mask_matches'].discard(match)
return rc
def _mask_unmask_match(self, match, method, methods_reference, dry_run = False, clean_all_cache = False):
f = methods_reference.get(method)
if not callable(f):
raise exceptionTools.IncorrectParameter('IncorrectParameter: %s: %s' % (_("not a valid method"),method,) )
self.Cacher.sync(wait = True)
done = f(match, dry_run)
if done: self.SystemSettings.clear()
# clear atomMatch cache anyway
if clean_all_cache and not dry_run:
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(self.atomMatchCacheKey)
self.clear_dump_cache(etpCache['dep_tree'])
self.clear_dump_cache("%s/%s%s/" % (etpCache['dbMatch'],etpConst['dbnamerepoprefix'],match[1],))
self.clear_dump_cache("%s/%s%s/" % (etpCache['dbSearch'],etpConst['dbnamerepoprefix'],match[1],))
self.package_match_validator_cache.clear()
return done
def unmask_match_by_atom(self, match, dry_run = False):
m_id, m_repo = match
dbconn = self.openRepositoryDatabase(m_repo)
atom = dbconn.retrieveAtom(m_id)
return self.unmask_match_generic(match, atom, dry_run = dry_run)
def unmask_match_by_keyslot(self, match, dry_run = False):
m_id, m_repo = match
dbconn = self.openRepositoryDatabase(m_repo)
keyslot = "%s:%s" % dbconn.retrieveKeySlot(m_id)
return self.unmask_match_generic(match, keyslot, dry_run = dry_run)
def mask_match_by_atom(self, match, dry_run = False):
m_id, m_repo = match
dbconn = self.openRepositoryDatabase(m_repo)
atom = dbconn.retrieveAtom(m_id)
return self.mask_match_generic(match, atom, dry_run = dry_run)
def mask_match_by_keyslot(self, match, dry_run = False):
m_id, m_repo = match
dbconn = self.openRepositoryDatabase(m_repo)
keyslot = "%s:%s" % dbconn.retrieveKeySlot(m_id)
return self.mask_match_generic(match, keyslot, dry_run = dry_run)
def unmask_match_generic(self, match, keyword, dry_run = False):
self.clear_match_mask(match, dry_run)
m_file = self.SystemSettings.etpSettingFiles['unmask']
return self._mask_unmask_match_generic(keyword, m_file, dry_run = dry_run)
def mask_match_generic(self, match, keyword, dry_run = False):
self.clear_match_mask(match, dry_run)
m_file = self.SystemSettings.etpSettingFiles['mask']
return self._mask_unmask_match_generic(keyword, m_file, dry_run = dry_run)
def _mask_unmask_match_generic(self, keyword, m_file, dry_run = False):
exist = False
if not os.path.isfile(m_file):
if not os.access(os.path.dirname(m_file),os.W_OK):
return False # cannot write
elif not os.access(m_file, os.W_OK):
return False
elif not dry_run:
exist = True
if dry_run:
return True
content = []
if exist:
f = open(m_file,"r")
content = [x.strip() for x in f.readlines()]
f.close()
content.append(keyword)
m_file_tmp = m_file+".tmp"
f = open(m_file_tmp,"w")
for line in content:
f.write(line+"\n")
f.flush()
f.close()
shutil.move(m_file_tmp,m_file)
return True
def clear_match_mask(self, match, dry_run = False):
masking_list = [self.SystemSettings.etpSettingFiles['mask'],self.SystemSettings.etpSettingFiles['unmask']]
return self._clear_match_generic(match, masking_list = masking_list, dry_run = dry_run)
def _clear_match_generic(self, match, masking_list = [], dry_run = False):
self.SystemSettings['live_packagemasking']['unmask_matches'].discard(match)
self.SystemSettings['live_packagemasking']['mask_matches'].discard(match)
if dry_run: return
for mask_file in masking_list:
if not (os.path.isfile(mask_file) and os.access(mask_file,os.W_OK)): continue
f = open(mask_file,"r")
newf = self.entropyTools.open_buffer()
line = f.readline()
while line:
line = line.strip()
if line.startswith("#"):
newf.write(line+"\n")
line = f.readline()
continue
elif not line:
newf.write("\n")
line = f.readline()
continue
mymatch = self.atomMatch(line, packagesFilter = False)
if mymatch == match:
line = f.readline()
continue
newf.write(line+"\n")
line = f.readline()
f.close()
tmpfile = mask_file+".w_tmp"
f = open(tmpfile,"w")
f.write(newf.getvalue())
f.flush()
f.close()
newf.close()
shutil.move(tmpfile,mask_file)
def add_user_package_set(self, set_name, set_atoms):
def _ensure_package_sets_dir():
sets_dir = etpConst['confsetsdir']
if not os.path.isdir(sets_dir):
if os.path.lexists(sets_dir):
os.remove(sets_dir)
os.makedirs(sets_dir,0755)
const_setup_perms(sets_dir, etpConst['entropygid'])
try:
set_name = str(set_name)
except (UnicodeEncodeError,UnicodeDecodeError,):
raise exceptionTools.InvalidPackageSet("InvalidPackageSet: %s %s" % (set_name,_("must be an ASCII string"),))
if set_name.startswith(etpConst['packagesetprefix']):
raise exceptionTools.InvalidPackageSet("InvalidPackageSet: %s %s '%s'" % (set_name,_("cannot start with"),etpConst['packagesetprefix'],))
set_match, rc = self.packageSetMatch(set_name)
if rc: return -1,_("Name already taken")
_ensure_package_sets_dir()
set_file = os.path.join(etpConst['confsetsdir'],set_name)
if os.path.isfile(set_file) and os.access(set_file,os.W_OK):
try:
os.remove(set_file)
except OSError:
return -2,_("Cannot remove the old element")
if not os.access(os.path.dirname(set_file),os.W_OK):
return -3,_("Cannot create the element")
f = open(set_file,"w")
for x in set_atoms: f.write("%s\n" % (x,))
f.flush()
f.close()
self.SystemSettings['system_package_sets'][set_name] = set(set_atoms)
return 0,_("All fine")
def remove_user_package_set(self, set_name):
try:
set_name = str(set_name)
except (UnicodeEncodeError,UnicodeDecodeError,):
raise exceptionTools.InvalidPackageSet("InvalidPackageSet: %s %s" % (set_name,_("must be an ASCII string"),))
if set_name.startswith(etpConst['packagesetprefix']):
raise exceptionTools.InvalidPackageSet("InvalidPackageSet: %s %s '%s'" % (set_name,_("cannot start with"),etpConst['packagesetprefix'],))
set_match, rc = self.packageSetMatch(set_name)
if not rc: return -1,_("Already removed")
set_id, set_x, set_y = set_match
if set_id != etpConst['userpackagesetsid']:
return -2,_("Not defined by user")
set_file = os.path.join(etpConst['confsetsdir'],set_name)
if os.path.isfile(set_file) and os.access(set_file,os.W_OK):
os.remove(set_file)
if set_name in self.SystemSettings['system_package_sets']:
del self.SystemSettings['system_package_sets'][set_name]
return 0,_("All fine")
return -3,_("Set not found or unable to remove")
# every tbz2 file that would be installed must pass from here
def add_tbz2_to_repos(self, tbz2file):
atoms_contained = []
basefile = os.path.basename(tbz2file)
if os.path.isdir(etpConst['entropyunpackdir']+"/"+basefile[:-5]):
shutil.rmtree(etpConst['entropyunpackdir']+"/"+basefile[:-5])
os.makedirs(etpConst['entropyunpackdir']+"/"+basefile[:-5])
dbfile = self.entropyTools.extractEdb(tbz2file, dbpath = etpConst['entropyunpackdir']+"/"+basefile[:-5]+"/packages.db")
if dbfile == None:
return -1,atoms_contained
etpSys['dirstoclean'].add(os.path.dirname(dbfile))
# add dbfile
repodata = {}
repodata['repoid'] = basefile
repodata['description'] = "Dynamic database from "+basefile
repodata['packages'] = []
repodata['dbpath'] = os.path.dirname(dbfile)
repodata['pkgpath'] = os.path.realpath(tbz2file) # extra info added
repodata['smartpackage'] = False # extra info added
mydbconn = self.openGenericDatabase(dbfile)
# read all idpackages
try:
myidpackages = mydbconn.listAllIdpackages() # all branches admitted from external files
except:
return -2,atoms_contained
if len(myidpackages) > 1:
repodata[basefile]['smartpackage'] = True
for myidpackage in myidpackages:
compiled_arch = mydbconn.retrieveDownloadURL(myidpackage)
if compiled_arch.find("/"+etpSys['arch']+"/") == -1:
return -3,atoms_contained
atoms_contained.append((int(myidpackage),basefile))
self.addRepository(repodata)
self.validate_repositories()
if basefile not in self.validRepositories:
self.removeRepository(basefile)
return -4,atoms_contained
mydbconn.closeDB()
del mydbconn
return 0,atoms_contained
# This is the function that should be used by third party applications
# to retrieve a list of available updates, along with conflicts (removalQueue) and obsoletes
# (removed)
def retrieveWorldQueue(self, empty_deps = False, branch = etpConst['branch']):
update, remove, fine = self.calculate_world_updates(empty_deps = empty_deps, branch = branch)
del fine
data = {}
data['removed'] = list(remove)
data['runQueue'] = []
data['removalQueue'] = []
status = -1
if update:
# calculate install+removal queues
install, removal, status = self.retrieveInstallQueue(update, empty_deps, deep_deps = False)
# update data['removed']
data['removed'] = [x for x in data['removed'] if x not in removal]
data['runQueue'] += install
data['removalQueue'] += removal
return data,status
def validatePackageRemoval(self, idpackage):
pkgatom = self.clientDbconn.retrieveAtom(idpackage)
pkgkey = self.entropyTools.dep_getkey(pkgatom)
if self.is_installed_idpackage_in_system_mask(idpackage):
idpackages = self.SystemSettings['repos_system_mask_installed_keys'].get(pkgkey)
if not idpackages: return False
if len(idpackages) > 1:
return True
return False # sorry!
# did we store the bastard in the db?
system_pkg = self.clientDbconn.isSystemPackage(idpackage)
if not system_pkg: return True
# check if the package is slotted and exist more than one installed first
sysresults = self.clientDbconn.atomMatch(pkgkey, multiMatch = True)
if sysresults[1] == 0:
if len(sysresults[0]) < 2: return False
return True
return False
def retrieveRemovalQueue(self, idpackages, deep = False):
queue = []
if not idpackages:
return queue
treeview, status = self.generate_depends_tree(idpackages, deep = deep)
if status == 0:
for x in range(len(treeview))[::-1]: queue.extend(treeview[x])
return queue
def retrieveInstallQueue(self, matched_atoms, empty_deps, deep_deps, quiet = False):
install = []
removal = []
treepackages, result = self.get_required_packages(matched_atoms, empty_deps, deep_deps, quiet = quiet)
if result == -2:
return treepackages,removal,result
# format
removal = treepackages.pop(0, set())
for x in sorted(treepackages.keys()): install.extend(treepackages[x])
# filter out packages that are in actionQueue comparing key + slot
if install and removal:
myremmatch = {}
for x in removal:
atom = self.clientDbconn.retrieveAtom(x)
# XXX check if users removed idpackage while this whole instance is running
if atom == None: continue
myremmatch[(self.entropyTools.dep_getkey(atom),self.clientDbconn.retrieveSlot(x),)] = x
for pkg_id, pkg_repo in install:
dbconn = self.openRepositoryDatabase(pkg_repo)
testtuple = (self.entropyTools.dep_getkey(dbconn.retrieveAtom(pkg_id)),dbconn.retrieveSlot(pkg_id))
removal.discard(myremmatch.get(testtuple))
return install, sorted(removal), 0
# this function searches into client database for a package matching provided key + slot
# and returns its idpackage or -1 if none found
def retrieveInstalledIdPackage(self, pkgkey, pkgslot):
match = self.clientDbconn.atomMatch(pkgkey, matchSlot = pkgslot)
if match[1] == 0:
return match[0]
return -1
'''
Package interface :: begin
'''
def check_needed_package_download(self, filepath, checksum = None):
# is the file available
if os.path.isfile(etpConst['entropyworkdir']+"/"+filepath):
if checksum is None:
return 0
else:
# check digest
md5res = self.entropyTools.compareMd5(etpConst['entropyworkdir']+"/"+filepath,checksum)
if (md5res):
return 0
else:
return -2
else:
return -1
def fetch_file(self, url, branch, digest = None, resume = True, fetch_file_abort_function = None, filepath = None):
# remove old
filename = os.path.basename(url)
if not filepath:
filepath = etpConst['packagesbindir']+"/"+branch+"/"+filename
filepath_dir = os.path.dirname(filepath)
if not os.path.isdir(filepath_dir):
os.makedirs(filepath_dir,0755)
# load class
fetchConn = self.urlFetcher(url, filepath, resume = resume, abort_check_func = fetch_file_abort_function)
fetchConn.progress = self.progress
# start to download
data_transfer = 0
resumed = False
try:
fetchChecksum = fetchConn.download()
data_transfer = fetchConn.datatransfer
resumed = fetchConn.resumed
except KeyboardInterrupt:
return -4, data_transfer, resumed
except NameError:
raise
except:
return -1, data_transfer, resumed
if fetchChecksum == "-3":
return -3, data_transfer, resumed
del fetchConn
if digest:
if fetchChecksum != digest:
# not properly downloaded
return -2, data_transfer, resumed
else:
return 0, data_transfer, resumed
return 0, data_transfer, resumed
def add_failing_mirror(self, mirrorname,increment = 1):
item = self.mirrorDownloadFailures.get(mirrorname)
if item == None:
self.mirrorDownloadFailures[mirrorname] = increment
else:
self.mirrorDownloadFailures[mirrorname] += increment # add a failure
return self.mirrorDownloadFailures[mirrorname]
def get_failing_mirror_status(self, mirrorname):
item = self.mirrorDownloadFailures.get(mirrorname)
if item == None:
return 0
else:
return item
def fetch_file_on_mirrors(self, repository, branch, filename, digest = False, verified = False, fetch_abort_function = None):
uris = etpRepositories[repository]['packages'][::-1]
remaining = set(uris[:])
if verified: # file is already in place, match_checksum set infoDict['verified'] to True
return 0
mirrorcount = 0
for uri in uris:
if not remaining:
# tried all the mirrors, quitting for error
return 3
mirrorcount += 1
mirrorCountText = "( mirror #"+str(mirrorcount)+" ) "
url = uri+"/"+filename
# check if uri is sane
if self.get_failing_mirror_status(uri) >= 30:
# ohohoh!
self.mirrorDownloadFailures[uri] = 30 # set to 30 for convenience
mytxt = mirrorCountText
mytxt += blue(" %s: ") % (_("Mirror"),)
mytxt += red(self.entropyTools.spliturl(url)[1])
mytxt += " - %s." % (_("maximum failure threshold reached"),)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = red(" ## ")
)
if self.get_failing_mirror_status(uri) == 30:
self.add_failing_mirror(uri,45) # put to 75 then decrement by 4 so we won't reach 30 anytime soon ahahaha
else:
# now decrement each time this point is reached, if will be back < 30, then equo will try to use it again
if self.get_failing_mirror_status(uri) > 31:
self.add_failing_mirror(uri,-4)
else:
# put to 0 - reenable mirror, welcome back uri!
self.mirrorDownloadFailures[uri] = 0
if uri in remaining:
remaining.remove(uri)
continue
do_resume = True
while 1:
try:
mytxt = mirrorCountText
mytxt += blue("%s: ") % (_("Downloading from"),)
mytxt += red(self.entropyTools.spliturl(url)[1])
# now fetch the new one
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = red(" ## ")
)
rc, data_transfer, resumed = self.fetch_file(
url,
branch,
digest,
do_resume,
fetch_file_abort_function = fetch_abort_function
)
if rc == 0:
mytxt = mirrorCountText
mytxt += blue("%s: ") % (_("Successfully downloaded from"),)
mytxt += red(self.entropyTools.spliturl(url)[1])
mytxt += " %s %s/%s" % (_("at"),self.entropyTools.bytesIntoHuman(data_transfer),_("second"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
return 0
elif resumed:
do_resume = False
continue
else:
error_message = mirrorCountText
error_message += blue("%s: %s") % (
_("Error downloading from"),
red(self.entropyTools.spliturl(url)[1]),
)
# something bad happened
if rc == -1:
error_message += " - %s." % (_("file not available on this mirror"),)
elif rc == -2:
self.add_failing_mirror(uri,1)
error_message += " - %s." % (_("wrong checksum"),)
elif rc == -3:
#self.add_failing_mirror(uri,2)
error_message += " - not found."
elif rc == -4:
error_message += " - %s." % (_("discarded download"),)
else:
self.add_failing_mirror(uri, 5)
error_message += " - %s." % (_("unknown reason"),)
self.updateProgress(
error_message,
importance = 1,
type = "warning",
header = red(" ## ")
)
if rc == -4: # user discarded fetch
return 1
if uri in remaining:
remaining.remove(uri)
break
except KeyboardInterrupt:
break
except:
raise
return 0
def quickpkg(self, atomstring, savedir = None):
if savedir == None:
savedir = etpConst['packagestmpdir']
if not os.path.isdir(etpConst['packagestmpdir']):
os.makedirs(etpConst['packagestmpdir'])
# match package
match = self.clientDbconn.atomMatch(atomstring)
if match[0] == -1:
return -1,None,None
atom = self.clientDbconn.atomMatch(match[0])
pkgdata = self.clientDbconn.getPackageData(match[0])
resultfile = self.quickpkg_handler(pkgdata = pkgdata, dirpath = savedir)
if resultfile == None:
return -1,atom,None
else:
return 0,atom,resultfile
def quickpkg_handler(
self,
pkgdata,
dirpath,
edb = True,
portdbPath = None,
fake = False,
compression = "bz2",
shiftpath = ""
):
import stat
import tarfile
if compression not in ("bz2","","gz"):
compression = "bz2"
# getting package info
pkgtag = ''
pkgrev = "~"+str(pkgdata['revision'])
if pkgdata['versiontag']: pkgtag = "#"+pkgdata['versiontag']
pkgname = pkgdata['name']+"-"+pkgdata['version']+pkgrev+pkgtag # + version + tag
pkgcat = pkgdata['category']
#pkgfile = pkgname+etpConst['packagesext']
dirpath += "/"+pkgname+etpConst['packagesext']
if os.path.isfile(dirpath):
os.remove(dirpath)
tar = tarfile.open(dirpath,"w:"+compression)
if not fake:
contents = sorted([x for x in pkgdata['content']])
id_strings = {}
# collect files
for path in contents:
# convert back to filesystem str
encoded_path = path
path = path.encode('raw_unicode_escape')
path = shiftpath+path
try:
exist = os.lstat(path)
except OSError:
continue # skip file
arcname = path[len(shiftpath):] # remove shiftpath
if arcname.startswith("/"):
arcname = arcname[1:] # remove trailing /
ftype = pkgdata['content'][encoded_path]
if str(ftype) == '0': ftype = 'dir' # force match below, '0' means databases without ftype
if 'dir' == ftype and \
not stat.S_ISDIR(exist.st_mode) and \
os.path.isdir(path): # workaround for directory symlink issues
path = os.path.realpath(path)
tarinfo = tar.gettarinfo(path, arcname)
tarinfo.uname = id_strings.setdefault(tarinfo.uid, str(tarinfo.uid))
tarinfo.gname = id_strings.setdefault(tarinfo.gid, str(tarinfo.gid))
if stat.S_ISREG(exist.st_mode):
tarinfo.type = tarfile.REGTYPE
f = open(path)
try:
tar.addfile(tarinfo, f)
finally:
f.close()
else:
tar.addfile(tarinfo)
tar.close()
# appending xpak metadata
if etpConst['gentoo-compat']:
import etpXpak
Spm = self.Spm()
gentoo_name = self.entropyTools.remove_tag(pkgname)
gentoo_name = self.entropyTools.remove_entropy_revision(gentoo_name)
if portdbPath == None:
dbdir = Spm.get_vdb_path()+"/"+pkgcat+"/"+gentoo_name+"/"
else:
dbdir = portdbPath+"/"+pkgcat+"/"+gentoo_name+"/"
if os.path.isdir(dbdir):
tbz2 = etpXpak.tbz2(dirpath)
tbz2.recompose(dbdir)
if edb:
self.inject_entropy_database_into_package(dirpath, pkgdata)
if os.path.isfile(dirpath):
return dirpath
return None
def inject_entropy_database_into_package(self, package_filename, data, treeupdates_actions = None):
dbpath = self.get_tmp_dbpath()
dbconn = self.openGenericDatabase(dbpath)
dbconn.initializeDatabase()
dbconn.addPackage(data, revision = data['revision'])
if treeupdates_actions != None:
dbconn.bumpTreeUpdatesActions(treeupdates_actions)
dbconn.commitChanges()
dbconn.closeDB()
self.entropyTools.aggregateEdb(tbz2file = package_filename, dbfile = dbpath)
return dbpath
def get_tmp_dbpath(self):
dbpath = etpConst['packagestmpdir']+"/"+str(self.entropyTools.getRandomNumber())
while os.path.isfile(dbpath):
dbpath = etpConst['packagestmpdir']+"/"+str(self.entropyTools.getRandomNumber())
return dbpath
def Package(self):
conn = PackageInterface(EquoInstance = self)
return conn
'''
Package interface :: end
'''
'''
Source Package Manager Interface :: begin
'''
def Spm(self):
myroot = etpConst['systemroot']
cached = self.spmCache.get(myroot)
if cached != None:
return cached
conn = SpmInterface(self)
self.spmCache[myroot] = conn.intf
return conn.intf
def _extract_pkg_metadata_generate_extraction_dict(self):
data = {
'chost': {
'path': etpConst['spm']['xpak_entries']['chost'],
'critical': True,
},
'description': {
'path': etpConst['spm']['xpak_entries']['description'],
'critical': False,
},
'homepage': {
'path': etpConst['spm']['xpak_entries']['homepage'],
'critical': False,
},
'slot': {
'path': etpConst['spm']['xpak_entries']['slot'],
'critical': False,
},
'cflags': {
'path': etpConst['spm']['xpak_entries']['cflags'],
'critical': False,
},
'cxxflags': {
'path': etpConst['spm']['xpak_entries']['cxxflags'],
'critical': False,
},
'category': {
'path': etpConst['spm']['xpak_entries']['category'],
'critical': True,
},
'rdepend': {
'path': etpConst['spm']['xpak_entries']['rdepend'],
'critical': False,
},
'pdepend': {
'path': etpConst['spm']['xpak_entries']['pdepend'],
'critical': False,
},
'depend': {
'path': etpConst['spm']['xpak_entries']['depend'],
'critical': False,
},
'use': {
'path': etpConst['spm']['xpak_entries']['use'],
'critical': False,
},
'iuse': {
'path': etpConst['spm']['xpak_entries']['iuse'],
'critical': False,
},
'license': {
'path': etpConst['spm']['xpak_entries']['license'],
'critical': False,
},
'provide': {
'path': etpConst['spm']['xpak_entries']['provide'],
'critical': False,
},
'sources': {
'path': etpConst['spm']['xpak_entries']['src_uri'],
'critical': False,
},
'eclasses': {
'path': etpConst['spm']['xpak_entries']['inherited'],
'critical': False,
},
'counter': {
'path': etpConst['spm']['xpak_entries']['counter'],
'critical': False,
},
'keywords': {
'path': etpConst['spm']['xpak_entries']['keywords'],
'critical': False,
},
}
return data
def _extract_pkg_metadata_content(self, content_file, package_path):
pkg_content = {}
if os.path.isfile(content_file):
f = open(content_file,"r")
content = f.readlines()
f.close()
outcontent = set()
for line in content:
line = line.strip().split()
try:
datatype = line[0]
datafile = line[1:]
if datatype == 'obj':
datafile = datafile[:-2]
datafile = ' '.join(datafile)
elif datatype == 'dir':
datafile = ' '.join(datafile)
elif datatype == 'sym':
datafile = datafile[:-3]
datafile = ' '.join(datafile)
else:
myexc = "InvalidData: %s %s. %s." % (
datafile,
_("not supported"),
_("Probably Portage API has changed"),
)
raise exceptionTools.InvalidData(myexc)
outcontent.add((datafile,datatype))
except:
pass
_outcontent = set()
for i in outcontent:
i = list(i)
datatype = i[1]
_outcontent.add((i[0],i[1]))
outcontent = sorted(list(_outcontent))
for i in outcontent:
pkg_content[i[0]] = i[1]
else:
# CONTENTS is not generated when a package is emerged with portage and the option -B
# we have to unpack the tbz2 and generate content dict
mytempdir = etpConst['packagestmpdir']+"/"+os.path.basename(package_path)+".inject"
if os.path.isdir(mytempdir):
shutil.rmtree(mytempdir)
if not os.path.isdir(mytempdir):
os.makedirs(mytempdir)
self.entropyTools.uncompressTarBz2(package_path, extractPath = mytempdir, catchEmpty = True)
for currentdir, subdirs, files in os.walk(mytempdir):
pkg_content[currentdir[len(mytempdir):]] = "dir"
for item in files:
item = currentdir+"/"+item
if os.path.islink(item):
pkg_content[item[len(mytempdir):]] = "sym"
else:
pkg_content[item[len(mytempdir):]] = "obj"
# now remove
shutil.rmtree(mytempdir,True)
try:
os.rmdir(mytempdir)
except:
pass
return pkg_content
def _extract_pkg_metadata_needed(self, needed_file):
pkg_needed = set()
lines = []
try:
f = open(needed_file,"r")
lines = [x.strip() for x in f.readlines() if x.strip()]
f.close()
except IOError:
return lines
for line in lines:
needed = line.split()
if len(needed) == 2:
ownlib = needed[0]
ownelf = -1
if os.access(ownlib,os.R_OK):
ownelf = self.entropyTools.read_elf_class(ownlib)
for lib in needed[1].split(","):
#if lib.find(".so") != -1:
pkg_needed.add((lib,ownelf))
return list(pkg_needed)
def _extract_pkg_metadata_messages(self, log_dir, category, name, version, silent = False):
pkg_messages = []
if os.path.isdir(log_dir):
elogfiles = os.listdir(log_dir)
myelogfile = "%s:%s-%s" % (category, name, version,)
foundfiles = [x for x in elogfiles if x.startswith(myelogfile)]
if foundfiles:
elogfile = foundfiles[0]
if len(foundfiles) > 1:
# get the latest
mtimes = []
for item in foundfiles: mtimes.append((self.entropyTools.getFileUnixMtime(os.path.join(log_dir,item)),item))
mtimes = sorted(mtimes)
elogfile = mtimes[-1][1]
messages = self.entropyTools.extractElog(os.path.join(log_dir,elogfile))
for message in messages:
message = message.replace("emerge","install")
pkg_messages.append(message)
elif not silent:
mytxt = " %s, %s" % (_("not set"),_("have you configured make.conf properly?"),)
self.updateProgress(
red(log_dir)+mytxt,
importance = 1,
type = "warning",
header = brown(" * ")
)
return pkg_messages
def _extract_pkg_metadata_license_data(self, licenses_dir, license_string):
pkg_licensedata = {}
if licenses_dir and os.path.isdir(licenses_dir):
licdata = [x.strip() for x in license_string.split() if x.strip() and self.entropyTools.is_valid_string(x.strip())]
for mylicense in licdata:
licfile = os.path.join(licenses_dir,mylicense)
if os.access(licfile,os.R_OK):
if self.entropyTools.istextfile(licfile):
f = open(licfile)
pkg_licensedata[mylicense] = f.read()
f.close()
return pkg_licensedata
def _extract_pkg_metadata_mirror_links(self, Spm, sources_list):
# =mirror://openoffice|link1|link2|link3
pkg_links = []
for i in sources_list:
if i.startswith("mirror://"):
# parse what mirror I need
mirrorURI = i.split("/")[2]
mirrorlist = Spm.get_third_party_mirrors(mirrorURI)
pkg_links.append([mirrorURI,mirrorlist])
# mirrorURI = openoffice and mirrorlist = [link1, link2, link3]
return pkg_links
def _extract_pkg_metadata_ebuild_entropy_tag(self, ebuild):
search_tag = etpConst['spm']['ebuild_pkg_tag_var']
ebuild_tag = ''
f = open(ebuild,"r")
tags = [x.strip() for x in f.readlines() if x.strip() and x.strip().startswith(search_tag)]
f.close()
if not tags: return ebuild_tag
tag = tags[-1]
tag = tag.split("=")[-1].strip('"').strip("'").strip()
return tag
# This function extracts all the info from a .tbz2 file and returns them
def extract_pkg_metadata(self, package, etpBranch = etpConst['branch'], silent = False, inject = False):
data = {}
info_package = bold(os.path.basename(package))+": "
if not silent:
self.updateProgress(
red(info_package+_("Extracting package metadata")+" ..."),
importance = 0,
type = "info",
header = brown(" * "),
back = True
)
filepath = package
tbz2File = package
package = package.split(etpConst['packagesext'])[0]
package = self.entropyTools.remove_entropy_revision(package)
package = self.entropyTools.remove_tag(package)
# remove category
if package.find(":") != -1:
package = ':'.join(package.split(":")[1:])
package = package.split("-")
pkgname = ""
pkglen = len(package)
if package[pkglen-1].startswith("r"):
pkgver = package[pkglen-2]+"-"+package[pkglen-1]
pkglen -= 2
else:
pkgver = package[-1]
pkglen -= 1
for i in range(pkglen):
if i == pkglen-1:
pkgname += package[i]
else:
pkgname += package[i]+"-"
pkgname = pkgname.split("/")[-1]
# Fill Package name and version
data['name'] = pkgname
data['version'] = pkgver
data['digest'] = self.entropyTools.md5sum(tbz2File)
data['datecreation'] = str(self.entropyTools.getFileUnixMtime(tbz2File))
data['size'] = str(self.entropyTools.get_file_size(tbz2File))
tbz2TmpDir = etpConst['packagestmpdir']+"/"+data['name']+"-"+data['version']+"/"
if not os.path.isdir(tbz2TmpDir):
if os.path.lexists(tbz2TmpDir):
os.remove(tbz2TmpDir)
os.makedirs(tbz2TmpDir)
self.entropyTools.extractXpak(tbz2File,tbz2TmpDir)
data['injected'] = False
if inject: data['injected'] = True
data['branch'] = etpBranch
portage_entries = self._extract_pkg_metadata_generate_extraction_dict()
for item in portage_entries:
value = ''
try:
f = open(os.path.join(tbz2TmpDir,portage_entries[item]['path']),"r")
value = f.readline().strip()
f.close()
except IOError:
if portage_entries[item]['critical']:
raise
data[item] = value
# setup vars
data['eclasses'] = data['eclasses'].split()
try:
data['counter'] = int(data['counter'])
except ValueError:
data['counter'] = -2 # -2 values will be insterted as incremental negative values into the database
data['keywords'] = [x.strip() for x in data['keywords'].split() if x.strip()]
if not data['keywords']: data['keywords'].insert(0,"") # support for packages with no keywords
needed_file = os.path.join(tbz2TmpDir,etpConst['spm']['xpak_entries']['needed'])
data['needed'] = self._extract_pkg_metadata_needed(needed_file)
content_file = os.path.join(tbz2TmpDir,etpConst['spm']['xpak_entries']['contents'])
data['content'] = self._extract_pkg_metadata_content(content_file, filepath)
data['disksize'] = self.entropyTools.sum_file_sizes(data['content'])
# [][][] Kernel dependent packages hook [][][]
data['versiontag'] = ''
kernelstuff = False
kernelstuff_kernel = False
for item in data['content']:
if item.startswith("/lib/modules/"):
kernelstuff = True
# get the version of the modules
kmodver = item.split("/lib/modules/")[1]
kmodver = kmodver.split("/")[0]
lp = kmodver.split("-")[-1]
if lp.startswith("r"):
kname = kmodver.split("-")[-2]
kver = kmodver.split("-")[0]+"-"+kmodver.split("-")[-1]
else:
kname = kmodver.split("-")[-1]
kver = kmodver.split("-")[0]
break
# validate the results above
if kernelstuff:
matchatom = "linux-%s-%s" % (kname,kver,)
if (matchatom == data['name']+"-"+data['version']):
kernelstuff_kernel = True
data['versiontag'] = kmodver
if not kernelstuff_kernel:
data['slot'] = kmodver # if you change this behaviour,
# you must change "reagent update"
# and "equo database gentoosync" consequentially
file_ext = etpConst['spm']['ebuild_file_extension']
ebuilds_in_path = [x for x in os.listdir(tbz2TmpDir) if x.endswith(".%s" % (file_ext,))]
if not data['versiontag'] and ebuilds_in_path:
# has the user specified a custom package tag inside the ebuild
ebuild_path = os.path.join(tbz2TmpDir,ebuilds_in_path[0])
data['versiontag'] = self._extract_pkg_metadata_ebuild_entropy_tag(ebuild_path)
data['download'] = etpConst['packagesrelativepath'] + data['branch'] + "/"
data['download'] += self.entropyTools.create_package_filename(data['category'], data['name'], data['version'], data['versiontag'])
data['trigger'] = ""
if os.path.isfile(etpConst['triggersdir']+"/"+data['category']+"/"+data['name']+"/"+etpConst['triggername']):
f = open(etpConst['triggersdir']+"/"+data['category']+"/"+data['name']+"/"+etpConst['triggername'],"rb")
data['trigger'] = f.read()
f.close()
Spm = self.Spm()
portage_metadata = Spm.calculate_dependencies(
data['iuse'], data['use'], data['license'], data['depend'],
data['rdepend'], data['pdepend'], data['provide'], data['sources']
)
data['provide'] = portage_metadata['PROVIDE'].split()
data['license'] = portage_metadata['LICENSE']
data['useflags'] = []
for x in data['use'].split():
if x.startswith("+"):
x = x[1:]
elif x.startswith("-"):
x = x[1:]
if (x in portage_metadata['USE']) or (x in portage_metadata['USE_MASK']):
data['useflags'].append(x)
else:
data['useflags'].append("-"+x)
data['sources'] = portage_metadata['SRC_URI'].split()
data['dependencies'] = {}
for x in portage_metadata['RDEPEND'].split():
if x.startswith("!") or (x in ("(","||",")","")):
continue
data['dependencies'][x] = 0
for x in portage_metadata['PDEPEND'].split():
if x.startswith("!") or (x in ("(","||",")","")):
continue
data['dependencies'][x] = etpConst['spm']['pdepend_id']
data['conflicts'] = [x[1:] for x in portage_metadata['RDEPEND'].split()+portage_metadata['PDEPEND'].split() if x.startswith("!") and not x in ("(","||",")","")]
if (kernelstuff) and (not kernelstuff_kernel):
# add kname to the dependency
data['dependencies']["=sys-kernel/linux-"+kname+"-"+kver] = 0
# Conflicting tagged packages support
key = data['category']+"/"+data['name']
confl_data = self.SystemSettings['conflicting_tagged_packages'].get(key)
if confl_data != None:
for conflict in confl_data: data['conflicts'].append(conflict)
# Get License text if possible
licenses_dir = os.path.join(Spm.get_spm_setting('PORTDIR'),'licenses')
data['licensedata'] = self._extract_pkg_metadata_license_data(licenses_dir, data['license'])
data['mirrorlinks'] = self._extract_pkg_metadata_mirror_links(Spm, data['sources'])
# write only if it's a systempackage
data['systempackage'] = False
system_packages = [self.entropyTools.dep_getkey(x) for x in Spm.get_atoms_in_system()]
if data['category']+"/"+data['name'] in system_packages:
data['systempackage'] = True
# write only if it's a systempackage
protect, mask = Spm.get_config_protect_and_mask()
data['config_protect'] = protect
data['config_protect_mask'] = mask
log_dir = etpConst['logdir']+"/elog"
if not os.path.isdir(log_dir): os.makedirs(log_dir)
data['messages'] = self._extract_pkg_metadata_messages(log_dir, data['category'], data['name'], data['version'], silent = silent)
data['etpapi'] = etpConst['etpapi']
# removing temporary directory
shutil.rmtree(tbz2TmpDir,True)
if os.path.isdir(tbz2TmpDir):
try: os.remove(tbz2TmpDir)
except OSError: pass
if not silent:
self.updateProgress(
red(info_package+_("Package extraction complete")), importance = 0,
type = "info", header = brown(" * "), back = True
)
return data
'''
Source Package Manager Interface :: end
'''
def Triggers(self, *args, **kwargs):
conn = TriggerInterface(self, *args, **kwargs)
return conn
def Repositories(self, reponames = [], forceUpdate = False, noEquoCheck = False, fetchSecurity = True):
conn = RepoInterface(EquoInstance = self, reponames = reponames, forceUpdate = forceUpdate, noEquoCheck = noEquoCheck, fetchSecurity = fetchSecurity)
return conn
def FileUpdatesInterfaceLoader(self):
conn = FileUpdatesInterface(EquoInstance = self)
return conn
'''
Real package actions (install/remove) interface
'''
class PackageInterface:
def __init__(self, EquoInstance):
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid Equo instance or subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = EquoInstance
self.infoDict = {}
self.prepared = False
self.matched_atom = ()
self.valid_actions = ("source","fetch","remove","remove_conflict","install")
self.action = None
self.fetch_abort_function = None
self.xterm_title = ''
def kill(self):
self.infoDict.clear()
self.matched_atom = ()
self.valid_actions = ()
self.action = None
self.prepared = False
self.fetch_abort_function = None
def error_on_prepared(self):
if self.prepared:
mytxt = _("Already prepared")
raise exceptionTools.PermissionDenied("PermissionDenied: %s" % (mytxt,))
def error_on_not_prepared(self):
if not self.prepared:
mytxt = _("Not yet prepared")
raise exceptionTools.PermissionDenied("PermissionDenied: %s" % (mytxt,))
def check_action_validity(self, action):
if action not in self.valid_actions:
mytxt = _("Action must be in")
raise exceptionTools.InvalidData("InvalidData: %s %s" % (mytxt,self.valid_actions,))
def match_checksum(self):
self.error_on_not_prepared()
dlcount = 0
match = False
while dlcount <= 5:
self.Entropy.updateProgress(
blue(_("Checking package checksum...")),
importance = 0,
type = "info",
header = red(" ## "),
back = True
)
dlcheck = self.Entropy.check_needed_package_download(self.infoDict['download'], checksum = self.infoDict['checksum'])
if dlcheck == 0:
self.Entropy.updateProgress(
blue(_("Package checksum matches.")),
importance = 0,
type = "info",
header = red(" ## ")
)
self.infoDict['verified'] = True
match = True
break # file downloaded successfully
else:
dlcount += 1
self.Entropy.updateProgress(
blue(_("Package checksum does not match. Redownloading... attempt #%s") % (dlcount,)),
importance = 0,
type = "info",
header = red(" ## "),
back = True
)
fetch = self.Entropy.fetch_file_on_mirrors(
self.infoDict['repository'],
self.Entropy.get_branch_from_download_relative_uri(self.infoDict['download']),
self.infoDict['download'],
self.infoDict['checksum'],
fetch_abort_function = self.fetch_abort_function
)
if fetch != 0:
self.Entropy.updateProgress(
blue(_("Cannot properly fetch package! Quitting.")),
importance = 0,
type = "info",
header = red(" ## ")
)
return fetch
self.infoDict['verified'] = True
match = True
break
if (not match):
mytxt = _("Cannot properly fetch package or checksum does not match. Try download latest repositories.")
self.Entropy.updateProgress(
blue(mytxt),
importance = 0,
type = "info",
header = red(" ## ")
)
return 1
return 0
'''
@description: unpack the given package file into the unpack dir
@input infoDict: dictionary containing package information
@output: 0 = all fine, >0 = error!
'''
def __unpack_package(self):
self.error_on_not_prepared()
if not self.infoDict['merge_from']:
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Unpacking package: "+str(self.infoDict['atom']))
else:
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Merging package: "+str(self.infoDict['atom']))
if os.path.isdir(self.infoDict['unpackdir']):
shutil.rmtree(self.infoDict['unpackdir'].encode('raw_unicode_escape'))
elif os.path.isfile(self.infoDict['unpackdir']):
os.remove(self.infoDict['unpackdir'].encode('raw_unicode_escape'))
os.makedirs(self.infoDict['imagedir'])
if not os.path.isfile(self.infoDict['pkgpath']) and not self.infoDict['merge_from']:
if os.path.isdir(self.infoDict['pkgpath']):
shutil.rmtree(self.infoDict['pkgpath'])
if os.path.islink(self.infoDict['pkgpath']):
os.remove(self.infoDict['pkgpath'])
self.infoDict['verified'] = False
rc = self.fetch_step()
if rc != 0: return rc
if not self.infoDict['merge_from']:
unpack_tries = 3
while 1:
unpack_tries -= 1
try:
rc = self.Entropy.entropyTools.spawnFunction(
self.Entropy.entropyTools.uncompressTarBz2,
self.infoDict['pkgpath'],
self.infoDict['imagedir'],
catchEmpty = True
)
except EOFError:
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"EOFError on "+self.infoDict['pkgpath'])
rc = 1
except (UnicodeEncodeError,UnicodeDecodeError,):
# this will make devs to actually catch the right exception and prepare a fix
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Raising Unicode Error for "+self.infoDict['pkgpath'])
rc = self.Entropy.entropyTools.uncompressTarBz2(
self.infoDict['pkgpath'],self.infoDict['imagedir'],
catchEmpty = True
)
if rc == 0:
break
if unpack_tries <= 0:
return rc
# otherwise, try to download it again
self.infoDict['verified'] = False
f_rc = self.fetch_step()
if f_rc != 0: return f_rc
else:
pid = os.fork()
if pid > 0:
os.waitpid(pid, 0)
else:
self.__fill_image_dir(self.infoDict['merge_from'],self.infoDict['imagedir'])
os._exit(0)
# unpack xpak ?
if etpConst['gentoo-compat']:
if os.path.isdir(self.infoDict['xpakpath']):
shutil.rmtree(self.infoDict['xpakpath'])
try:
os.rmdir(self.infoDict['xpakpath'])
except OSError:
pass
# create data dir where we'll unpack the xpak
os.makedirs(self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'],0755)
#os.mkdir(self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'])
xpakPath = self.infoDict['xpakpath']+"/"+etpConst['entropyxpakfilename']
if not self.infoDict['merge_from']:
if (self.infoDict['smartpackage']):
# we need to get the .xpak from database
xdbconn = self.Entropy.openRepositoryDatabase(self.infoDict['repository'])
xpakdata = xdbconn.retrieveXpakMetadata(self.infoDict['idpackage'])
if xpakdata:
# save into a file
f = open(xpakPath,"wb")
f.write(xpakdata)
f.flush()
f.close()
self.infoDict['xpakstatus'] = self.Entropy.entropyTools.unpackXpak(
xpakPath,
self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
)
else:
self.infoDict['xpakstatus'] = None
del xpakdata
else:
self.infoDict['xpakstatus'] = self.Entropy.entropyTools.extractXpak(
self.infoDict['pkgpath'],
self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
)
else:
# link xpakdir to self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
tolink_dir = self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
if os.path.isdir(tolink_dir):
shutil.rmtree(tolink_dir,True)
# now link
os.symlink(self.infoDict['xpakdir'],tolink_dir)
# create fake portage ${D} linking it to imagedir
portage_db_fakedir = os.path.join(
self.infoDict['unpackdir'],
"portage/"+self.infoDict['category'] + "/" + self.infoDict['name'] + "-" + self.infoDict['version']
)
os.makedirs(portage_db_fakedir,0755)
# now link it to self.infoDict['imagedir']
os.symlink(self.infoDict['imagedir'],os.path.join(portage_db_fakedir,"image"))
return 0
def __remove_package(self):
# clear on-disk cache
self.__clear_cache()
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing package: %s" % (self.infoDict['removeatom'],))
# remove from database
if self.infoDict['removeidpackage'] != -1:
mytxt = "%s: " % (_("Removing from Entropy"),)
self.Entropy.updateProgress(
blue(mytxt) + red(self.infoDict['removeatom']),
importance = 1,
type = "info",
header = red(" ## ")
)
self.__remove_package_from_database()
# Handle gentoo database
if (etpConst['gentoo-compat']):
gentooAtom = self.Entropy.entropyTools.remove_tag(self.infoDict['removeatom'])
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing from Portage: "+str(gentooAtom))
self.__remove_package_from_gentoo_database(gentooAtom)
del gentooAtom
self.__remove_content_from_system()
return 0
def __remove_content_from_system(self):
# load CONFIG_PROTECT and its mask
# client database at this point has been surely opened,
# so our dicts are already filled
protect = etpConst['dbconfigprotect']
mask = etpConst['dbconfigprotectmask']
sys_root = etpConst['systemroot']
col_protect = etpConst['collisionprotect']
# remove files from system
directories = set()
for item in self.infoDict['removecontent']:
# collision check
if col_protect > 0:
if self.Entropy.clientDbconn.isFileAvailable(item) and os.path.isfile(sys_root+item):
# in this way we filter out directories
mytxt = red(_("Collision found during removal of")) + " " + sys_root+item + " - "
mytxt += red(_("cannot overwrite"))
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = red(" ## ")
)
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Collision found during remove of "+sys_root+item+" - cannot overwrite"
)
continue
protected = False
if (not self.infoDict['removeconfig']) and (not self.infoDict['diffremoval']):
protected_item_test = sys_root+item
protected, x, do_continue = self._handle_config_protect(protect, mask, None, protected_item_test, do_allocation_check = False)
if do_continue: protected = True
if protected:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_VERBOSE,
"[remove] Protecting config file: "+sys_root+item
)
mytxt = "[%s] %s: %s" % (
red(_("remove")),
brown(_("Protecting config file")),
sys_root+item,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = red(" ## ")
)
else:
try:
os.lstat(sys_root+item)
except OSError:
continue # skip file, does not exist
except UnicodeEncodeError:
mytxt = brown(_("This package contains a badly encoded file !!!"))
self.Entropy.updateProgress(
red("QA: ")+mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
continue # file has a really bad encoding
if os.path.isdir(sys_root+item) and os.path.islink(sys_root+item):
# S_ISDIR returns False for directory symlinks, so using os.path.isdir
# valid directory symlink
directories.add((sys_root+item,"link"))
elif os.path.isdir(sys_root+item):
# plain directory
directories.add((sys_root+item,"dir"))
else: # files, symlinks or not
# just a file or symlink or broken directory symlink (remove now)
try:
os.remove(sys_root+item)
# add its parent directory
dirfile = os.path.dirname(sys_root+item)
if os.path.isdir(dirfile) and os.path.islink(dirfile):
directories.add((dirfile,"link"))
elif os.path.isdir(dirfile):
directories.add((dirfile,"dir"))
except OSError:
pass
# now handle directories
directories = sorted(list(directories), reverse = True)
while 1:
taint = False
for directory, dirtype in directories:
mydir = "%s%s" % (sys_root,directory,)
if dirtype == "link":
try:
mylist = os.listdir(mydir)
if not mylist:
try:
os.remove(mydir)
taint = True
except OSError:
pass
except OSError:
pass
elif dirtype == "dir":
try:
mylist = os.listdir(mydir)
if not mylist:
try:
os.rmdir(mydir)
taint = True
except OSError:
pass
except OSError:
pass
if not taint:
break
del directories
'''
@description: remove package entry from Gentoo database
@input gentoo package atom (cat/name+ver):
@output: 0 = all fine, <0 = error!
'''
def __remove_package_from_gentoo_database(self, atom):
# handle gentoo-compat
try:
Spm = self.Entropy.Spm()
except:
return -1 # no Spm support ??
portDbDir = Spm.get_vdb_path()
removePath = portDbDir+atom
key = self.Entropy.entropyTools.dep_getkey(atom)
others_installed = Spm.search_keys(key)
slot = self.infoDict['slot']
tag = self.infoDict['versiontag']
if (tag == slot) and tag: slot = "0"
if os.path.isdir(removePath):
shutil.rmtree(removePath,True)
elif others_installed:
for myatom in others_installed:
myslot = Spm.get_installed_package_slot(myatom)
if myslot != slot:
continue
shutil.rmtree(portDbDir+myatom,True)
if not others_installed:
world_file = Spm.get_world_file()
world_file_tmp = world_file+".entropy.tmp"
if os.access(world_file,os.W_OK) and os.path.isfile(world_file):
new = open(world_file_tmp,"w")
old = open(world_file,"r")
line = old.readline()
while line:
if line.find(key) != -1:
line = old.readline()
continue
if line.find(key+":"+slot) != -1:
line = old.readline()
continue
new.write(line)
line = old.readline()
new.flush()
new.close()
old.close()
shutil.move(world_file_tmp,world_file)
return 0
'''
@description: function that runs at the end of the package installation process, just removes data left by other steps
@output: 0 = all fine, >0 = error!
'''
def __cleanup_package(self, unpack_dir):
# remove unpack dir
shutil.rmtree(unpack_dir,True)
try:
os.rmdir(unpack_dir)
except OSError:
pass
return 0
def __remove_package_from_database(self):
self.error_on_not_prepared()
self.Entropy.clientDbconn.removePackage(self.infoDict['removeidpackage'])
return 0
def __clear_cache(self):
self.Entropy.clear_dump_cache(etpCache['advisories'])
self.Entropy.clear_dump_cache(etpCache['filter_satisfied_deps'])
self.Entropy.clear_dump_cache(etpCache['depends_tree'])
self.Entropy.clear_dump_cache(etpCache['check_package_update'])
self.Entropy.clear_dump_cache(etpCache['dep_tree'])
self.Entropy.clear_dump_cache(etpCache['dbMatch']+etpConst['clientdbid']+"/")
self.Entropy.clear_dump_cache(etpCache['dbSearch']+etpConst['clientdbid']+"/")
self.__update_available_cache()
try:
self.__update_world_cache()
except:
self.Entropy.clear_dump_cache(etpCache['world_update'])
def __update_world_cache(self):
if self.Entropy.xcache and (self.action in ("install","remove",)):
wc_dir = os.path.dirname(os.path.join(etpConst['dumpstoragedir'],etpCache['world_update']))
wc_filename = os.path.basename(etpCache['world_update'])
wc_cache_files = [os.path.join(wc_dir,x) for x in os.listdir(wc_dir) if x.startswith(wc_filename)]
for cache_file in wc_cache_files:
try:
data = self.Entropy.dumpTools.loadobj(cache_file, completePath = True)
(update, remove, fine) = data['r']
empty_deps = data['empty_deps']
except:
self.Entropy.clear_dump_cache(etpCache['world_update'])
return
if empty_deps:
continue
if self.action == "install":
if self.matched_atom in update:
update.remove(self.matched_atom)
self.Entropy.dumpTools.dumpobj(
cache_file,
{'r':(update, remove, fine),'empty_deps': empty_deps},
completePath = True
)
else:
key, slot = self.Entropy.clientDbconn.retrieveKeySlot(self.infoDict['removeidpackage'])
matches = self.Entropy.atomMatch(key, matchSlot = slot, multiMatch = True, multiRepo = True)
if matches[1] != 0:
# hell why! better to rip all off
self.Entropy.clear_dump_cache(etpCache['world_update'])
return
taint = False
for match in matches[0]:
if match in update:
taint = True
update.remove(match)
if match in remove:
taint = True
remove.remove(match)
if taint:
self.Entropy.dumpTools.dumpobj(
cache_file,
{'r':(update, remove, fine),'empty_deps': empty_deps},
completePath = True
)
elif (not self.Entropy.xcache) or (self.action in ("install",)):
self.Entropy.clear_dump_cache(etpCache['world_update'])
def __update_available_cache(self):
# update world available cache
if self.Entropy.xcache and (self.action in ("remove","install")):
disk_cache = self.Entropy.Cacher.pop(etpCache['world_available'])
if disk_cache != None:
c_hash = self.Entropy.get_available_packages_chash(etpConst['branch'])
try:
if disk_cache['chash'] == c_hash:
# remove and old install
if self.infoDict['removeidpackage'] != -1:
taint = False
key = self.Entropy.entropyTools.dep_getkey(self.infoDict['removeatom'])
slot = self.infoDict['slot']
matches = self.Entropy.atomMatch(key, matchSlot = slot, multiRepo = True, multiMatch = True)
if matches[1] == 0:
for mymatch in matches[0]:
if mymatch not in disk_cache['available']:
disk_cache['available'].append(mymatch)
taint = True
if taint:
mydata = {}
mylist = []
for myidpackage,myrepo in disk_cache['available']:
mydbc = self.Entropy.openRepositoryDatabase(myrepo)
mydata[mydbc.retrieveAtom(myidpackage)] = (myidpackage,myrepo)
mykeys = sorted(mydata.keys())
for mykey in mykeys:
mylist.append(mydata[mykey])
disk_cache['available'] = mylist
# install, doing here because matches[0] could contain self.matched_atoms
if self.matched_atom in disk_cache['available']:
disk_cache['available'].remove(self.matched_atom)
self.Entropy.Cacher.push(etpCache['world_available'],disk_cache.copy())
except KeyError:
self.Entropy.Cacher.push(etpCache['world_available'],{})
elif not self.Entropy.xcache:
self.Entropy.clear_dump_cache(etpCache['world_available'])
'''
@description: install unpacked files, update database and also update gentoo db if requested
@output: 0 = all fine, >0 = error!
'''
def __install_package(self):
# clear on-disk cache
self.__clear_cache()
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Installing package: %s" % (self.infoDict['atom'],)
)
# copy files over - install
rc = self.__move_image_to_system()
if rc != 0:
return rc
# inject into database
mytxt = "%s: %s" % (blue(_("Updating database")),red(self.infoDict['atom']),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
newidpackage = self._install_package_into_database()
# remove old files and gentoo stuff
if (self.infoDict['removeidpackage'] != -1):
# doing a diff removal
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Remove old package: %s" % (self.infoDict['removeatom'],)
)
self.infoDict['removeidpackage'] = -1 # disabling database removal
if etpConst['gentoo-compat']:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Removing Entropy and Gentoo database entry for %s" % (self.infoDict['removeatom'],)
)
else:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Removing Entropy (only) database entry for %s" % (self.infoDict['removeatom'],)
)
self.Entropy.updateProgress(
blue(_("Cleaning old package files...")),
importance = 1,
type = "info",
header = red(" ## ")
)
self.__remove_package()
rc = 0
if etpConst['gentoo-compat']:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Installing new Gentoo database entry: %s" % (self.infoDict['atom'],)
)
rc = self._install_package_into_gentoo_database(newidpackage)
return rc
'''
@description: inject the database information into the Gentoo database
@output: 0 = all fine, !=0 = error!
'''
def _install_package_into_gentoo_database(self, newidpackage):
# handle gentoo-compat
try:
Spm = self.Entropy.Spm()
except:
return -1 # no Portage support
portDbDir = Spm.get_vdb_path()
if os.path.isdir(portDbDir):
# extract xpak from unpackDir+etpConst['packagecontentdir']+"/"+package
key = self.infoDict['category']+"/"+self.infoDict['name']
atomsfound = set()
dbdirs = os.listdir(portDbDir)
if self.infoDict['category'] in dbdirs:
catdirs = os.listdir(portDbDir+"/"+self.infoDict['category'])
dirsfound = set([self.infoDict['category']+"/"+x for x in catdirs if key == self.Entropy.entropyTools.dep_getkey(self.infoDict['category']+"/"+x)])
atomsfound.update(dirsfound)
### REMOVE
# parse slot and match and remove
if atomsfound:
pkgToRemove = ''
for atom in atomsfound:
atomslot = Spm.get_installed_package_slot(atom)
# get slot from gentoo db
if atomslot == self.infoDict['slot']:
pkgToRemove = atom
break
if (pkgToRemove):
removePath = portDbDir+pkgToRemove
shutil.rmtree(removePath,True)
try:
os.rmdir(removePath)
except OSError:
pass
del atomsfound
# we now install it
if ((self.infoDict['xpakstatus'] != None) and \
os.path.isdir( self.infoDict['xpakpath'] + "/" + etpConst['entropyxpakdatarelativepath'])) or \
self.infoDict['merge_from']:
if self.infoDict['merge_from']:
copypath = self.infoDict['xpakdir']
if not os.path.isdir(copypath):
return 0
else:
copypath = self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
if not os.path.isdir(portDbDir+self.infoDict['category']):
os.makedirs(portDbDir+self.infoDict['category'],0755)
destination = portDbDir+self.infoDict['category']+"/"+self.infoDict['name']+"-"+self.infoDict['version']
if os.path.isdir(destination):
shutil.rmtree(destination)
try:
shutil.copytree(copypath,destination)
except (IOError,), e:
mytxt = "%s: %s: %s: %s" % (red(_("QA")),brown(_("Cannot update Portage database to destination")),purple(destination),e,)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
# test if /var/cache/edb/counter is fine
if os.path.isfile(etpConst['edbcounter']):
try:
f = open(etpConst['edbcounter'],"r")
counter = int(f.readline().strip())
f.close()
except:
# need file recreation, parse gentoo tree
counter = Spm.refill_counter()
else:
counter = Spm.refill_counter()
# write new counter to file
if os.path.isdir(destination):
counter += 1
f = open(destination+"/"+etpConst['spm']['xpak_entries']['counter'],"w")
f.write(str(counter))
f.flush()
f.close()
f = open(etpConst['edbcounter'],"w")
f.write(str(counter))
f.flush()
f.close()
# update counter inside clientDatabase
self.Entropy.clientDbconn.insertCounter(newidpackage,counter)
else:
mytxt = brown(_("Cannot update Portage counter, destination %s does not exist.") % (destination,))
self.Entropy.updateProgress(
red("QA: ")+mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
# add to Portage world
# key: key
# slot: self.infoDict['slot']
myslot = self.infoDict['slot']
if (self.infoDict['versiontag'] == self.infoDict['slot']) and self.infoDict['versiontag']:
# usually kernel packages
myslot = "0"
keyslot = key+":"+myslot
world_file = Spm.get_world_file()
world_atoms = set()
if os.access(world_file,os.R_OK) and os.path.isfile(world_file):
f = open(world_file,"r")
world_atoms = set([x.strip() for x in f.readlines() if x.strip()])
f.close()
else:
mytxt = brown(_("Cannot update Portage world file, destination %s does not exist.") % (world_file,))
self.Entropy.updateProgress(
red("QA: ")+mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
return 0
try:
if keyslot not in world_atoms and \
os.access(os.path.dirname(world_file),os.W_OK) and \
self.Entropy.entropyTools.istextfile(world_file):
world_atoms.discard(key)
world_atoms.add(keyslot)
world_atoms = sorted(list(world_atoms))
world_file_tmp = world_file+".entropy_inst"
f = open(world_file_tmp,"w")
for item in world_atoms:
f.write(item+"\n")
f.flush()
f.close()
shutil.move(world_file_tmp,world_file)
except (UnicodeDecodeError,UnicodeEncodeError), e:
self.Entropy.entropyTools.printTraceback(f = self.Entropy.clientLog)
mytxt = brown(_("Cannot update Portage world file, destination %s is corrupted.") % (world_file,))
self.Entropy.updateProgress(
red("QA: ")+mytxt+": "+unicode(e),
importance = 1,
type = "warning",
header = darkred(" ## ")
)
return 0
'''
@description: injects package info into the installed packages database
@output: 0 = all fine, >0 = error!
'''
def _install_package_into_database(self):
# fetch info
dbconn = self.Entropy.openRepositoryDatabase(self.infoDict['repository'])
data = dbconn.getPackageData(self.infoDict['idpackage'], content_insert_formatted = True)
# open client db
# always set data['injected'] to False
# installed packages database SHOULD never have more than one package for scope (key+slot)
data['injected'] = False
data['counter'] = -1 # gentoo counter will be set in self._install_package_into_gentoo_database()
idpackage, rev, x = self.Entropy.clientDbconn.handlePackage(etpData = data, forcedRevision = data['revision'], formattedContent = True)
# update datecreation
ctime = self.Entropy.entropyTools.getCurrentUnixTime()
self.Entropy.clientDbconn.setDateCreation(idpackage, str(ctime))
# add idpk to the installedtable
self.Entropy.clientDbconn.removePackageFromInstalledTable(idpackage)
self.Entropy.clientDbconn.addPackageToInstalledTable(idpackage,self.infoDict['repository'])
# clear depends table, this will make clientdb dependstable to be regenerated during the next request (retrieveDepends)
self.Entropy.clientDbconn.clearDependsTable()
return idpackage
def __fill_image_dir(self, mergeFrom, imageDir):
dbconn = self.Entropy.openRepositoryDatabase(self.infoDict['repository'])
package_content = dbconn.retrieveContent(self.infoDict['idpackage'], extended = True, formatted = True)
contents = sorted([x for x in package_content])
# collect files
for path in contents:
# convert back to filesystem str
encoded_path = path
path = os.path.join(mergeFrom,encoded_path[1:])
topath = os.path.join(imageDir,encoded_path[1:])
path = path.encode('raw_unicode_escape')
topath = topath.encode('raw_unicode_escape')
try:
exist = os.lstat(path)
except OSError:
continue # skip file
ftype = package_content[encoded_path]
if str(ftype) == '0': ftype = 'dir' # force match below, '0' means databases without ftype
if 'dir' == ftype and \
not stat.S_ISDIR(exist.st_mode) and \
os.path.isdir(path): # workaround for directory symlink issues
path = os.path.realpath(path)
copystat = False
# if our directory is a symlink instead, then copy the symlink
if os.path.islink(path):
tolink = os.readlink(path)
if os.path.islink(topath):
os.remove(topath)
os.symlink(tolink,topath)
elif os.path.isdir(path):
if not os.path.isdir(topath):
os.makedirs(topath)
copystat = True
elif os.path.isfile(path):
if os.path.isfile(topath):
os.remove(topath) # should never happen
shutil.copy2(path,topath)
copystat = True
if copystat:
user = os.stat(path)[stat.ST_UID]
group = os.stat(path)[stat.ST_GID]
os.chown(topath,user,group)
shutil.copystat(path,topath)
def __move_image_to_system(self):
# load CONFIG_PROTECT and its mask
protect = etpRepositories[self.infoDict['repository']]['configprotect']
mask = etpRepositories[self.infoDict['repository']]['configprotectmask']
sys_root = etpConst['systemroot']
col_protect = etpConst['collisionprotect']
items_installed = set()
# setup imageDir properly
imageDir = self.infoDict['imagedir']
# merge data into system
for currentdir,subdirs,files in os.walk(imageDir.encode('utf-8')):
# create subdirs
for subdir in subdirs:
imagepathDir = "%s/%s" % (currentdir,subdir,)
rootdir = "%s%s" % (sys_root,imagepathDir[len(imageDir):],)
# handle broken symlinks
if os.path.islink(rootdir) and not os.path.exists(rootdir):# broken symlink
os.remove(rootdir)
# if our directory is a file on the live system
elif os.path.isfile(rootdir): # really weird...!
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"WARNING!!! %s is a file when it should be a directory !! Removing in 20 seconds..." % (rootdir,)
)
mytxt = darkred(_("%s is a file when should be a directory !! Removing in 20 seconds...") % (rootdir,))
self.Entropy.updateProgress(
red("QA: ")+mytxt,
importance = 1,
type = "warning",
header = red(" !!! ")
)
self.Entropy.entropyTools.ebeep(10)
time.sleep(20)
os.remove(rootdir)
# if our directory is a symlink instead, then copy the symlink
if os.path.islink(imagepathDir) and not os.path.isdir(rootdir):
# for security we skip live items that are dirs
tolink = os.readlink(imagepathDir)
if os.path.islink(rootdir):
os.remove(rootdir)
os.symlink(tolink,rootdir)
elif (not os.path.isdir(rootdir)) and (not os.access(rootdir,os.R_OK)):
try:
# we should really force a simple mkdir first of all
os.mkdir(rootdir)
except OSError:
os.makedirs(rootdir)
if not os.path.islink(rootdir) and os.access(rootdir,os.W_OK):
# symlink don't need permissions, also until os.walk ends they might be broken
# XXX also, added os.access() check because there might be directories/files unwriteable
# what to do otherwise?
user = os.stat(imagepathDir)[stat.ST_UID]
group = os.stat(imagepathDir)[stat.ST_GID]
os.chown(rootdir,user,group)
shutil.copystat(imagepathDir,rootdir)
items_installed.add(os.path.join(os.path.realpath(os.path.dirname(rootdir)),os.path.basename(rootdir)))
for item in files:
fromfile = "%s/%s" % (currentdir,item,)
tofile = "%s%s" % (sys_root,fromfile[len(imageDir):],)
if col_protect > 1:
todbfile = fromfile[len(imageDir):]
myrc = self._handle_install_collision_protect(tofile, todbfile)
if not myrc:
continue
protected, tofile, do_continue = self._handle_config_protect(protect, mask, fromfile, tofile)
if do_continue:
continue
try:
if os.path.realpath(fromfile) == os.path.realpath(tofile) and os.path.islink(tofile):
# there is a serious issue here, better removing tofile, happened to someone:
try: # try to cope...
os.remove(tofile)
except OSError:
pass
# if our file is a dir on the live system
if os.path.isdir(tofile) and not os.path.islink(tofile): # really weird...!
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"WARNING!!! %s is a directory when it should be a file !! Removing in 20 seconds..." % (tofile,)
)
mytxt = _("%s is a directory when it should be a file !! Removing in 20 seconds...") % (tofile,)
self.Entropy.updateProgress(
red("QA: ")+darkred(mytxt),
importance = 1,
type = "warning",
header = red(" !!! ")
)
self.Entropy.entropyTools.ebeep(10)
time.sleep(20)
try:
shutil.rmtree(tofile, True)
os.rmdir(tofile)
except:
pass
try: # if it was a link
os.remove(tofile)
except OSError:
pass
# this also handles symlinks
# XXX
# XXX moving file using the raw format like portage does
# XXX
try:
shutil.move(fromfile,tofile)
except shutil.Error, e:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"WARNING!!! Error during file move to system: %s" % (e,)
)
mytxt = "%s: %s, %s" % (_("File move error"),e,_("please report"),)
self.Entropy.updateProgress(
red("QA: ")+darkred(mytxt),
importance = 1,
type = "warning",
header = red(" !!! ")
)
except IOError, e:
if e.errno == 2:
# better to pass away, sometimes gentoo packages are fucked up and contain broken things
pass
else:
rc = subprocess.call(["mv",fromfile,tofile])
if rc != 0: return 4
items_installed.add(os.path.join(os.path.realpath(os.path.dirname(tofile)),os.path.basename(tofile)))
if protected:
# add to disk cache
self.Entropy.FileUpdates.add_to_cache(tofile, quiet = True)
# this is useful to avoid the removal of installed files by __remove_package just because
# there's a difference in the directory path, perhaps, which is not handled correctly by
# EntropyDatabaseInterface.contentDiff for obvious reasons (think about stuff in /usr/lib and /usr/lib64,
# where the latter is just a symlink to the former)
if self.infoDict.get('removecontent'):
my_remove_content = set([x for x in self.infoDict['removecontent'] if os.path.join(os.path.realpath(os.path.dirname("%s%s" % (sys_root,x,))),os.path.basename(x)) in items_installed])
self.infoDict['removecontent'] -= my_remove_content
return 0
def _handle_config_protect(self, protect, mask, fromfile, tofile, do_allocation_check = True):
protected = False
tofile_before_protect = tofile
do_continue = False
try:
encoded_protect = [x.encode('raw_unicode_escape') for x in protect]
if tofile in encoded_protect:
protected = True
elif os.path.dirname(tofile) in encoded_protect:
protected = True
else:
tofile_testdir = os.path.dirname(tofile)
old_tofile_testdir = None
while tofile_testdir != old_tofile_testdir:
if tofile_testdir in encoded_protect:
protected = True
break
old_tofile_testdir = tofile_testdir
tofile_testdir = os.path.dirname(tofile_testdir)
if protected: # check if perhaps, file is masked, so unprotected
newmask = [x.encode('raw_unicode_escape') for x in mask]
if tofile in newmask:
protected = False
elif os.path.dirname(tofile) in newmask:
protected = False
if not os.path.lexists(tofile):
protected = False # file doesn't exist
# check if it's a text file
if (protected) and os.path.isfile(tofile):
protected = self.Entropy.entropyTools.istextfile(tofile)
else:
protected = False # it's not a file
# request new tofile then
if protected:
if tofile not in etpConst['configprotectskip']:
prot_status = True
if do_allocation_check:
tofile, prot_status = self.Entropy.entropyTools.allocateMaskedFile(tofile, fromfile)
if not prot_status:
protected = False
else:
oldtofile = tofile
if oldtofile.find("._cfg") != -1:
oldtofile = os.path.dirname(oldtofile)+"/"+os.path.basename(oldtofile)[10:]
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Protecting config file: %s" % (oldtofile,)
)
mytxt = red("%s: %s") % (_("Protecting config file"),oldtofile,)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
else:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Skipping config file installation/removal, as stated in equo.conf: %s" % (tofile,)
)
mytxt = "%s: %s" % (_("Skipping file installation/removal"),tofile,)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
do_continue = True
except Exception, e:
self.Entropy.entropyTools.printTraceback()
protected = False # safely revert to false
tofile = tofile_before_protect
mytxt = darkred("%s: %s") % (_("Cannot check CONFIG PROTECTION. Error"),e,)
self.Entropy.updateProgress(
red("QA: ")+mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
return protected, tofile, do_continue
def _handle_install_collision_protect(self, tofile, todbfile):
avail = self.Entropy.clientDbconn.isFileAvailable(todbfile, get_id = True)
if (self.infoDict['removeidpackage'] not in avail) and avail:
mytxt = darkred(_("Collision found during install for"))
mytxt += " %s - %s" % (blue(tofile),darkred(_("cannot overwrite")),)
self.Entropy.updateProgress(
red("QA: ")+mytxt,
importance = 1,
type = "warning",
header = darkred(" ## ")
)
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"WARNING!!! Collision found during install for %s - cannot overwrite" % (tofile,)
)
return False
return True
def sources_fetch_step(self):
self.error_on_not_prepared()
down_data = self.infoDict['download']
rc = 0
for key in sorted(down_data.keys()):
# first fine wins
for url in down_data[key]:
dest_file = os.path.join(self.infoDict['unpackdir'],os.path.basename(url))
rc = self._fetch_source(url, dest_file)
if not rc: break
if rc: break
return rc
def _fetch_source(self, url, dest_file):
try:
mytxt = "%s: %s" % (blue(_("Downloading")),brown(url),)
# now fetch the new one
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
rc, data_transfer, resumed = self.Entropy.fetch_file(
url,
None,
None,
False,
fetch_file_abort_function = self.fetch_abort_function,
filepath = dest_file
)
if rc == 0:
mytxt = blue("%s: ") % (_("Successfully downloaded from"),)
mytxt += red(self.Entropy.entropyTools.spliturl(url)[1])
mytxt += " %s %s/%s" % (_("at"),self.Entropy.entropyTools.bytesIntoHuman(data_transfer),_("second"),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
self.Entropy.updateProgress(
"%s: %s" % (blue(_("Local path")),brown(dest_file),),
importance = 1,
type = "info",
header = red(" # ")
)
return 0
else:
error_message = blue("%s: %s") % (
_("Error downloading from"),
red(self.Entropy.entropyTools.spliturl(url)[1]),
)
# something bad happened
if rc == -1:
error_message += " - %s." % (_("file not available on this mirror"),)
elif rc == -3:
error_message += " - not found."
elif rc == -4:
error_message += " - %s." % (_("discarded download"),)
else:
error_message += " - %s: %s" % (_("unknown reason"),rc,)
self.Entropy.updateProgress(
error_message,
importance = 1,
type = "warning",
header = red(" ## ")
)
if rc == -4: # user discarded fetch
return 1
except KeyboardInterrupt:
return 1
def fetch_step(self):
self.error_on_not_prepared()
mytxt = "%s: %s" % (blue(_("Downloading archive")),red(os.path.basename(self.infoDict['download'])),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
rc = self.Entropy.fetch_file_on_mirrors(
self.infoDict['repository'],
self.Entropy.get_branch_from_download_relative_uri(self.infoDict['download']),
self.infoDict['download'],
self.infoDict['checksum'],
self.infoDict['verified'],
fetch_abort_function = self.fetch_abort_function
)
if rc != 0:
mytxt = "%s. %s: %s" % (
red(_("Package cannot be fetched. Try to update repositories and retry")),
blue(_("Error")),
rc,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "error",
header = darkred(" ## ")
)
return rc
return 0
def fetch_not_available_step(self):
self.Entropy.updateProgress(
blue(_("Fetch for the chosen package is not available, unknown error.")),
importance = 1,
type = "info",
header = red(" ## ")
)
return 0
def vanished_step(self):
self.Entropy.updateProgress(
blue(_("Installed package in queue vanished, skipping.")),
importance = 1,
type = "info",
header = red(" ## ")
)
return 0
def checksum_step(self):
self.error_on_not_prepared()
rc = self.match_checksum()
return rc
def unpack_step(self):
self.error_on_not_prepared()
if not self.infoDict['merge_from']:
mytxt = "%s: %s" % (blue(_("Unpacking package")),red(os.path.basename(self.infoDict['download'])),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
else:
mytxt = "%s: %s" % (blue(_("Merging package")),red(os.path.basename(self.infoDict['atom'])),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
rc = self.__unpack_package()
if rc != 0:
if rc == 512:
errormsg = "%s. %s. %s: 512" % (
red(_("You are running out of disk space")),
red(_("I bet, you're probably Michele")),
blue(_("Error")),
)
else:
errormsg = "%s. %s. %s: %s" % (
red(_("An error occured while trying to unpack the package")),
red(_("Check if your system is healthy")),
blue(_("Error")),
rc,
)
self.Entropy.updateProgress(
errormsg,
importance = 1,
type = "error",
header = red(" ## ")
)
return rc
def install_step(self):
self.error_on_not_prepared()
mytxt = "%s: %s" % (blue(_("Installing package")),red(self.infoDict['atom']),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
rc = self.__install_package()
if rc != 0:
mytxt = "%s. %s. %s: %s" % (
red(_("An error occured while trying to install the package")),
red(_("Check if your system is healthy")),
blue(_("Error")),
rc,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "error",
header = red(" ## ")
)
return rc
def remove_step(self):
self.error_on_not_prepared()
mytxt = "%s: %s" % (blue(_("Removing data")),red(self.infoDict['removeatom']),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
rc = self.__remove_package()
if rc != 0:
mytxt = "%s. %s. %s: %s" % (
red(_("An error occured while trying to remove the package")),
red(_("Check if you have enough disk space on your hard disk")),
blue(_("Error")),
rc,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "error",
header = red(" ## ")
)
return rc
def cleanup_step(self):
self.error_on_not_prepared()
mytxt = "%s: %s" % (blue(_("Cleaning")),red(self.infoDict['atom']),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" ## ")
)
task = self.Entropy.entropyTools.parallelTask(self.__cleanup_package, self.infoDict['unpackdir'])
task.parallel_wait()
task.start()
# we don't care if cleanupPackage fails since it's not critical
return 0
def logmessages_step(self):
for msg in self.infoDict['messages']:
self.Entropy.clientLog.write(">>> "+msg)
return 0
def messages_step(self):
self.error_on_not_prepared()
# get messages
if self.infoDict['messages']:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"Message from %s:" % (self.infoDict['atom'],)
)
mytxt = "%s:" % (darkgreen(_("Compilation messages")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "warning",
header = brown(" ## ")
)
for msg in self.infoDict['messages']:
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,msg)
self.Entropy.updateProgress(
msg,
importance = 0,
type = "warning",
header = brown(" ## ")
)
if self.infoDict['messages']:
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"End message.")
def postinstall_step(self):
self.error_on_not_prepared()
pkgdata = self.infoDict['triggers'].get('install')
if pkgdata:
Trigger = self.Entropy.Triggers('postinstall',pkgdata, self.action)
Trigger.prepare()
Trigger.run()
Trigger.kill()
del Trigger
del pkgdata
return 0
def preinstall_step(self):
self.error_on_not_prepared()
pkgdata = self.infoDict['triggers'].get('install')
if pkgdata:
Trigger = self.Entropy.Triggers('preinstall',pkgdata, self.action)
Trigger.prepare()
if (self.infoDict.get("diffremoval") != None): # diffremoval is true only when the remove action is triggered by installPackages()
if self.infoDict['diffremoval']:
remdata = self.infoDict['triggers'].get('remove')
if remdata:
rTrigger = self.Entropy.Triggers('preremove',remdata, self.action)
rTrigger.prepare()
Trigger.triggers = [x for x in Trigger.triggers if x not in rTrigger.triggers]
rTrigger.kill()
del rTrigger
del remdata
Trigger.run()
Trigger.kill()
del Trigger
del pkgdata
return 0
def preremove_step(self):
self.error_on_not_prepared()
remdata = self.infoDict['triggers'].get('remove')
if remdata:
Trigger = self.Entropy.Triggers('preremove',remdata, self.action)
Trigger.prepare()
Trigger.run()
Trigger.kill()
del Trigger
del remdata
return 0
def postremove_step(self):
self.error_on_not_prepared()
remdata = self.infoDict['triggers'].get('remove')
if remdata:
Trigger = self.Entropy.Triggers('postremove',remdata, self.action)
Trigger.prepare()
if self.infoDict['diffremoval'] and (self.infoDict.get("atom") != None):
# diffremoval is true only when the remove action is triggered by installPackages()
pkgdata = self.infoDict['triggers'].get('install')
if pkgdata:
iTrigger = self.Entropy.Triggers('postinstall',pkgdata, self.action)
iTrigger.prepare()
Trigger.triggers = [x for x in Trigger.triggers if x not in iTrigger.triggers]
iTrigger.kill()
del iTrigger
del pkgdata
Trigger.run()
Trigger.kill()
del Trigger
del remdata
return 0
def removeconflict_step(self):
for idpackage in self.infoDict['conflicts']:
if not self.Entropy.clientDbconn.isIDPackageAvailable(idpackage):
continue
Package = self.Entropy.Package()
Package.prepare((idpackage,),"remove_conflict", self.infoDict['remove_metaopts'])
rc = Package.run(xterm_header = self.xterm_title)
Package.kill()
if rc != 0:
return rc
return 0
def run_stepper(self, xterm_header):
if xterm_header == None:
xterm_header = ""
if self.infoDict.has_key('remove_installed_vanished'):
self.xterm_title += ' Installed package vanished'
self.Entropy.setTitle(self.xterm_title)
rc = self.vanished_step()
return rc
if self.infoDict.has_key('fetch_not_available'):
self.xterm_title += ' Fetch not available'
self.Entropy.setTitle(self.xterm_title)
rc = self.fetch_not_available_step()
return rc
def do_fetch():
mytxt = _("Fetching")
self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['download']),)
self.Entropy.setTitle(self.xterm_title)
return self.fetch_step()
def do_sources_fetch():
mytxt = _("Fetching sources")
self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['atom']),)
self.Entropy.setTitle(self.xterm_title)
return self.sources_fetch_step()
def do_checksum():
mytxt = _("Verifying")
self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['download']),)
self.Entropy.setTitle(self.xterm_title)
return self.checksum_step()
def do_unpack():
if not self.infoDict['merge_from']:
mytxt = _("Unpacking")
self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['download']),)
else:
mytxt = _("Merging")
self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['atom']),)
self.Entropy.setTitle(self.xterm_title)
return self.unpack_step()
def do_remove_conflicts():
return self.removeconflict_step()
def do_install():
mytxt = _("Installing")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['atom'],)
self.Entropy.setTitle(self.xterm_title)
return self.install_step()
def do_remove():
mytxt = _("Removing")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['removeatom'],)
self.Entropy.setTitle(self.xterm_title)
return self.remove_step()
def do_showmessages():
return self.messages_step()
def do_logmessages():
return self.logmessages_step()
def do_cleanup():
mytxt = _("Cleaning")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['atom'],)
self.Entropy.setTitle(self.xterm_title)
return self.cleanup_step()
def do_postinstall():
mytxt = _("Postinstall")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['atom'],)
self.Entropy.setTitle(self.xterm_title)
return self.postinstall_step()
def do_preinstall():
mytxt = _("Preinstall")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['atom'],)
self.Entropy.setTitle(self.xterm_title)
return self.preinstall_step()
def do_preremove():
mytxt = _("Preremove")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['removeatom'],)
self.Entropy.setTitle(self.xterm_title)
return self.preremove_step()
def do_postremove():
mytxt = _("Postremove")
self.xterm_title += ' %s: %s' % (mytxt,self.infoDict['removeatom'],)
self.Entropy.setTitle(self.xterm_title)
return self.postremove_step()
steps_data = {
"fetch": do_fetch,
"sources_fetch": do_sources_fetch,
"checksum": do_checksum,
"unpack": do_unpack,
"remove_conflicts": do_remove_conflicts,
"install": do_install,
"remove": do_remove,
"showmessages": do_showmessages,
"logmessages": do_logmessages,
"cleanup": do_cleanup,
"postinstall": do_postinstall,
"preinstall": do_preinstall,
"postremove": do_postremove,
"preremove": do_preremove,
}
rc = 0
for step in self.infoDict['steps']:
self.xterm_title = xterm_header
rc = steps_data.get(step)()
if rc != 0: break
return rc
'''
@description: execute the requested steps
@input xterm_header: purely optional
'''
def run(self, xterm_header = None):
self.error_on_not_prepared()
gave_up = self.Entropy.lock_check(self.Entropy._resources_run_check_lock)
if gave_up:
return 20
locked = self.Entropy.application_lock_check()
if locked:
self.Entropy._resources_run_remove_lock()
return 21
# lock
self.Entropy._resources_run_create_lock()
try:
rc = self.run_stepper(xterm_header)
except:
self.Entropy._resources_run_remove_lock()
raise
# remove lock
self.Entropy._resources_run_remove_lock()
if rc != 0:
self.Entropy.updateProgress(
blue(_("An error occured. Action aborted.")),
importance = 2,
type = "error",
header = darkred(" ## ")
)
return rc
'''
Install/Removal process preparation function
- will generate all the metadata needed to run the action steps, creating infoDict automatically
@input matched_atom(tuple): is what is returned by EquoInstance.atomMatch:
(idpackage,repoid):
(2000,u'sabayonlinux.org')
NOTE: in case of remove action, matched_atom must be:
(idpackage,)
@input action(string): is an action to take, which must be one in self.valid_actions
'''
def prepare(self, matched_atom, action, metaopts = {}):
self.error_on_prepared()
self.check_action_validity(action)
self.action = action
self.matched_atom = matched_atom
self.metaopts = metaopts
# generate metadata dictionary
self.generate_metadata()
def generate_metadata(self):
self.error_on_prepared()
self.check_action_validity(self.action)
if self.action == "fetch":
self.__generate_fetch_metadata()
elif self.action in ("remove","remove_conflict"):
self.__generate_remove_metadata()
elif self.action == "install":
self.__generate_install_metadata()
elif self.action == "source":
self.__generate_fetch_metadata(sources = True)
self.prepared = True
def __generate_remove_metadata(self):
self.infoDict.clear()
idpackage = self.matched_atom[0]
if not self.Entropy.clientDbconn.isIDPackageAvailable(idpackage):
self.infoDict['remove_installed_vanished'] = True
return 0
self.infoDict['triggers'] = {}
self.infoDict['removeatom'] = self.Entropy.clientDbconn.retrieveAtom(idpackage)
self.infoDict['slot'] = self.Entropy.clientDbconn.retrieveSlot(idpackage)
self.infoDict['versiontag'] = self.Entropy.clientDbconn.retrieveVersionTag(idpackage)
self.infoDict['removeidpackage'] = idpackage
self.infoDict['diffremoval'] = False
removeConfig = False
if self.metaopts.has_key('removeconfig'):
removeConfig = self.metaopts.get('removeconfig')
self.infoDict['removeconfig'] = removeConfig
self.infoDict['removecontent'] = self.Entropy.clientDbconn.retrieveContent(idpackage)
self.infoDict['triggers']['remove'] = self.Entropy.clientDbconn.getTriggerInfo(idpackage)
self.infoDict['triggers']['remove']['removecontent'] = self.infoDict['removecontent']
self.infoDict['steps'] = []
self.infoDict['steps'].append("preremove")
self.infoDict['steps'].append("remove")
self.infoDict['steps'].append("postremove")
return 0
def __generate_install_metadata(self):
self.infoDict.clear()
idpackage, repository = self.matched_atom
self.infoDict['idpackage'] = idpackage
self.infoDict['repository'] = repository
# fetch abort function
if self.metaopts.has_key('fetch_abort_function'):
self.fetch_abort_function = self.metaopts.pop('fetch_abort_function')
# get package atom
dbconn = self.Entropy.openRepositoryDatabase(repository)
self.infoDict['triggers'] = {}
self.infoDict['atom'] = dbconn.retrieveAtom(idpackage)
self.infoDict['slot'] = dbconn.retrieveSlot(idpackage)
self.infoDict['version'], self.infoDict['versiontag'], self.infoDict['revision'] = dbconn.getVersioningData(idpackage)
self.infoDict['category'] = dbconn.retrieveCategory(idpackage)
self.infoDict['download'] = dbconn.retrieveDownloadURL(idpackage)
self.infoDict['name'] = dbconn.retrieveName(idpackage)
self.infoDict['messages'] = dbconn.retrieveMessages(idpackage)
self.infoDict['checksum'] = dbconn.retrieveDigest(idpackage)
self.infoDict['accept_license'] = dbconn.retrieveLicensedataKeys(idpackage)
self.infoDict['conflicts'] = self.Entropy.get_match_conflicts(self.matched_atom)
# fill action queue
self.infoDict['removeidpackage'] = -1
removeConfig = False
if self.metaopts.has_key('removeconfig'):
removeConfig = self.metaopts.get('removeconfig')
self.infoDict['remove_metaopts'] = {
'removeconfig': True,
}
if self.metaopts.has_key('remove_metaopts'):
self.infoDict['remove_metaopts'] = self.metaopts.get('remove_metaopts')
self.infoDict['merge_from'] = None
mf = self.metaopts.get('merge_from')
if mf != None:
self.infoDict['merge_from'] = unicode(mf)
self.infoDict['removeconfig'] = removeConfig
self.infoDict['removeidpackage'] = self.Entropy.retrieveInstalledIdPackage(
self.Entropy.entropyTools.dep_getkey(self.infoDict['atom']),
self.infoDict['slot']
)
if self.infoDict['removeidpackage'] != -1:
avail = self.Entropy.clientDbconn.isIDPackageAvailable(self.infoDict['removeidpackage'])
if avail:
self.infoDict['removeatom'] = self.Entropy.clientDbconn.retrieveAtom(self.infoDict['removeidpackage'])
else:
self.infoDict['removeidpackage'] = -1
# smartpackage ?
self.infoDict['smartpackage'] = False
# set unpack dir and image dir
if self.infoDict['repository'].endswith(etpConst['packagesext']):
# do arch check
compiled_arch = dbconn.retrieveDownloadURL(idpackage)
if compiled_arch.find("/"+etpSys['arch']+"/") == -1:
self.infoDict.clear()
self.prepared = False
return -1
self.infoDict['smartpackage'] = etpRepositories[self.infoDict['repository']]['smartpackage']
self.infoDict['pkgpath'] = etpRepositories[self.infoDict['repository']]['pkgpath']
else:
self.infoDict['pkgpath'] = etpConst['entropyworkdir']+"/"+self.infoDict['download']
self.infoDict['unpackdir'] = etpConst['entropyunpackdir']+"/"+self.infoDict['download']
self.infoDict['imagedir'] = etpConst['entropyunpackdir']+"/"+self.infoDict['download']+"/"+etpConst['entropyimagerelativepath']
# gentoo xpak data
if etpConst['gentoo-compat']:
self.infoDict['xpakpath'] = etpConst['entropyunpackdir']+"/"+self.infoDict['download']+"/"+etpConst['entropyxpakrelativepath']
if not self.infoDict['merge_from']:
self.infoDict['xpakstatus'] = None
self.infoDict['xpakdir'] = self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
else:
self.infoDict['xpakstatus'] = True
portdbdir = 'var/db/pkg' # XXX hard coded ?
portdbdir = os.path.join(self.infoDict['merge_from'],portdbdir)
portdbdir = os.path.join(portdbdir,self.infoDict['category'])
portdbdir = os.path.join(portdbdir,self.infoDict['name']+"-"+self.infoDict['version'])
self.infoDict['xpakdir'] = portdbdir
# compare both versions and if they match, disable removeidpackage
if self.infoDict['removeidpackage'] != -1:
installedVer, installedTag, installedRev = self.Entropy.clientDbconn.getVersioningData(self.infoDict['removeidpackage'])
pkgcmp = self.Entropy.entropyTools.entropyCompareVersions(
(self.infoDict['version'], self.infoDict['versiontag'], self.infoDict['revision'],),
(installedVer, installedTag, installedRev,)
)
if pkgcmp == 0:
self.infoDict['removeidpackage'] = -1
else:
# differential remove list
self.infoDict['diffremoval'] = True
self.infoDict['removeatom'] = self.Entropy.clientDbconn.retrieveAtom(self.infoDict['removeidpackage'])
self.infoDict['removecontent'] = self.Entropy.clientDbconn.contentDiff(
self.infoDict['removeidpackage'],
dbconn,
idpackage
)
self.infoDict['triggers']['remove'] = self.Entropy.clientDbconn.getTriggerInfo(
self.infoDict['removeidpackage']
)
self.infoDict['triggers']['remove']['removecontent'] = self.infoDict['removecontent']
# set steps
self.infoDict['steps'] = []
if self.infoDict['conflicts']:
self.infoDict['steps'].append("remove_conflicts")
# install
if (self.infoDict['removeidpackage'] != -1):
self.infoDict['steps'].append("preremove")
self.infoDict['steps'].append("unpack")
self.infoDict['steps'].append("preinstall")
self.infoDict['steps'].append("install")
if (self.infoDict['removeidpackage'] != -1):
self.infoDict['steps'].append("postremove")
self.infoDict['steps'].append("postinstall")
if not etpConst['gentoo-compat']: # otherwise gentoo triggers will show that
self.infoDict['steps'].append("showmessages")
else:
self.infoDict['steps'].append("logmessages")
self.infoDict['steps'].append("cleanup")
self.infoDict['triggers']['install'] = dbconn.getTriggerInfo(idpackage)
self.infoDict['triggers']['install']['accept_license'] = self.infoDict['accept_license']
self.infoDict['triggers']['install']['unpackdir'] = self.infoDict['unpackdir']
if etpConst['gentoo-compat']:
#self.infoDict['triggers']['install']['xpakpath'] = self.infoDict['xpakpath']
self.infoDict['triggers']['install']['xpakdir'] = self.infoDict['xpakdir']
return 0
def __generate_fetch_metadata(self, sources = False):
self.infoDict.clear()
idpackage, repository = self.matched_atom
dochecksum = True
# fetch abort function
if self.metaopts.has_key('fetch_abort_function'):
self.fetch_abort_function = self.metaopts.pop('fetch_abort_function')
if self.metaopts.has_key('dochecksum'):
dochecksum = self.metaopts.get('dochecksum')
self.infoDict['repository'] = repository
self.infoDict['idpackage'] = idpackage
dbconn = self.Entropy.openRepositoryDatabase(repository)
self.infoDict['atom'] = dbconn.retrieveAtom(idpackage)
if sources:
self.infoDict['download'] = dbconn.retrieveSources(idpackage, extended = True)
else:
self.infoDict['checksum'] = dbconn.retrieveDigest(idpackage)
self.infoDict['download'] = dbconn.retrieveDownloadURL(idpackage)
if not self.infoDict['download']:
self.infoDict['fetch_not_available'] = True
return 0
self.infoDict['verified'] = False
self.infoDict['steps'] = []
if not repository.endswith(etpConst['packagesext']) and not sources:
if self.Entropy.check_needed_package_download(self.infoDict['download'], None) < 0:
self.infoDict['steps'].append("fetch")
if dochecksum:
self.infoDict['steps'].append("checksum")
elif sources:
self.infoDict['steps'].append("sources_fetch")
if sources:
# create sources destination directory
unpack_dir = etpConst['entropyunpackdir']+"/sources/"+self.infoDict['atom']
self.infoDict['unpackdir'] = unpack_dir
if os.path.lexists(unpack_dir):
if os.path.isfile(unpack_dir):
os.remove(unpack_dir)
elif os.path.isdir(unpack_dir):
shutil.rmtree(unpack_dir,True)
if not os.path.lexists(unpack_dir):
os.makedirs(unpack_dir,0775)
const_setup_perms(unpack_dir,etpConst['entropygid'])
else:
# if file exists, first checksum then fetch
if os.path.isfile(os.path.join(etpConst['entropyworkdir'],self.infoDict['download'])):
# check size first
repo_size = dbconn.retrieveSize(idpackage)
f = open(os.path.join(etpConst['entropyworkdir'],self.infoDict['download']),"r")
f.seek(0,2)
disk_size = f.tell()
f.close()
if repo_size == disk_size:
self.infoDict['steps'].reverse()
return 0
class FileUpdatesInterface:
def __init__(self, EquoInstance):
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid Equo instance or subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = EquoInstance
self.scandata = None
def merge_file(self, key):
self.scanfs(dcache = True)
self.do_backup(key)
if os.access(etpConst['systemroot'] + self.scandata[key]['source'], os.R_OK):
shutil.move(
etpConst['systemroot'] + self.scandata[key]['source'],
etpConst['systemroot'] + self.scandata[key]['destination']
)
self.remove_from_cache(key)
def remove_file(self, key):
self.scanfs(dcache = True)
try:
os.remove(etpConst['systemroot'] + self.scandata[key]['source'])
except OSError:
pass
self.remove_from_cache(key)
def do_backup(self, key):
self.scanfs(dcache = True)
if etpConst['filesbackup'] and os.path.isfile(etpConst['systemroot']+self.scandata[key]['destination']):
bcount = 0
backupfile = etpConst['systemroot'] + \
os.path.dirname(self.scandata[key]['destination']) + \
"/._equo_backup." + unicode(bcount) + "_" + \
os.path.basename(self.scandata[key]['destination'])
while os.path.lexists(backupfile):
bcount += 1
backupfile = etpConst['systemroot'] + \
os.path.dirname(self.scandata[key]['destination']) + \
"/._equo_backup." + unicode(bcount) + "_" + \
os.path.basename(self.scandata[key]['destination'])
try:
shutil.copy2(etpConst['systemroot'] + self.scandata[key]['destination'],backupfile)
except IOError:
pass
'''
@description: scan for files that need to be merged
@output: dictionary using filename as key
'''
def scanfs(self, dcache = True, quiet = False):
if dcache:
if self.scandata != None:
return self.scandata
# can we load cache?
try:
z = self.load_cache()
if z != None:
self.scandata = z
return self.scandata
except:
pass
# open client database to fill etpConst['dbconfigprotect']
scandata = {}
counter = 0
name_cache = set()
for path in etpConst['dbconfigprotect']:
# it's a file?
scanfile = False
if os.path.isfile(path):
# find inside basename
path = os.path.dirname(path)
scanfile = True
for currentdir,subdirs,files in os.walk(path):
for item in files:
if scanfile:
if path != item:
continue
filepath = os.path.join(currentdir,item)
if item.startswith("._cfg"):
# further check then
number = item[5:9]
try:
int(number)
except ValueError:
continue # not a valid etc-update file
if item[9] != "_": # no valid format provided
continue
if filepath in name_cache:
continue # skip, already done
name_cache.add(filepath)
mydict = self.generate_dict(filepath)
if mydict['automerge']:
if not quiet:
mytxt = _("Automerging file")
self.Entropy.updateProgress(
darkred("%s: %s") % (
mytxt,
darkgreen(etpConst['systemroot'] + mydict['source']),
),
importance = 0,
type = "info"
)
if os.path.isfile(etpConst['systemroot']+mydict['source']):
try:
shutil.move( etpConst['systemroot']+mydict['source'],
etpConst['systemroot']+mydict['destination']
)
except IOError, e:
if not quiet:
mytxt = "%s :: %s: %s. %s: %s" % (
red(_("I/O Error")),
red(_("Cannot automerge file")),
brown(etpConst['systemroot'] + mydict['source']),
blue("Error"),
e,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning"
)
continue
else:
counter += 1
scandata[counter] = mydict.copy()
if not quiet:
try:
self.Entropy.updateProgress(
"("+blue(str(counter))+") " + red(" file: ") + \
os.path.dirname(filepath) + "/" + os.path.basename(filepath)[10:],
importance = 1,
type = "info"
)
except:
pass # possible encoding issues
# store data
self.Entropy.Cacher.push(etpCache['configfiles'],scandata.copy())
self.scandata = scandata.copy()
return scandata
def load_cache(self):
sd = self.Entropy.Cacher.pop(etpCache['configfiles'])
if not isinstance(sd,dict):
raise exceptionTools.CacheCorruptionError("CacheCorruptionError")
# quick test if data is reliable
try:
name_cache = set()
for x in sd:
mysource = sd[x]['source']
# filter dupies
if mysource in name_cache:
sd.pop(x)
continue
if not os.path.isfile(etpConst['systemroot']+mysource):
raise exceptionTools.CacheCorruptionError("CacheCorruptionError")
name_cache.add(mysource)
return sd
except (KeyError,EOFError,IOError,):
raise exceptionTools.CacheCorruptionError("CacheCorruptionError")
'''
@description: prints information about config files that should be updated
@attention: please be sure that filepath is properly formatted before using this function
'''
def add_to_cache(self, filepath, quiet = False):
self.scanfs(dcache = True, quiet = quiet)
keys = self.scandata.keys()
try:
for key in keys:
if self.scandata[key]['source'] == filepath[len(etpConst['systemroot']):]:
del self.scandata[key]
except:
pass
# get next counter
if keys:
keys = sorted(keys)
index = keys[-1]
else:
index = 0
index += 1
mydata = self.generate_dict(filepath)
self.scandata[index] = mydata.copy()
self.Entropy.Cacher.push(etpCache['configfiles'],self.scandata.copy())
def remove_from_cache(self, key):
self.scanfs(dcache = True)
try:
del self.scandata[key]
except:
pass
self.Entropy.Cacher.push(etpCache['configfiles'],self.scandata.copy())
return self.scandata
def generate_dict(self, filepath):
item = os.path.basename(filepath)
currentdir = os.path.dirname(filepath)
tofile = item[10:]
number = item[5:9]
try:
int(number)
except:
mytxt = _("Invalid config file number")
raise exceptionTools.InvalidDataType("InvalidDataType: %s '0000->9999'." % (mytxt,))
tofilepath = currentdir+"/"+tofile
mydict = {}
mydict['revision'] = number
mydict['destination'] = tofilepath[len(etpConst['systemroot']):]
mydict['source'] = filepath[len(etpConst['systemroot']):]
mydict['automerge'] = False
if not os.path.isfile(tofilepath):
mydict['automerge'] = True
if (not mydict['automerge']):
# is it trivial?
try:
if not os.path.lexists(filepath): # if file does not even exist
return mydict
if os.path.islink(filepath):
# if it's broken, skip diff and automerge
if not os.path.exists(filepath):
return mydict
result = commands.getoutput('diff -Nua "%s" "%s" | grep "^[+-][^+-]" | grep -v \'# .Header:.*\'' % (filepath,tofilepath,))
if not result:
mydict['automerge'] = True
except:
pass
# another test
if (not mydict['automerge']):
try:
if not os.path.lexists(filepath): # if file does not even exist
return mydict
if os.path.islink(filepath):
# if it's broken, skip diff and automerge
if not os.path.exists(filepath):
return mydict
result = subprocess.call('diff -Bbua "%s" "%s" | egrep \'^[+-]\' | egrep -v \'^[+-][\t ]*#|^--- |^\+\+\+ \' | egrep -qv \'^[-+][\t ]*$\'' % (filepath,tofilepath,), shell = True)
if result == 1:
mydict['automerge'] = True
except:
pass
return mydict
#
# repository control class, that's it
#
class RepoInterface:
import dumpTools
import entropyTools
import socket
def __init__(self, EquoInstance, reponames = [], forceUpdate = False, noEquoCheck = False, fetchSecurity = True):
self.LockScanner = None
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid Equo instance or subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.supported_download_items = (
"db","rev","ck",
"lock","mask","system_mask","dbdump", "conflicting_tagged",
"dbdumpck","lic_whitelist","make.conf",
"package.mask","package.unmask","package.keywords","profile.link",
"package.use","server.cert","ca.cert",
"notice_board"
)
self.big_socket_timeout = 25
self.Entropy = EquoInstance
self.dbapi2 = dbapi2
self.reponames = reponames
self.forceUpdate = forceUpdate
self.syncErrors = False
self.dbupdated = False
self.newEquo = False
self.fetchSecurity = fetchSecurity
self.noEquoCheck = noEquoCheck
self.alreadyUpdated = 0
self.notAvailable = 0
self.valid_eapis = [1,2,3]
self.reset_dbformat_eapi(None)
self.current_repository_got_locked = False
self.updated_repos = set()
# check etpRepositories
if not etpRepositories:
mytxt = _("No repositories specified in %s") % (etpConst['repositoriesconf'],)
raise exceptionTools.MissingParameter("MissingParameter: %s" % (mytxt,))
# Test network connectivity
conntest = self.entropyTools.get_remote_data(etpConst['conntestlink'])
if not conntest:
mytxt = _("Cannot connect to %s") % (etpConst['conntestlink'],)
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: %s" % (mytxt,))
if not self.reponames:
self.reponames.extend(etpRepositories.keys()[:])
def __del__(self):
if self.LockScanner != None:
self.LockScanner.kill()
def get_eapi3_connection(self, repository):
# get database url
dburl = etpRepositories[repository]['plain_database']
if dburl.startswith("file://"):
return None
try:
dburl = dburl.split("/")[2]
except IndexError:
return None
port = etpRepositories[repository]['service_port']
try:
eapi3_socket = SystemSocketClientInterface(
self.Entropy, RepositorySocketClientCommands, output_header = "\t"
)
eapi3_socket.socket_timeout = self.big_socket_timeout
eapi3_socket.connect(dburl, port)
return eapi3_socket
except (exceptionTools.ConnectionError,self.socket.error,):
return None
def check_eapi3_availability(self, repository):
conn = self.get_eapi3_connection(repository)
if conn == None: return False
try:
conn.disconnect()
except (self.socket.error,AttributeError,):
return False
return True
def reset_dbformat_eapi(self, repository):
self.dbformat_eapi = 2
if repository != None:
eapi_avail = self.check_eapi3_availability(repository)
if eapi_avail:
self.dbformat_eapi = 3
# FIXME, find a way to do that without needing sqlite3 exec.
if not os.access("/usr/bin/sqlite3",os.X_OK) or self.entropyTools.islive():
self.dbformat_eapi = 1
else:
rc = subprocess.call("/usr/bin/sqlite3 -version &> /dev/null", shell = True)
if rc != 0: self.dbformat_eapi = 1
eapi_env = os.getenv("FORCE_EAPI")
if eapi_env != None:
try:
myeapi = int(eapi_env)
except (ValueError,TypeError,):
return
if myeapi in self.valid_eapis:
self.dbformat_eapi = myeapi
def __validate_repository_id(self, repoid):
if repoid not in self.reponames:
mytxt = _("Repository is not listed in self.reponames")
raise exceptionTools.InvalidData("InvalidData: %s" % (mytxt,))
def __validate_compression_method(self, repo):
self.__validate_repository_id(repo)
cmethod = etpConst['etpdatabasecompressclasses'].get(etpRepositories[repo]['dbcformat'])
if cmethod == None:
mytxt = _("Wrong database compression method")
raise exceptionTools.InvalidDataType("InvalidDataType: %s" % (mytxt,))
return cmethod
def __ensure_repository_path(self, repo):
self.__validate_repository_id(repo)
# create dir if it doesn't exist
if not os.path.isdir(etpRepositories[repo]['dbpath']):
os.makedirs(etpRepositories[repo]['dbpath'],0775)
const_setup_perms(etpConst['etpdatabaseclientdir'],etpConst['entropygid'])
def _construct_paths(self, item, repo, cmethod):
if item not in self.supported_download_items:
mytxt = _("Supported items: %s") % (self.supported_download_items,)
raise exceptionTools.InvalidData("InvalidData: %s" % (mytxt,))
if (item in ("db","dbdump", "dbdumpck",)) and (cmethod == None):
mytxt = _("For %s, cmethod can't be None") % (item,)
raise exceptionTools.InvalidData("InvalidData: %s" % (mytxt,))
if item == "db":
url = etpRepositories[repo]['database'] + "/" + etpConst[cmethod[2]]
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst[cmethod[2]]
elif item == "dbdump":
url = etpRepositories[repo]['database'] + "/" + etpConst[cmethod[3]]
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst[cmethod[3]]
elif item == "rev":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabaserevisionfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabaserevisionfile']
elif item == "ck":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabasehashfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabasehashfile']
elif item == "dbdumpck":
url = etpRepositories[repo]['database'] + "/" + etpConst[cmethod[4]]
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst[cmethod[4]]
elif item == "mask":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabasemaskfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabasemaskfile']
elif item == "system_mask":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabasesytemmaskfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabasesytemmaskfile']
elif item == "conflicting_tagged":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabaseconflictingtaggedfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabaseconflictingtaggedfile']
elif item == "make.conf":
myfile = os.path.basename(etpConst['spm']['global_make_conf'])
url = etpRepositories[repo]['database'] + "/" + myfile
filepath = etpRepositories[repo]['dbpath'] + "/" + myfile
elif item == "package.mask":
myfile = os.path.basename(etpConst['spm']['global_package_mask'])
url = etpRepositories[repo]['database'] + "/" + myfile
filepath = etpRepositories[repo]['dbpath'] + "/" + myfile
elif item == "package.unmask":
myfile = os.path.basename(etpConst['spm']['global_package_unmask'])
url = etpRepositories[repo]['database'] + "/" + myfile
filepath = etpRepositories[repo]['dbpath'] + "/" + myfile
elif item == "package.keywords":
myfile = os.path.basename(etpConst['spm']['global_package_keywords'])
url = etpRepositories[repo]['database'] + "/" + myfile
filepath = etpRepositories[repo]['dbpath'] + "/" + myfile
elif item == "package.use":
myfile = os.path.basename(etpConst['spm']['global_package_use'])
url = etpRepositories[repo]['database'] + "/" + myfile
filepath = etpRepositories[repo]['dbpath'] + "/" + myfile
elif item == "profile.link":
myfile = etpConst['spm']['global_make_profile_link_name']
url = etpRepositories[repo]['database'] + "/" + myfile
filepath = etpRepositories[repo]['dbpath'] + "/" + myfile
elif item == "lic_whitelist":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabaselicwhitelistfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabaselicwhitelistfile']
elif item == "lock":
url = etpRepositories[repo]['database']+"/"+etpConst['etpdatabasedownloadlockfile']
filepath = "/dev/null"
elif item == "server.cert":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabasecacertfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabasecacertfile']
elif item == "ca.cert":
url = etpRepositories[repo]['database'] + "/" + etpConst['etpdatabaseservercertfile']
filepath = etpRepositories[repo]['dbpath'] + "/" + etpConst['etpdatabaseservercertfile']
elif item == "notice_board":
url = etpRepositories[repo]['notice_board']
filepath = etpRepositories[repo]['dbpath'] + "/" + os.path.basename(etpRepositories[repo]['notice_board'])
return url, filepath
def __remove_repository_files(self, repo, cmethod):
dbfilenameid = cmethod[2]
self.__validate_repository_id(repo)
if self.dbformat_eapi == 1:
if os.path.isfile(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabasehashfile']):
os.remove(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabasehashfile'])
if os.path.isfile(etpRepositories[repo]['dbpath']+"/"+etpConst[dbfilenameid]):
os.remove(etpRepositories[repo]['dbpath']+"/"+etpConst[dbfilenameid])
if os.path.isfile(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabaserevisionfile']):
os.remove(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabaserevisionfile'])
elif self.dbformat_eapi == 2:
if os.path.isfile(etpRepositories[repo]['dbpath']+"/"+cmethod[4]):
os.remove(etpRepositories[repo]['dbpath']+"/"+cmethod[4])
if os.path.isfile(etpRepositories[repo]['dbpath']+"/"+etpConst[cmethod[3]]):
os.remove(etpRepositories[repo]['dbpath']+"/"+etpConst[cmethod[3]])
if os.path.isfile(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabaserevisionfile']):
os.remove(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabaserevisionfile'])
else:
mytxt = _("self.dbformat_eapi must be in (1,2)")
raise exceptionTools.InvalidData('InvalidData: %s' % (mytxt,))
def __unpack_downloaded_database(self, repo, cmethod):
self.__validate_repository_id(repo)
rc = 0
path = None
if self.dbformat_eapi == 1:
myfile = etpRepositories[repo]['dbpath']+"/"+etpConst[cmethod[2]]
try:
path = eval("self.entropyTools."+cmethod[1])(myfile)
except EOFError:
rc = 1
if os.path.isfile(myfile):
os.remove(myfile)
elif self.dbformat_eapi == 2:
myfile = etpRepositories[repo]['dbpath']+"/"+etpConst[cmethod[3]]
try:
path = eval("self.entropyTools."+cmethod[1])(myfile)
except EOFError:
rc = 1
if os.path.isfile(myfile):
os.remove(myfile)
else:
mytxt = _("self.dbformat_eapi must be in (1,2)")
raise exceptionTools.InvalidData('InvalidData: %s' % (mytxt,))
if rc == 0:
self.Entropy.setup_default_file_perms(path)
return rc
def __verify_database_checksum(self, repo, cmethod = None):
self.__validate_repository_id(repo)
if self.dbformat_eapi == 1:
dbfile = etpConst['etpdatabasefile']
try:
f = open(etpRepositories[repo]['dbpath']+"/"+etpConst['etpdatabasehashfile'],"r")
md5hash = f.readline().strip()
md5hash = md5hash.split()[0]
f.close()
except:
return -1
elif self.dbformat_eapi == 2:
dbfile = etpConst[cmethod[3]]
try:
f = open(etpRepositories[repo]['dbpath']+"/"+etpConst[cmethod[4]],"r")
md5hash = f.readline().strip()
md5hash = md5hash.split()[0]
f.close()
except:
return -1
else:
mytxt = _("self.dbformat_eapi must be in (1,2)")
raise exceptionTools.InvalidData('InvalidData: %s' % (mytxt,))
rc = self.entropyTools.compareMd5(etpRepositories[repo]['dbpath']+"/"+dbfile,md5hash)
return rc
# @returns -1 if the file is not available
# @returns int>0 if the revision has been retrieved
def get_online_repository_revision(self, repo):
self.__validate_repository_id(repo)
url = etpRepositories[repo]['database']+"/"+etpConst['etpdatabaserevisionfile']
status = self.entropyTools.get_remote_data(url)
if (status):
status = status[0].strip()
try:
status = int(status)
except ValueError:
status = -1
return status
else:
return -1
def get_online_eapi3_lock(self, repo):
self.__validate_repository_id(repo)
url = etpRepositories[repo]['database']+"/"+etpConst['etpdatabaseeapi3lockfile']
data = self.entropyTools.get_remote_data(url)
if not data:
return False
return True
def is_repository_eapi3_locked(self, repo):
self.__validate_repository_id(repo)
return self.get_online_eapi3_lock(repo)
def is_repository_updatable(self, repo):
self.__validate_repository_id(repo)
onlinestatus = self.get_online_repository_revision(repo)
if (onlinestatus != -1):
localstatus = self.Entropy.get_repository_revision(repo)
if (localstatus == onlinestatus) and (not self.forceUpdate):
return False
return True
def is_repository_unlocked(self, repo):
self.__validate_repository_id(repo)
rc = self.download_item("lock", repo, disallow_redirect = True)
if rc: # cannot download database
self.syncErrors = True
return False
return True
def clear_repository_cache(self, repo):
self.__validate_repository_id(repo)
self.Entropy.clear_dump_cache("%s/%s%s/" % (etpCache['dbMatch'],etpConst['dbnamerepoprefix'],repo,))
self.Entropy.clear_dump_cache("%s/%s%s/" % (etpCache['dbSearch'],etpConst['dbnamerepoprefix'],repo,))
# this function can be reimplemented
def download_item(self, item, repo, cmethod = None, lock_status_func = None, disallow_redirect = True):
self.__validate_repository_id(repo)
url, filepath = self._construct_paths(item, repo, cmethod)
# to avoid having permissions issues
# it's better to remove the file before,
# otherwise new permissions won't be written
if os.path.isfile(filepath):
os.remove(filepath)
fetchConn = self.Entropy.urlFetcher(
url,
filepath,
resume = False,
abort_check_func = lock_status_func,
disallow_redirect = disallow_redirect
)
fetchConn.progress = self.Entropy.progress
rc = fetchConn.download()
del fetchConn
if rc in ("-1","-2","-3"):
return False
self.Entropy.setup_default_file_perms(filepath)
return True
def check_downloaded_database(self, repo, cmethod):
dbfilename = etpConst['etpdatabasefile']
if self.dbformat_eapi == 2:
dbfilename = etpConst[cmethod[3]]
# verify checksum
mytxt = "%s %s %s" % (red(_("Checking downloaded database")),darkgreen(dbfilename),red("..."))
self.Entropy.updateProgress(
mytxt,
importance = 0,
back = True,
type = "info",
header = "\t"
)
db_status = self.__verify_database_checksum(repo, cmethod)
if db_status == -1:
mytxt = "%s. %s !" % (red(_("Cannot open digest")),red(_("Cannot verify database integrity")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = "\t"
)
elif db_status:
mytxt = "%s: %s" % (red(_("Downloaded database status")),bold(_("OK")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = "\t"
)
else:
mytxt = "%s: %s" % (red(_("Downloaded database status")),darkred(_("ERROR")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "error",
header = "\t"
)
mytxt = "%s. %s" % (red(_("An error occured while checking database integrity")),red(_("Giving up")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "error",
header = "\t"
)
return 1
return 0
def show_repository_information(self, repo, count_info):
self.Entropy.updateProgress(
bold("%s") % ( etpRepositories[repo]['description'] ),
importance = 2,
type = "info",
count = count_info,
header = blue(" # ")
)
mytxt = "%s: %s" % (red(_("Database URL")),darkgreen(etpRepositories[repo]['database']),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = blue(" # ")
)
mytxt = "%s: %s" % (red(_("Database local path")),darkgreen(etpRepositories[repo]['dbpath']),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # ")
)
mytxt = "%s: %s" % (red(_("Database EAPI")),darkgreen(str(self.dbformat_eapi)),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # ")
)
def get_eapi3_local_database(self, repo):
dbfile = os.path.join(etpRepositories[repo]['dbpath'],etpConst['etpdatabasefile'])
mydbconn = None
try:
mydbconn = self.Entropy.openGenericDatabase(dbfile, xcache = False, indexing_override = False)
mydbconn.validateDatabase()
except (
self.Entropy.dbapi2.OperationalError,
self.Entropy.dbapi2.IntegrityError,
exceptionTools.SystemDatabaseError,
IOError,
OSError,):
mydbconn = None
return mydbconn
def get_eapi3_database_differences(self, eapi3_interface, repo, idpackages, session):
data = eapi3_interface.CmdInterface.differential_packages_comparison(
session, idpackages, repo, etpConst['currentarch'], etpConst['product']
)
if isinstance(data,bool): # then it's probably == False
return False,False,False
elif not isinstance(data,dict):
return None,None,None
elif not data.has_key('added') or \
not data.has_key('removed') or \
not data.has_key('checksum'):
return None,None,None
return data['added'],data['removed'],data['checksum']
def get_eapi3_database_treeupdates(self, eapi3_interface, repo, session):
self.socket.setdefaulttimeout(self.big_socket_timeout)
data = eapi3_interface.CmdInterface.get_repository_treeupdates(
session, repo, etpConst['currentarch'], etpConst['product']
)
if not isinstance(data,dict): return None,None
return data.get('digest'), data.get('actions')
def get_eapi3_package_sets(self, eapi3_interface, repo, session):
self.socket.setdefaulttimeout(self.big_socket_timeout)
data = eapi3_interface.CmdInterface.get_package_sets(
session, repo, etpConst['currentarch'], etpConst['product']
)
if not isinstance(data,dict): return {}
return data
def handle_eapi3_database_sync(self, repo, threshold = 1500, chunk_size = 12):
def prepare_exit(mysock, session = None):
try:
if session != None:
mysock.close_session(session)
mysock.disconnect()
except (self.socket.error,):
pass
eapi3_interface = self.get_eapi3_connection(repo)
if eapi3_interface == None: return False
session = eapi3_interface.open_session()
try:
mydbconn = self.get_eapi3_local_database(repo)
myidpackages = mydbconn.listAllIdpackages()
except (self.dbapi2.DatabaseError,self.dbapi2.IntegrityError,self.dbapi2.OperationalError,):
prepare_exit(eapi3_interface, session)
return False
added_ids, removed_ids, checksum = self.get_eapi3_database_differences(
eapi3_interface, repo,
myidpackages, session
)
if (None in (added_ids,removed_ids,checksum)) or \
(not added_ids and not removed_ids and self.forceUpdate):
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
return False
elif not checksum: # {added_ids, removed_ids, checksum} == False
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
mytxt = "%s: %s" % ( blue(_("EAPI3 Service status")), darkred(_("remote database suddenly locked")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # "),
)
return None
# is it worth it?
if len(added_ids) > threshold:
mytxt = "%s: %s (%s: %s/%s)" % (
blue(_("EAPI3 Service")), darkred(_("skipping differential sync")),
brown(_("threshold")), blue(str(len(added_ids))), darkred(str(threshold)),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # "),
)
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
return False
count = 0
added_segments = []
mytmp = set()
for idpackage in added_ids:
count += 1
mytmp.add(idpackage)
if count % chunk_size == 0:
added_segments.append(list(mytmp))
mytmp.clear()
if mytmp: added_segments.append(list(mytmp))
del mytmp
# fetch and store
count = 0
maxcount = len(added_segments)
for segment in added_segments:
count += 1
mytxt = "%s %s" % (blue(_("Fetching segments")), "...",)
self.Entropy.updateProgress(
mytxt, importance = 0, type = "info",
header = "\t", back = True, count = (count,maxcount,)
)
fetch_count = 0
max_fetch_count = 5
error_caught = False
error_rc = None
while 1:
# anti loop protection
if fetch_count > max_fetch_count:
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
error_caught = True
error_rc = False
break
fetch_count += 1
pkgdata = eapi3_interface.CmdInterface.get_package_information(
session, segment, repo, etpConst['currentarch'], etpConst['product']
)
if pkgdata == None:
mytxt = "%s: %s" % ( blue(_("Fetch error on segment")), darkred(str(segment)),)
self.Entropy.updateProgress(
mytxt, importance = 1, type = "warning",
header = "\t", count = (count,maxcount,)
)
continue
elif not pkgdata: # pkgdata == False
mytxt = "%s: %s" % (
blue(_("Service status")),
darkred("remote database suddenly locked"),
)
self.Entropy.updateProgress(
mytxt, importance = 1, type = "info",
header = "\t", count = (count,maxcount,)
)
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
error_caught = True
break
elif isinstance(pkgdata,tuple):
mytxt = "%s: %s, %s. %s" % ( blue(_("Service status")), pkgdata[0], pkgdata[1], darkred("Error processing the command"),)
self.Entropy.updateProgress(
mytxt, importance = 1, type = "info",
header = "\t", count = (count,maxcount,)
)
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
error_caught = True
break
try:
for idpackage in pkgdata:
self.dumpTools.dumpobj(
"%s%s" % (etpCache['eapi3_fetch'],idpackage,),
pkgdata[idpackage],
ignoreExceptions = False
)
except (IOError,EOFError,OSError,), e:
mytxt = "%s: %s: %s." % ( blue(_("Local status")), darkred("Error storing data"), e,)
self.Entropy.updateProgress(
mytxt, importance = 1, type = "info",
header = "\t", count = (count,maxcount,)
)
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
error_caught = True
break
break
if error_caught: return error_rc
del added_segments
# get treeupdates stuff
dbdigest, treeupdates_actions = self.get_eapi3_database_treeupdates(eapi3_interface, repo, session)
if dbdigest == None:
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
mytxt = "%s: %s" % ( blue(_("EAPI3 Service status")), darkred(_("treeupdates data not available")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # "),
)
return None
try:
mydbconn.setRepositoryUpdatesDigest(repo, dbdigest)
mydbconn.bumpTreeUpdatesActions(treeupdates_actions)
except (self.dbapi2.DatabaseError,self.dbapi2.IntegrityError,self.dbapi2.OperationalError,):
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
mytxt = "%s: %s" % (blue(_("EAPI3 Service status")), darkred(_("cannot update treeupdates data")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # "),
)
return None
# get updated package sets
repo_sets = self.get_eapi3_package_sets(eapi3_interface, repo, session)
try:
mydbconn.clearPackageSets()
mydbconn.insertPackageSets(repo_sets)
except (self.dbapi2.DatabaseError,self.dbapi2.IntegrityError,self.dbapi2.OperationalError,):
mydbconn.closeDB()
prepare_exit(eapi3_interface, session)
mytxt = "%s: %s" % (blue(_("EAPI3 Service status")), darkred(_("cannot update package sets data")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # "),
)
return None
# I don't need you anymore
# disconnect socket
prepare_exit(eapi3_interface, session)
# now that we have all stored, add
count = 0
maxcount = len(added_ids)
for idpackage in added_ids:
count += 1
mydata = self.Entropy.Cacher.pop("%s%s" % (etpCache['eapi3_fetch'],idpackage,))
if mydata == None:
mytxt = "%s: %s" % (
blue(_("Fetch error on segment while adding")),
darkred(str(segment)),
)
self.Entropy.updateProgress(
mytxt, importance = 1, type = "warning",
header = "\t", count = (count,maxcount,)
)
mydbconn.closeDB()
return False
mytxt = "%s %s" % (blue(_("Injecting package")), darkgreen(mydata['atom']),)
self.Entropy.updateProgress(
mytxt, importance = 0, type = "info",
header = "\t", back = True, count = (count,maxcount,)
)
mydbconn.addPackage(
mydata, revision = mydata['revision'],
idpackage = idpackage, do_remove = False,
do_commit = False, formatted_content = True
)
self.Entropy.updateProgress(
blue(_("Packages injection complete")), importance = 0,
type = "info", header = "\t",
)
# now remove
maxcount = len(removed_ids)
count = 0
for idpackage in removed_ids:
myatom = mydbconn.retrieveAtom(idpackage)
count += 1
mytxt = "%s: %s" % (blue(_("Removing package")), darkred(str(myatom)),)
self.Entropy.updateProgress(
mytxt, importance = 0, type = "info",
header = "\t", back = True, count = (count,maxcount,)
)
mydbconn.removePackage(idpackage, do_cleanup = False, do_commit = False)
self.Entropy.updateProgress(
blue(_("Packages removal complete")),
importance = 0, type = "info",
header = "\t",
)
#mydbconn.doCleanups()
mydbconn.commitChanges()
# now verify if both checksums match
result = False
mychecksum = mydbconn.database_checksum(do_order = True, strict = False, strings = True)
if checksum == mychecksum:
result = True
else:
mytxt = "%s: %s: %s | %s: %s" % (
blue(_("Database checksum doesn't match remote.")),
darkgreen(_("local")), mychecksum,
darkred(_("remote")), checksum,
)
self.Entropy.updateProgress(
mytxt, importance = 0,
type = "info", header = "\t",
)
mydbconn.closeDB()
return result
def run_sync(self):
self.dbupdated = False
repocount = 0
repolength = len(self.reponames)
for repo in self.reponames:
repocount += 1
self.reset_dbformat_eapi(repo)
self.show_repository_information(repo, (repocount,repolength))
if not self.forceUpdate:
updated = self.handle_repository_update(repo)
if updated:
self.Entropy.cycleDone()
self.alreadyUpdated += 1
continue
locked = self.handle_repository_lock(repo)
if locked:
self.notAvailable += 1
self.Entropy.cycleDone()
continue
# clear database interface cache belonging to this repository
self.clear_repository_cache(repo)
self.__ensure_repository_path(repo)
# dealing with EAPI
# setting some vars
do_skip = False
skip_this_repo = False
db_down_status = False
do_db_update_transfer = False
rc = 0
# some variables
dumpfile = os.path.join(etpRepositories[repo]['dbpath'],etpConst['etpdatabasedump'])
dbfile = os.path.join(etpRepositories[repo]['dbpath'],etpConst['etpdatabasefile'])
dbfile_old = dbfile+".sync"
while 1:
if do_skip:
break
if self.dbformat_eapi < 3:
cmethod = self.__validate_compression_method(repo)
down_status = self.handle_database_download(repo, cmethod)
if not down_status:
self.Entropy.cycleDone()
self.notAvailable += 1
do_skip = True
skip_this_repo = True
continue
db_down_status = self.handle_database_checksum_download(repo, cmethod)
break
elif self.dbformat_eapi == 3 and not (os.path.isfile(dbfile) and os.access(dbfile,os.W_OK)):
do_db_update_transfer = None
self.dbformat_eapi -= 1
continue
elif self.dbformat_eapi == 3:
status = False
try:
status = self.handle_eapi3_database_sync(repo)
except self.socket.error, e:
mytxt = "%s: %s" % (
blue(_("EAPI3 Service error")),
darkred(unicode(e)),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = blue(" # "),
)
status = False
if status == None: # remote db not available anymore ?
time.sleep(5)
locked = self.handle_repository_lock(repo)
if locked:
self.Entropy.cycleDone()
self.notAvailable += 1
do_skip = True
skip_this_repo = True
else: # ah, well... dunno then...
do_db_update_transfer = None
self.dbformat_eapi -= 1
continue
elif not status: # (status == False)
# set to none and completely skip database alignment
do_db_update_transfer = None
self.dbformat_eapi -= 1
continue
break
if skip_this_repo:
continue
if self.dbformat_eapi in (1,2,):
if self.dbformat_eapi == 2 and db_down_status:
rc = self.check_downloaded_database(repo, cmethod)
if rc != 0:
# delete all
self.__remove_repository_files(repo, cmethod)
self.syncErrors = True
self.Entropy.cycleDone()
continue
if isinstance(do_db_update_transfer,bool) and not do_db_update_transfer:
if os.path.isfile(dbfile):
try:
shutil.move(dbfile,dbfile_old)
do_db_update_transfer = True
except:
pass
# unpack database
unpack_status = self.handle_downloaded_database_unpack(repo, cmethod)
if not unpack_status:
# delete all
self.__remove_repository_files(repo, cmethod)
self.syncErrors = True
self.Entropy.cycleDone()
continue
if self.dbformat_eapi == 1 and db_down_status:
rc = self.check_downloaded_database(repo, cmethod)
if rc != 0:
# delete all
self.__remove_repository_files(repo, cmethod)
self.syncErrors = True
self.Entropy.cycleDone()
if os.path.isfile(dbfile_old):
os.remove(dbfile_old)
continue
# re-validate
if not os.path.isfile(dbfile):
do_db_update_transfer = False
elif os.path.isfile(dbfile) and not do_db_update_transfer and (self.dbformat_eapi != 1):
os.remove(dbfile)
if self.dbformat_eapi == 2:
rc = self.do_eapi2_inject_downloaded_dump(dumpfile, dbfile, cmethod)
if do_db_update_transfer:
self.do_eapi1_eapi2_databases_alignment(dbfile, dbfile_old)
if self.dbformat_eapi == 2:
# remove the dump
os.remove(dumpfile)
if rc != 0:
# delete all
self.__remove_repository_files(repo, cmethod)
self.syncErrors = True
self.Entropy.cycleDone()
if os.path.isfile(dbfile_old):
os.remove(dbfile_old)
continue
if os.path.isfile(dbfile) and os.access(dbfile,os.W_OK):
try:
self.Entropy.setup_default_file_perms(dbfile)
except OSError: # notification applet
pass
# database is going to be updated
self.dbupdated = True
self.do_standard_items_download(repo)
self.Entropy.update_repository_revision(repo)
if self.Entropy.indexing:
self.do_database_indexing(repo)
if (repo == etpConst['officialrepositoryid']):
try:
self.run_config_files_updates(repo)
except Exception, e:
self.entropyTools.printTraceback()
mytxt = "%s: %s" % (
blue(_("Configuration files update error, not critical, continuing")),
darkred(unicode(e)),
)
self.Entropy.updateProgress(mytxt, importance = 0, type = "info", header = blue(" # "),)
self.updated_repos.add(repo)
self.Entropy.cycleDone()
# remove garbage
if os.path.isfile(dbfile_old):
os.remove(dbfile_old)
# keep them closed
self.Entropy.closeAllRepositoryDatabases()
self.Entropy.validate_repositories()
self.Entropy.closeAllRepositoryDatabases()
# clean caches, fetch security
if self.dbupdated:
self.Entropy.generate_cache(
depcache = self.Entropy.xcache,
configcache = False,
client_purge = False,
install_queue = False
)
if self.fetchSecurity:
self.do_update_security_advisories()
# do treeupdates
if isinstance(self.Entropy.clientDbconn,EntropyDatabaseInterface):
for repo in self.reponames:
dbc = self.Entropy.openRepositoryDatabase(repo)
dbc.clientUpdatePackagesData(self.Entropy.clientDbconn)
self.Entropy.closeAllRepositoryDatabases()
if self.syncErrors:
self.Entropy.updateProgress(
red(_("Something bad happened. Please have a look.")),
importance = 1,
type = "warning",
header = darkred(" @@ ")
)
self.syncErrors = True
self.Entropy._resources_run_remove_lock()
return 128
if not self.noEquoCheck:
self.check_entropy_updates()
return 0
def run_config_files_updates(self, repo):
# are we root?
if etpConst['uid'] != 0:
self.Entropy.updateProgress(
brown(_("Skipping configuration files update, you are not root.")),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return
# make.conf
self._config_updates_make_conf(repo)
self._config_updates_make_profile(repo)
def _config_updates_make_conf(self, repo):
## WARNING: it doesn't handle multi-line variables, yet. remember this.
url, repo_make_conf = self._construct_paths("make.conf", repo, None)
system_make_conf = etpConst['spm']['global_make_conf']
make_conf_variables_check = ["CHOST"]
if os.path.isfile(repo_make_conf) and os.access(repo_make_conf,os.R_OK):
if not os.path.isfile(system_make_conf):
self.Entropy.updateProgress(
"%s %s. %s." % (red(system_make_conf),blue(_("does not exist")),blue(_("Overwriting")),),
importance = 1,
type = "info",
header = blue(" @@ ")
)
if os.path.lexists(system_make_conf):
shutil.move(
system_make_conf,
"%s.backup_%s" % (system_make_conf,self.entropyTools.getRandomNumber(),)
)
shutil.copy2(repo_make_conf,system_make_conf)
elif os.access(system_make_conf,os.W_OK):
repo_f = open(repo_make_conf,"r")
sys_f = open(system_make_conf,"r")
repo_make_c = [x.strip() for x in repo_f.readlines()]
sys_make_c = [x.strip() for x in sys_f.readlines()]
repo_f.close()
sys_f.close()
# read repository settings
repo_data = {}
for setting in make_conf_variables_check:
for line in repo_make_c:
if line.startswith(setting+"="):
# there can't be bash vars with a space after its name on declaration
repo_data[setting] = line
# I don't break, because there might be other overlapping settings
differences = {}
# update make.conf data in memory
for setting in repo_data:
for idx in range(len(sys_make_c)):
line = sys_make_c[idx]
if line.startswith(setting+"=") and (line != repo_data[setting]):
# there can't be bash vars with a space after its name on declaration
self.Entropy.updateProgress(
"%s: %s %s. %s." % (
red(system_make_conf), bold(unicode(setting)),
blue(_("variable differs")), red(_("Updating")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
differences[setting] = repo_data[setting]
line = repo_data[setting]
sys_make_c[idx] = line
if differences:
self.Entropy.updateProgress(
"%s: %s." % (red(system_make_conf), blue(_("updating critical variables")),),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# backup user make.conf
shutil.copy2(system_make_conf,"%s.entropy_backup" % (system_make_conf,))
self.Entropy.updateProgress(
"%s: %s." % (
red(system_make_conf), darkgreen("writing changes to disk"),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# write to disk, safely
tmp_make_conf = "%s.entropy_write" % (system_make_conf,)
f = open(tmp_make_conf,"w")
for line in sys_make_c: f.write(line+"\n")
f.flush()
f.close()
shutil.move(tmp_make_conf,system_make_conf)
# update environment
for var in differences:
try:
myval = '='.join(differences[var].strip().split("=")[1:])
if myval:
if myval[0] in ("'",'"',): myval = myval[1:]
if myval[-1] in ("'",'"',): myval = myval[:-1]
except IndexError:
myval = ''
os.environ[var] = myval
def _config_updates_make_profile(self, repo):
url, repo_make_profile = self._construct_paths("profile.link", repo, None)
system_make_profile = etpConst['spm']['global_make_profile']
if not (os.path.isfile(repo_make_profile) and os.access(repo_make_profile,os.R_OK)):
return
f = open(repo_make_profile,"r")
repo_profile_link_data = f.readline().strip()
f.close()
current_profile_link = ''
if os.path.islink(system_make_profile) and os.access(system_make_profile,os.R_OK):
current_profile_link = os.readlink(system_make_profile)
if repo_profile_link_data != current_profile_link:
self.Entropy.updateProgress(
"%s: %s %s. %s." % (
red(system_make_profile), blue("link"),
blue(_("differs")), red(_("Updating")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
if os.path.lexists(repo_make_profile):
shutil.move(system_make_profile,"%s.entropy_old" % (system_make_profile,))
# broken symlink?
if os.path.islink(system_make_profile) and not os.path.isdir(system_make_profile):
os.remove(system_make_profile)
os.symlink(repo_profile_link_data,system_make_profile)
if not self.entropyTools.is_valid_path(system_make_profile):
# revert change, link does not exist yet
self.Entropy.updateProgress(
"%s: %s %s. %s." % (
red(system_make_profile), blue("new link"),
blue(_("does not exist")), red(_("Reverting")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
os.symlink(current_profile_link,system_make_profile)
def check_entropy_updates(self):
rc = False
if not self.noEquoCheck:
try:
rc = self.Entropy.check_equo_updates()
except:
pass
if rc:
self.newEquo = True
mytxt = "%s: %s. %s." % (
bold("Equo/Entropy"),
blue(_("a new release is available")),
darkred(_("Mind to install it before any other package")),
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = bold(" !!! ")
)
def handle_downloaded_database_unpack(self, repo, cmethod):
file_to_unpack = etpConst['etpdatabasedump']
if self.dbformat_eapi == 1:
file_to_unpack = etpConst['etpdatabasefile']
mytxt = "%s %s %s" % (red(_("Unpacking database to")),darkgreen(file_to_unpack),red("..."),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = "\t"
)
myrc = self.__unpack_downloaded_database(repo, cmethod)
if myrc != 0:
mytxt = "%s %s !" % (red(_("Cannot unpack compressed package")),red(_("Skipping repository")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = "\t"
)
return False
return True
def handle_database_checksum_download(self, repo, cmethod):
hashfile = etpConst['etpdatabasehashfile']
downitem = 'ck'
if self.dbformat_eapi == 2: # EAPI = 2
hashfile = etpConst[cmethod[4]]
downitem = 'dbdumpck'
mytxt = "%s %s %s" % (red(_("Downloading checksum")),darkgreen(hashfile),red("..."),)
# download checksum
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = "\t"
)
db_down_status = self.download_item(downitem, repo, cmethod, disallow_redirect = True)
if not db_down_status:
mytxt = "%s %s !" % (red(_("Cannot fetch checksum")),red(_("Cannot verify database integrity")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = "\t"
)
return db_down_status
def load_background_repository_lock_check(self, repo):
# kill previous
self.current_repository_got_locked = False
self.kill_previous_repository_lock_scanner()
self.LockScanner = self.entropyTools.TimeScheduled( self.repository_lock_scanner, 2, {'repo': repo} )
self.LockScanner.setName("Lock_Scanner::"+str(abs(hash(os.urandom(20)))))
self.LockScanner.start()
def kill_previous_repository_lock_scanner(self):
if self.LockScanner != None:
self.LockScanner.kill()
def repository_lock_scanner(self, data):
repo = data['repo']
locked = self.handle_repository_lock(repo)
if locked:
self.current_repository_got_locked = True
def repository_lock_scanner_status(self):
# raise an exception if repo got suddenly locked
if self.current_repository_got_locked:
mytxt = _("Current repository got suddenly locked. Download aborted.")
raise exceptionTools.RepositoryError('RepositoryError %s' % (mytxt,))
def handle_database_download(self, repo, cmethod):
def show_repo_locked_message():
mytxt = "%s: %s." % (bold(_("Attention")),red(_("remote database got suddenly locked")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = "\t"
)
# starting to download
mytxt = "%s ..." % (red(_("Downloading repository database")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = "\t"
)
down_status = False
if self.dbformat_eapi == 2:
# start a check in background
self.load_background_repository_lock_check(repo)
down_status = self.download_item("dbdump", repo, cmethod, lock_status_func = self.repository_lock_scanner_status, disallow_redirect = True)
if self.current_repository_got_locked:
self.kill_previous_repository_lock_scanner()
show_repo_locked_message()
return False
if not down_status: # fallback to old db
# start a check in background
self.load_background_repository_lock_check(repo)
self.dbformat_eapi = 1
down_status = self.download_item("db", repo, cmethod, lock_status_func = self.repository_lock_scanner_status, disallow_redirect = True)
if self.current_repository_got_locked:
self.kill_previous_repository_lock_scanner()
show_repo_locked_message()
return False
if not down_status:
mytxt = "%s: %s." % (bold(_("Attention")),red(_("database does not exist online")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = "\t"
)
self.kill_previous_repository_lock_scanner()
return down_status
def handle_repository_update(self, repo):
# check if database is already updated to the latest revision
update = self.is_repository_updatable(repo)
if not update:
mytxt = "%s: %s." % (bold(_("Attention")),red(_("database is already up to date")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = "\t"
)
return True
# also check for eapi3 lock
if self.dbformat_eapi == 3:
locked = self.is_repository_eapi3_locked(repo)
if locked:
mytxt = "%s: %s." % (bold(_("Attention")),red(_("database will be ready soon")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = "\t"
)
return True
return False
def handle_repository_lock(self, repo):
# get database lock
unlocked = self.is_repository_unlocked(repo)
if not unlocked:
mytxt = "%s: %s. %s." % (
bold(_("Attention")),
red(_("Repository is being updated")),
red(_("Try again in a few minutes")),
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = "\t"
)
return True
return False
def do_eapi1_eapi2_databases_alignment(self, dbfile, dbfile_old):
dbconn = self.Entropy.openGenericDatabase(dbfile, xcache = False, indexing_override = False)
old_dbconn = self.Entropy.openGenericDatabase(dbfile_old, xcache = False, indexing_override = False)
upd_rc = 0
try:
upd_rc = old_dbconn.alignDatabases(dbconn, output_header = "\t")
except (dbapi2.OperationalError,dbapi2.IntegrityError,):
pass
old_dbconn.closeDB()
dbconn.closeDB()
if upd_rc > 0:
# -1 means no changes, == force used
# 0 means too much hassle
shutil.move(dbfile_old,dbfile)
return upd_rc
def do_eapi2_inject_downloaded_dump(self, dumpfile, dbfile, cmethod):
# load the dump into database
mytxt = "%s %s, %s %s" % (
red(_("Injecting downloaded dump")),
darkgreen(etpConst[cmethod[3]]),
red(_("please wait")),
red("..."),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = "\t"
)
dbconn = self.Entropy.openGenericDatabase(dbfile, xcache = False, indexing_override = False)
rc = dbconn.doDatabaseImport(dumpfile, dbfile)
dbconn.closeDB()
return rc
def do_update_security_advisories(self):
# update Security Advisories
try:
securityConn = self.Entropy.Security()
securityConn.fetch_advisories()
except Exception, e:
self.entropyTools.printTraceback(f = self.Entropy.clientLog)
mytxt = "%s: %s" % (red(_("Advisories fetch error")),e,)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" @@ ")
)
def do_standard_items_download(self, repo):
download_items = [
(
"ca.cert",
etpConst['etpdatabasecacertfile'],
True,
"%s %s %s" % (
red(_("Downloading SSL CA certificate")),
darkgreen(etpConst['etpdatabasecacertfile']),
red("..."),
)
),
(
"server.cert",
etpConst['etpdatabaseservercertfile'],
True,
"%s %s %s" % (
red(_("Downloading SSL Server certificate")),
darkgreen(etpConst['etpdatabaseservercertfile']),
red("..."),
)
),
(
"mask",
etpConst['etpdatabasemaskfile'],
True,
"%s %s %s" % (
red(_("Downloading package mask")),
darkgreen(etpConst['etpdatabasemaskfile']),
red("..."),
)
),
(
"system_mask",
etpConst['etpdatabasesytemmaskfile'],
True,
"%s %s %s" % (
red(_("Downloading packages system mask")),
darkgreen(etpConst['etpdatabasesytemmaskfile']),
red("..."),
)
),
(
"conflicting_tagged",
etpConst['etpdatabaseconflictingtaggedfile'],
True,
"%s %s %s" % (
red(_("Downloading conflicting tagged packages file")),
darkgreen(etpConst['etpdatabaseconflictingtaggedfile']),
red("..."),
)
),
(
"lic_whitelist",
etpConst['etpdatabaselicwhitelistfile'],
True,
"%s %s %s" % (
red(_("Downloading license whitelist")),
darkgreen(etpConst['etpdatabaselicwhitelistfile']),
red("..."),
)
),
(
"rev",
etpConst['etpdatabasemaskfile'],
False,
"%s %s %s" % (
red(_("Downloading revision")),
darkgreen(etpConst['etpdatabaserevisionfile']),
red("..."),
)
),
(
"make.conf",
os.path.basename(etpConst['spm']['global_make_conf']),
True,
"%s %s %s" % (
red(_("Downloading SPM global configuration")),
darkgreen(os.path.basename(etpConst['spm']['global_make_conf'])),
red("..."),
)
),
(
"package.unmask",
os.path.basename(etpConst['spm']['global_package_unmask']),
True,
"%s %s %s" % (
red(_("Downloading SPM package unmasking configuration")),
darkgreen(os.path.basename(etpConst['spm']['global_package_unmask'])),
red("..."),
)
),
(
"package.keywords",
os.path.basename(etpConst['spm']['global_package_keywords']),
True,
"%s %s %s" % (
red(_("Downloading SPM package keywording configuration")),
darkgreen(os.path.basename(etpConst['spm']['global_package_keywords'])),
red("..."),
)
),
(
"package.use",
os.path.basename(etpConst['spm']['global_package_use']),
True,
"%s %s %s" % (
red(_("Downloading SPM package USE flags configuration")),
darkgreen(os.path.basename(etpConst['spm']['global_package_use'])),
red("..."),
)
),
(
"profile.link",
etpConst['spm']['global_make_profile_link_name'],
True,
"%s %s %s" % (
red(_("Downloading SPM Profile configuration")),
darkgreen(etpConst['spm']['global_make_profile_link_name']),
red("..."),
)
),
(
"notice_board",
os.path.basename(etpRepositories[repo]['local_notice_board']),
True,
"%s %s %s" % (
red(_("Downloading Notice Board")),
darkgreen(os.path.basename(etpRepositories[repo]['local_notice_board'])),
red("..."),
)
)
]
for item, myfile, ignorable, mytxt in download_items:
self.Entropy.updateProgress(
mytxt,
importance = 0,
type = "info",
header = "\t",
back = True
)
mystatus = self.download_item(item, repo, disallow_redirect = True)
mytype = 'info'
if not mystatus:
if ignorable:
message = "%s: %s." % (blue(myfile),red(_("not available, it's ok")))
else:
mytype = 'warning'
message = "%s: %s." % (blue(myfile),darkred(_("not available, not much ok!")))
else:
message = "%s: %s." % (blue(myfile),darkgreen(_("available, w00t!")))
self.Entropy.updateProgress(
message,
importance = 0,
type = mytype,
header = "\t"
)
mytxt = "%s: %s" % (
red(_("Repository revision")),
bold(str(self.Entropy.get_repository_revision(repo))),
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = "\t"
)
def do_database_indexing(self, repo):
# renice a bit, to avoid eating resources
old_prio = self.Entropy.set_priority(15)
mytxt = red("%s ...") % (_("Indexing Repository metadata"),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = "\t",
back = True
)
dbconn = self.Entropy.openRepositoryDatabase(repo)
dbconn.createAllIndexes()
# get list of indexes
repo_indexes = dbconn.listAllIndexes()
if self.Entropy.clientDbconn != None:
try: # client db can be absent
client_indexes = self.Entropy.clientDbconn.listAllIndexes()
if repo_indexes != client_indexes:
self.Entropy.clientDbconn.createAllIndexes()
except:
pass
self.Entropy.set_priority(old_prio)
def sync(self):
# close them
self.Entropy.closeAllRepositoryDatabases()
# let's dance!
mytxt = darkgreen("%s ...") % (_("Repositories synchronization"),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "info",
header = darkred(" @@ ")
)
gave_up = self.Entropy.lock_check(self.Entropy._resources_run_check_lock)
if gave_up:
return 3
locked = self.Entropy.application_lock_check()
if locked:
self.Entropy._resources_run_remove_lock()
return 4
# lock
self.Entropy._resources_run_create_lock()
try:
rc = self.run_sync()
except:
self.Entropy._resources_run_remove_lock()
raise
if rc: return rc
# remove lock
self.Entropy._resources_run_remove_lock()
if (self.notAvailable >= len(self.reponames)):
return 2
elif (self.notAvailable > 0):
return 1
return 0
class QAInterface:
import entropyTools
def __init__(self, EntropyInterface):
if not isinstance(EntropyInterface, (EquoInterface, ServerInterface)) and \
not issubclass(EntropyInterface, (EquoInterface, ServerInterface)):
mytxt = _("A valid EquoInterface/ServerInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (EntropyInterface,mytxt,))
self.Entropy = EntropyInterface
def test_depends_linking(self, idpackages, dbconn, repo = etpConst['officialrepositoryid']):
scan_msg = blue(_("Now searching for broken depends"))
self.Entropy.updateProgress(
"[repo:%s] %s..." % (
darkgreen(repo),
scan_msg,
),
importance = 1,
type = "info",
header = red(" @@ ")
)
broken = False
count = 0
maxcount = len(idpackages)
for idpackage in idpackages:
count += 1
atom = dbconn.retrieveAtom(idpackage)
scan_msg = "%s, %s:" % (blue(_("scanning for broken depends")),darkgreen(atom),)
self.Entropy.updateProgress(
"[repo:%s] %s" % (
darkgreen(repo),
scan_msg,
),
importance = 1,
type = "info",
header = blue(" @@ "),
back = True,
count = (count,maxcount,)
)
mydepends = dbconn.retrieveDepends(idpackage)
if not mydepends:
continue
for mydepend in mydepends:
myatom = dbconn.retrieveAtom(mydepend)
self.Entropy.updateProgress(
"[repo:%s] %s => %s" % (
darkgreen(repo),
darkgreen(atom),
darkred(myatom),
),
importance = 0,
type = "info",
header = blue(" @@ "),
back = True,
count = (count,maxcount,)
)
mycontent = dbconn.retrieveContent(mydepend)
mybreakages = self.content_test(mycontent)
if not mybreakages:
continue
broken = True
self.Entropy.updateProgress(
"[repo:%s] %s %s => %s" % (
darkgreen(repo),
darkgreen(atom),
darkred(myatom),
bold(_("broken libraries detected")),
),
importance = 1,
type = "warning",
header = purple(" @@ "),
count = (count,maxcount,)
)
for mylib in mybreakages:
self.Entropy.updateProgress(
"%s %s:" % (
darkgreen(mylib),
red(_("needs")),
),
importance = 1,
type = "warning",
header = brown(" ## ")
)
for needed in mybreakages[mylib]:
self.Entropy.updateProgress(
"%s" % (
red(needed),
),
importance = 1,
type = "warning",
header = purple(" # ")
)
return broken
def scan_missing_dependencies(self, idpackages, dbconn, ask = True, self_check = False, repo = etpConst['officialrepositoryid'], black_list = set(), black_list_adder = None):
taint = False
scan_msg = blue(_("Now searching for missing RDEPENDs"))
self.Entropy.updateProgress(
"[repo:%s] %s..." % (
darkgreen(repo),
scan_msg,
),
importance = 1,
type = "info",
header = red(" @@ ")
)
scan_msg = blue(_("scanning for missing RDEPENDs"))
count = 0
maxcount = len(idpackages)
for idpackage in idpackages:
count += 1
atom = dbconn.retrieveAtom(idpackage)
if not atom: continue
self.Entropy.updateProgress(
"[repo:%s] %s: %s" % (
darkgreen(repo),
scan_msg,
darkgreen(atom),
),
importance = 1,
type = "info",
header = blue(" @@ "),
back = True,
count = (count,maxcount,)
)
missing_extended, missing = self.get_missing_rdepends(dbconn, idpackage, self_check = self_check)
missing -= black_list
for item in missing_extended.keys():
missing_extended[item] -= black_list
if not missing_extended[item]:
del missing_extended[item]
if (not missing) or (not missing_extended):
continue
self.Entropy.updateProgress(
"[repo:%s] %s: %s %s:" % (
darkgreen(repo),
blue("package"),
darkgreen(atom),
blue(_("is missing the following dependencies")),
),
importance = 1,
type = "info",
header = red(" @@ "),
count = (count,maxcount,)
)
for missing_data in missing_extended:
self.Entropy.updateProgress(
"%s:" % (brown(unicode(missing_data)),),
importance = 0,
type = "info",
header = purple(" ## ")
)
for dependency in missing_extended[missing_data]:
self.Entropy.updateProgress(
"%s" % (darkred(dependency),),
importance = 0,
type = "info",
header = blue(" # ")
)
if ask:
rc = self.Entropy.askQuestion(_("Do you want to add them?"))
if rc == "No":
continue
rc = self.Entropy.askQuestion(_("Selectively?"))
if rc == "Yes":
newmissing = set()
new_blacklist = set()
for dependency in missing:
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
darkgreen(repo),
brown(atom),
blue(dependency),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
rc = self.Entropy.askQuestion(_("Want to add?"))
if rc == "Yes":
newmissing.add(dependency)
else:
rc = self.Entropy.askQuestion(_("Want to blacklist?"))
if rc == "Yes":
new_blacklist.add(dependency)
if new_blacklist and (black_list_adder != None):
black_list_adder(new_blacklist, repo = repo)
missing = newmissing
if missing:
taint = True
dbconn.insertDependencies(idpackage,missing)
dbconn.commitChanges()
self.Entropy.updateProgress(
"[repo:%s] %s: %s" % (
darkgreen(repo),
darkgreen(atom),
blue(_("missing dependencies added")),
),
importance = 1,
type = "info",
header = red(" @@ "),
count = (count,maxcount,)
)
return taint
def content_test(self, mycontent):
def is_contained(needed, content):
for item in content:
if os.path.basename(item) == needed:
return True
return False
mylibs = {}
for myfile in mycontent:
myfile = myfile.encode('raw_unicode_escape')
if not os.access(myfile,os.R_OK):
continue
if not os.path.isfile(myfile):
continue
if not self.entropyTools.is_elf_file(myfile):
continue
mylibs[myfile] = self.entropyTools.read_elf_dynamic_libraries(myfile)
broken_libs = {}
for mylib in mylibs:
for myneeded in mylibs[mylib]:
# is this inside myself ?
if is_contained(myneeded, mycontent):
continue
found = self.resolve_dynamic_library(myneeded, mylib)
if found:
continue
if not broken_libs.has_key(mylib):
broken_libs[mylib] = set()
broken_libs[mylib].add(myneeded)
return broken_libs
def resolve_dynamic_library(self, library, requiring_executable):
def do_resolve(mypaths):
found_path = None
for mypath in mypaths:
mypath = os.path.join(etpConst['systemroot']+mypath,library)
if not os.access(mypath,os.R_OK):
continue
if os.path.isdir(mypath):
continue
if not self.entropyTools.is_elf_file(mypath):
continue
found_path = mypath
break
return found_path
mypaths = self.entropyTools.collectLinkerPaths()
found_path = do_resolve(mypaths)
if not found_path:
mypaths = self.entropyTools.read_elf_linker_paths(requiring_executable)
found_path = do_resolve(mypaths)
return found_path
def get_missing_rdepends(self, dbconn, idpackage, self_check = False):
rdepends = {}
rdepends_plain = set()
neededs = dbconn.retrieveNeeded(idpackage, extended = True)
ldpaths = set(self.entropyTools.collectLinkerPaths())
deps_content = set()
dependencies = self._get_deep_dependency_list(dbconn, idpackage, atoms = True)
dependencies_cache = set()
def update_depscontent(mycontent, dbconn, ldpaths):
return set( \
[ x for x in mycontent if os.path.dirname(x) in ldpaths \
and (dbconn.isNeededAvailable(os.path.basename(x)) > 0) ] \
)
def is_in_content(myneeded, content):
for item in content:
item = os.path.basename(item)
if myneeded == item:
return True
return False
for dependency in dependencies:
match = dbconn.atomMatch(dependency)
if match[0] != -1:
mycontent = dbconn.retrieveContent(match[0])
deps_content |= update_depscontent(mycontent, dbconn, ldpaths)
key, slot = dbconn.retrieveKeySlot(match[0])
dependencies_cache.add((key,slot))
key, slot = dbconn.retrieveKeySlot(idpackage)
mycontent = dbconn.retrieveContent(idpackage)
deps_content |= update_depscontent(mycontent, dbconn, ldpaths)
dependencies_cache.add((key,slot))
idpackages_cache = set()
for needed, elfclass in neededs:
data_solved = dbconn.resolveNeeded(needed,elfclass)
data_size = len(data_solved)
data_solved = set([x for x in data_solved if x[0] not in idpackages_cache])
if not data_solved or (data_size != len(data_solved)):
continue
if self_check:
if is_in_content(needed,mycontent):
continue
found = False
for data in data_solved:
if data[1] in deps_content:
found = True
break
if not found:
for data in data_solved:
key, slot = dbconn.retrieveKeySlot(data[0])
if (key,slot) not in dependencies_cache:
if not dbconn.isSystemPackage(data[0]):
if not rdepends.has_key((needed,elfclass)):
rdepends[(needed,elfclass)] = set()
rdepends[(needed,elfclass)].add(key+":"+slot)
rdepends_plain.add(key+":"+slot)
idpackages_cache.add(data[0])
return rdepends, rdepends_plain
def _get_deep_dependency_list(self, dbconn, idpackage, atoms = False):
mybuffer = self.entropyTools.lifobuffer()
matchcache = set()
depcache = set()
mydeps = dbconn.retrieveDependencies(idpackage)
for mydep in mydeps:
mybuffer.push(mydep)
mydep = mybuffer.pop()
while mydep:
if mydep in depcache:
mydep = mybuffer.pop()
continue
mymatch = dbconn.atomMatch(mydep)
if atoms:
matchcache.add(mydep)
else:
matchcache.add(mymatch[0])
if mymatch[0] != -1:
owndeps = dbconn.retrieveDependencies(mymatch[0])
for owndep in owndeps: mybuffer.push(owndep)
depcache.add(mydep)
mydep = mybuffer.pop()
if atoms and -1 in matchcache:
matchcache.discard(-1)
return matchcache
'''
Entropy FTP interface
'''
class FtpInterface:
# this must be run before calling the other functions
def __init__(self, ftpuri, EntropyInterface, verbose = True):
if not isinstance(EntropyInterface, (EquoInterface, TextInterface, ServerInterface)) and \
not issubclass(EntropyInterface, (EquoInterface, TextInterface, ServerInterface)):
mytxt = _("A valid TextInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (EntropyInterface,mytxt,))
import socket, ftplib, entropyTools
self.socket, self.ftplib, self.entropyTools = socket, ftplib, entropyTools
self.Entropy = EntropyInterface
self.verbose = verbose
self.oldprogress = 0.0
# import FTP modules
self.socket.setdefaulttimeout(60)
self.ftpuri = ftpuri
self.ftphost = self.entropyTools.extractFTPHostFromUri(self.ftpuri)
self.ftpuser, self.ftppassword, self.ftpport, self.ftpdir = self.entropyTools.extract_ftp_data(ftpuri)
count = 10
while 1:
count -= 1
try:
self.ftpconn = self.ftplib.FTP(self.ftphost)
break
except (self.socket.gaierror,), e:
raise exceptionTools.ConnectionError('ConnectionError: %s' % (e,))
except:
if not count: raise
continue
if self.verbose:
mytxt = _("connecting with user")
self.Entropy.updateProgress(
"[ftp:%s] %s: %s" % (darkgreen(self.ftphost),mytxt,blue(self.ftpuser),),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
try:
self.ftpconn.login(self.ftpuser,self.ftppassword)
except self.ftplib.error_perm, e:
raise exceptionTools.FtpError('FtpError: %s' % (e,))
if self.verbose:
mytxt = _("switching to")
self.Entropy.updateProgress(
"[ftp:%s] %s: %s" % (darkgreen(self.ftphost),mytxt,blue(self.ftpdir),),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
self.setCWD(self.ftpdir, dodir = True)
def setBasedir(self):
rc = self.setCWD(self.ftpdir)
return rc
# this can be used in case of exceptions
def reconnectHost(self):
# import FTP modules
self.socket.setdefaulttimeout(60)
counter = 10
while 1:
counter -= 1
try:
self.ftpconn = self.ftplib.FTP(self.ftphost)
break
except:
if not counter:
raise
continue
if self.verbose:
mytxt = _("reconnecting with user")
self.Entropy.updateProgress(
"[ftp:%s] %s: %s" % (darkgreen(self.ftphost),mytxt,blue(self.ftpuser),),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
self.ftpconn.login(self.ftpuser,self.ftppassword)
if self.verbose:
mytxt = _("switching to")
self.Entropy.updateProgress(
"[ftp:%s] %s: %s" % (darkgreen(self.ftphost),mytxt,blue(self.ftpdir),),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
self.setCWD(self.currentdir)
def getHost(self):
return self.ftphost
def getPort(self):
return self.ftpport
def getDir(self):
return self.ftpdir
def getCWD(self):
pwd = self.ftpconn.pwd()
return pwd
def setCWD(self, mydir, dodir = False):
try:
return self._setCWD(mydir, dodir)
except self.ftplib.error_perm, e:
raise exceptionTools.FtpError('FtpError: %s' % (e,))
def _setCWD(self, mydir, dodir = False):
if self.verbose:
mytxt = _("switching to")
self.Entropy.updateProgress(
"[ftp:%s] %s: %s" % (darkgreen(self.ftphost),mytxt,blue(mydir),),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
try:
self.ftpconn.cwd(mydir)
except self.ftplib.error_perm, e:
if e[0][:3] == '550' and dodir:
self.recursiveMkdir(mydir)
self.ftpconn.cwd(mydir)
else:
raise
self.currentdir = self.getCWD()
def setPASV(self,bool):
self.ftpconn.set_pasv(bool)
def setChmod(self,chmodvalue,file):
return self.ftpconn.voidcmd("SITE CHMOD "+str(chmodvalue)+" "+str(file))
def getFileMtime(self,path):
rc = self.ftpconn.sendcmd("mdtm "+path)
return rc.split()[-1]
def spawnCommand(self,cmd):
return self.ftpconn.sendcmd(cmd)
# list files and directory of a FTP
# @returns a list
def listDir(self):
# directory is: self.ftpdir
try:
rc = self.ftpconn.nlst()
_rc = []
for i in rc:
_rc.append(i.split("/")[-1])
rc = _rc
except:
return []
return rc
# list if the file is available
# @returns True or False
def isFileAvailable(self,filename):
# directory is: self.ftpdir
try:
rc = self.ftpconn.nlst()
_rc = []
for i in rc:
_rc.append(i.split("/")[-1])
rc = _rc
for i in rc:
if i == filename:
return True
return False
except:
return False
def deleteFile(self,file):
try:
rc = self.ftpconn.delete(file)
if rc.startswith("250"):
return True
else:
return False
except:
return False
def recursiveMkdir(self, mypath):
mydirs = [x for x in mypath.split("/") if x]
mycurpath = ""
for mydir in mydirs:
mycurpath = os.path.join(mycurpath,mydir)
if not self.isFileAvailable(mycurpath):
try:
self.mkdir(mycurpath)
except self.ftplib.error_perm, e:
if e[0].lower().find("permission denied") != -1:
raise
elif e[0][:3] != '550':
raise
def mkdir(self,directory):
return self.ftpconn.mkd(directory)
# this function also supports callback, because storbinary doesn't
def advancedStorBinary(self, cmd, fp, callback=None):
''' Store a file in binary mode. Our version supports a callback function'''
self.ftpconn.voidcmd('TYPE I')
conn = self.ftpconn.transfercmd(cmd)
while 1:
buf = fp.readline()
if not buf: break
conn.sendall(buf)
if callback: callback(buf)
conn.close()
# that's another workaround
#return "226"
try:
rc = self.ftpconn.voidresp()
except:
self.reconnectHost()
return "226"
return rc
def updateProgress(self, buf_len):
# get the buffer size
self.mykByteCount += float(buf_len)/1024
# create percentage
if self.myFileSize < 1:
myUploadPercentage = 100.0
else:
myUploadPercentage = round((round(self.mykByteCount,1)/self.myFileSize)*100,1)
currentprogress = myUploadPercentage
myUploadSize = round(self.mykByteCount,1)
if (currentprogress > self.oldprogress+0.5) and (myUploadPercentage < 100.1) and (myUploadSize <= self.myFileSize):
myUploadPercentage = str(myUploadPercentage)+"%"
# create text
mytxt = _("Upload status")
currentText = brown(" <-> %s: " % (mytxt,)) + \
green(str(myUploadSize)) + "/" + red(str(self.myFileSize)) + " kB " + \
brown("[") + str(myUploadPercentage) + brown("]")
print_info(currentText, back = True)
# XXX too slow, reimplement self.updateProgress and do whatever you want
#self.Entropy.updateProgress(currentText, importance = 0, type = "info", back = True)
self.oldprogress = currentprogress
def uploadFile(self,file,ascii = False):
self.oldprogress = 0.0
def uploadFileAndUpdateProgress(buf):
self.updateProgress(len(buf))
for i in range(10): # ten tries
filename = file.split("/")[len(file.split("/"))-1]
try:
f = open(file,"r")
# get file size
self.myFileSize = round(float(self.entropyTools.get_file_size(file))/1024,1)
self.mykByteCount = 0
if self.isFileAvailable(filename+".tmp"):
self.deleteFile(filename+".tmp")
if (ascii):
rc = self.ftpconn.storlines("STOR "+filename+".tmp",f)
else:
rc = self.advancedStorBinary("STOR "+filename+".tmp", f, callback = uploadFileAndUpdateProgress )
# now we can rename the file with its original name
self.renameFile(filename+".tmp",filename)
f.close()
if rc.find("226") != -1: # upload complete
return True
else:
return False
except Exception, e: # connection reset by peer
import traceback
traceback.print_exc()
self.Entropy.updateProgress(" ", importance = 0, type = "info")
mytxt = red("%s: %s, %s... #%s") % (
_("Upload issue"),
e,
_("retrying"),
i+1,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = " "
)
self.reconnectHost() # reconnect
if self.isFileAvailable(filename):
self.deleteFile(filename)
if self.isFileAvailable(filename+".tmp"):
self.deleteFile(filename+".tmp")
pass
def downloadFile(self, filename, downloaddir, ascii = False):
self.oldprogress = 0.0
def downloadFileStoreAndUpdateProgress(buf):
# writing file buffer
f.write(buf)
# update progress
self.mykByteCount += float(len(buf))/1024
# create text
cnt = round(self.mykByteCount,1)
mytxt = _("Download status")
currentText = brown(" <-> %s: " % (mytxt,)) + green(str(cnt)) + "/" + \
red(str(self.myFileSize)) + " kB"
self.Entropy.updateProgress(
currentText,
importance = 0,
type = "info",
back = True,
count = (cnt, self.myFileSize),
percent = True
)
# look if the file exist
if self.isFileAvailable(filename):
self.mykByteCount = 0
# get the file size
self.myFileSize = self.getFileSizeCompat(filename)
if (self.myFileSize):
self.myFileSize = round(float(int(self.myFileSize))/1024,1)
if (self.myFileSize == 0):
self.myFileSize = 1
else:
self.myFileSize = 0
if (not ascii):
f = open(downloaddir+"/"+filename,"wb")
rc = self.ftpconn.retrbinary('RETR '+filename, downloadFileStoreAndUpdateProgress, 1024)
else:
f = open(downloaddir+"/"+filename,"w")
rc = self.ftpconn.retrlines('RETR '+filename, f.write)
f.flush()
f.close()
if rc.find("226") != -1: # upload complete
return True
else:
return False
else:
return None
# also used to move files
def renameFile(self,fromfile,tofile):
rc = self.ftpconn.rename(fromfile,tofile)
return rc
def getFileSize(self,file):
return self.ftpconn.size(file)
def getFileSizeCompat(self,file):
data = self.getRoughList()
for item in data:
if item.find(file) != -1:
return item.split()[4]
return ""
def bufferizer(self,buf):
self.FTPbuffer.append(buf)
def getRoughList(self):
self.FTPbuffer = []
self.ftpconn.dir(self.bufferizer)
return self.FTPbuffer
def closeConnection(self):
try:
self.ftpconn.quit()
except (EOFError,AttributeError,self.socket.timeout,self.ftplib.error_reply,):
# AttributeError is raised when socket gets trashed
# EOFError is raised when the connection breaks
# timeout, who cares!
pass
class rssFeed:
import entropyTools
def __init__(self, filename, title, description, maxentries = 100):
self.feed_title = title
self.feed_title = self.feed_title.strip()
self.feed_description = description
self.feed_language = "en-EN"
self.feed_editor = etpConst['rss-managing-editor']
self.feed_copyright = "%s - (C) %s" % (
etpConst['systemname'],
self.entropyTools.getYear(),
)
self.file = filename
self.items = {}
self.itemscounter = 0
self.maxentries = maxentries
from xml.dom import minidom
self.minidom = minidom
# sanity check
broken = False
if os.path.isfile(self.file):
try:
self.xmldoc = self.minidom.parse(self.file)
except:
broken = True
if not os.path.isfile(self.file) or broken:
self.title = self.feed_title
self.description = self.feed_description
self.language = self.feed_language
self.cright = self.feed_copyright
self.editor = self.feed_editor
self.link = etpConst['rss-website-url']
f = open(self.file,"w")
f.write('')
f.close()
else:
self.rssdoc = self.xmldoc.getElementsByTagName("rss")[0]
self.channel = self.rssdoc.getElementsByTagName("channel")[0]
self.title = self.channel.getElementsByTagName("title")[0].firstChild.data.strip()
self.link = self.channel.getElementsByTagName("link")[0].firstChild.data.strip()
self.description = self.channel.getElementsByTagName("description")[0].firstChild.data.strip()
try:
self.language = self.channel.getElementsByTagName("language")[0].firstChild.data.strip()
except IndexError:
self.language = 'en'
try:
self.cright = self.channel.getElementsByTagName("copyright")[0].firstChild.data.strip()
except IndexError:
self.cright = ''
try:
self.editor = self.channel.getElementsByTagName("managingEditor")[0].firstChild.data.strip()
except IndexError:
self.editor = ''
entries = self.channel.getElementsByTagName("item")
self.itemscounter = len(entries)
if self.itemscounter > self.maxentries:
self.itemscounter = self.maxentries
mycounter = self.itemscounter
for item in entries:
if mycounter == 0: # max entries reached
break
mycounter -= 1
self.items[mycounter] = {}
self.items[mycounter]['title'] = item.getElementsByTagName("title")[0].firstChild.data.strip()
description = item.getElementsByTagName("description")[0].firstChild
if description:
self.items[mycounter]['description'] = description.data.strip()
else:
self.items[mycounter]['description'] = ""
link = item.getElementsByTagName("link")[0].firstChild
if link:
self.items[mycounter]['link'] = link.data.strip()
else:
self.items[mycounter]['link'] = ""
self.items[mycounter]['guid'] = item.getElementsByTagName("guid")[0].firstChild.data.strip()
self.items[mycounter]['pubDate'] = item.getElementsByTagName("pubDate")[0].firstChild.data.strip()
dcs = item.getElementsByTagName("dc:creator")
if dcs:
self.items[mycounter]['dc:creator'] = dcs[0].firstChild.data.strip()
def addItem(self, title, link = '', description = '', pubDate = ''):
self.itemscounter += 1
self.items[self.itemscounter] = {}
self.items[self.itemscounter]['title'] = title
if pubDate:
self.items[self.itemscounter]['pubDate'] = pubDate
else:
self.items[self.itemscounter]['pubDate'] = time.strftime("%a, %d %b %Y %X +0000")
self.items[self.itemscounter]['description'] = description
self.items[self.itemscounter]['link'] = link
if link:
self.items[self.itemscounter]['guid'] = link
else:
myguid = etpConst['systemname'].lower()
myguid = myguid.replace(" ","")
self.items[self.itemscounter]['guid'] = myguid+"~"+description+str(self.itemscounter)
return self.itemscounter
def removeEntry(self, id):
if id in self.items:
del self.items[id]
self.itemscounter -= 1
return self.itemscounter
def getEntries(self):
return self.items, self.itemscounter
def writeChanges(self, reverse = True):
# filter entries to fit in maxentries
if self.itemscounter > self.maxentries:
tobefiltered = self.itemscounter - self.maxentries
for index in range(tobefiltered):
try:
del self.items[index]
except KeyError:
pass
doc = self.minidom.Document()
rss = doc.createElement("rss")
rss.setAttribute("version","2.0")
rss.setAttribute("xmlns:atom","http://www.w3.org/2005/Atom")
channel = doc.createElement("channel")
# title
title = doc.createElement("title")
title_text = doc.createTextNode(unicode(self.title))
title.appendChild(title_text)
channel.appendChild(title)
# link
link = doc.createElement("link")
link_text = doc.createTextNode(unicode(self.link))
link.appendChild(link_text)
channel.appendChild(link)
# description
description = doc.createElement("description")
desc_text = doc.createTextNode(unicode(self.description))
description.appendChild(desc_text)
channel.appendChild(description)
# language
language = doc.createElement("language")
lang_text = doc.createTextNode(unicode(self.language))
language.appendChild(lang_text)
channel.appendChild(language)
# copyright
cright = doc.createElement("copyright")
cr_text = doc.createTextNode(unicode(self.cright))
cright.appendChild(cr_text)
channel.appendChild(cright)
# managingEditor
managingEditor = doc.createElement("managingEditor")
ed_text = doc.createTextNode(unicode(self.editor))
managingEditor.appendChild(ed_text)
channel.appendChild(managingEditor)
keys = self.items.keys()
if reverse: keys.reverse()
for key in keys:
# sanity check, you never know
if not self.items.has_key(key):
self.removeEntry(key)
continue
k_error = False
for item in ['title','link','guid','description','pubDate']:
if not self.items[key].has_key(item):
k_error = True
break
if k_error:
self.removeEntry(key)
continue
# item
item = doc.createElement("item")
# title
item_title = doc.createElement("title")
item_title_text = doc.createTextNode(unicode(self.items[key]['title']))
item_title.appendChild(item_title_text)
item.appendChild(item_title)
# link
item_link = doc.createElement("link")
item_link_text = doc.createTextNode(unicode(self.items[key]['link']))
item_link.appendChild(item_link_text)
item.appendChild(item_link)
# guid
item_guid = doc.createElement("guid")
item_guid.setAttribute("isPermaLink","true")
item_guid_text = doc.createTextNode(unicode(self.items[key]['guid']))
item_guid.appendChild(item_guid_text)
item.appendChild(item_guid)
# description
item_desc = doc.createElement("description")
item_desc_text = doc.createTextNode(unicode(self.items[key]['description']))
item_desc.appendChild(item_desc_text)
item.appendChild(item_desc)
# pubdate
item_date = doc.createElement("pubDate")
item_date_text = doc.createTextNode(unicode(self.items[key]['pubDate']))
item_date.appendChild(item_date_text)
item.appendChild(item_date)
# add item to channel
channel.appendChild(item)
# add channel to rss
rss.appendChild(channel)
doc.appendChild(rss)
f = open(self.file,"w")
f.writelines(doc.toprettyxml(indent=" ").encode('utf-8'))
f.flush()
f.close()
class TriggerInterface:
import entropyTools
def __init__(self, EquoInstance, phase, pkgdata, package_action = None):
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid Entropy Instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = EquoInstance
self.clientLog = self.Entropy.clientLog
self.validPhases = ("preinstall","postinstall","preremove","postremove")
self.pkgdata = pkgdata
self.prepared = False
self.triggers = set()
self.gentoo_compat = etpConst['gentoo-compat']
self.package_action = package_action
'''
@ description: Gentoo toolchain variables
'''
self.MODULEDB_DIR="/var/lib/module-rebuild/"
self.INITSERVICES_DIR="/var/lib/init.d/"
''' portage stuff '''
if self.gentoo_compat:
try:
Spm = self.Entropy.Spm()
self.Spm = Spm
except Exception, e:
self.entropyTools.printTraceback()
mytxt = darkred("%s, %s: %s, %s !") % (
_("Portage interface can't be loaded"),
_("Error"),
e,
_("please fix"),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = bold(" !!! ")
)
self.gentoo_compat = False
self.phase = phase
# validate phase
self.phaseValidation()
def phaseValidation(self):
if self.phase not in self.validPhases:
mytxt = "%s: %s" % (_("Valid phases"),self.validPhases,)
raise exceptionTools.InvalidData("InvalidData: %s" % (mytxt,))
def prepare(self):
self.triggers = eval("self."+self.phase)()
remove = set()
for trigger in self.triggers:
if trigger in etpUi[self.phase+'_triggers_disable']:
remove.add(trigger)
self.triggers = [x for x in self.triggers if x not in remove]
del remove
self.prepared = True
def run(self):
for trigger in self.triggers:
eval("self.trigger_"+trigger)()
def kill(self):
self.prepared = False
del self.triggers[:]
def postinstall(self):
functions = []
# Gentoo hook
if self.gentoo_compat:
functions.append('ebuild_postinstall')
# equo purge cache
if self.pkgdata['category']+"/"+self.pkgdata['name'] == "sys-apps/entropy":
functions.append("purgecache")
# binutils configuration
if self.pkgdata['category']+"/"+self.pkgdata['name'] == "sys-devel/binutils":
functions.append("binutilsswitch")
# opengl configuration
if (self.pkgdata['category'] == "x11-drivers") and (self.pkgdata['name'].startswith("nvidia-") or self.pkgdata['name'].startswith("ati-")):
if "ebuild_postinstall" in functions:
functions.remove("ebuild_postinstall") # disabling gentoo postinstall since we reimplemented it
functions.append("openglsetup")
# load linker paths
ldpaths = self.Entropy.entropyTools.collectLinkerPaths()
for x in self.pkgdata['content']:
if (x.startswith("/etc/conf.d") or x.startswith("/etc/init.d")) and ("conftouch" not in functions):
functions.append('conftouch')
if x.startswith('/lib/modules/') and ("kernelmod" not in functions):
if "ebuild_postinstall" in functions:
# disabling gentoo postinstall since we reimplemented it
functions.remove("ebuild_postinstall")
functions.append('kernelmod')
if x.startswith('/boot/kernel-') and ("addbootablekernel" not in functions):
functions.append('addbootablekernel')
if x.startswith('/usr/src/') and ("createkernelsym" not in functions):
functions.append('createkernelsym')
if x.startswith('/etc/env.d/') and ("env_update" not in functions):
functions.append('env_update')
if (os.path.dirname(x) in ldpaths) and ("run_ldconfig" not in functions):
if x.find(".so") > -1:
functions.append('run_ldconfig')
if self.pkgdata['trigger']:
functions.append('call_ext_postinstall')
del ldpaths
return functions
def preinstall(self):
functions = []
# Gentoo hook
if self.gentoo_compat:
functions.append('ebuild_preinstall')
for x in self.pkgdata['content']:
if x.startswith("/etc/init.d/") and ("initinform" not in functions):
functions.append('initinform')
if x.startswith("/boot") and ("mountboot" not in functions):
functions.append('mountboot')
if self.pkgdata['trigger']:
functions.append('call_ext_preinstall')
return functions
def postremove(self):
functions = []
# load linker paths
ldpaths = self.Entropy.entropyTools.collectLinkerPaths()
for x in self.pkgdata['removecontent']:
if x.startswith('/boot/kernel-') and ("removebootablekernel" not in functions):
functions.append('removebootablekernel')
if x.startswith('/etc/init.d/') and ("initdisable" not in functions):
functions.append('initdisable')
if x.endswith('.py') and ("cleanpy" not in functions):
functions.append('cleanpy')
if x.startswith('/etc/env.d/') and ("env_update" not in functions):
functions.append('env_update')
if (os.path.dirname(x) in ldpaths) and ("run_ldconfig" not in functions):
if x.find(".so") > -1:
functions.append('run_ldconfig')
if self.pkgdata['trigger']:
functions.append('call_ext_postremove')
del ldpaths
return functions
def preremove(self):
functions = []
# Gentoo hook
if self.gentoo_compat:
functions.append('ebuild_preremove')
functions.append('ebuild_postremove')
# doing here because we need /var/db/pkg stuff in place and also because doesn't make any difference
# opengl configuration
if (self.pkgdata['category'] == "x11-drivers") and (self.pkgdata['name'].startswith("nvidia-") or self.pkgdata['name'].startswith("ati-")):
if "ebuild_preremove" in functions:
functions.remove("ebuild_preremove")
if "ebuild_postremove" in functions:
# disabling gentoo postinstall since we reimplemented it
functions.remove("ebuild_postremove")
if self.package_action not in ["remove_conflict"]:
functions.append("openglsetup_xorg")
for x in self.pkgdata['removecontent']:
if x.startswith("/boot"):
functions.append('mountboot')
break
if self.pkgdata['trigger']:
functions.append('call_ext_preremove')
return functions
'''
Real triggers
'''
def trigger_call_ext_preinstall(self):
return self.trigger_call_ext_generic()
def trigger_call_ext_postinstall(self):
return self.trigger_call_ext_generic()
def trigger_call_ext_preremove(self):
return self.trigger_call_ext_generic()
def trigger_call_ext_postremove(self):
return self.trigger_call_ext_generic()
def trigger_call_ext_generic(self):
try:
return self.do_trigger_call_ext_generic()
except Exception, e:
mykey = self.pkgdata['category']+"/"+self.pkgdata['name']
self.entropyTools.printTraceback()
self.entropyTools.printTraceback(f = self.Entropy.clientLog)
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] ATTENTION Cannot run External trigger for "+mykey+"!! "+str(Exception)+": "+str(e)
)
mytxt = "%s: %s %s. %s." % (
bold(_("QA")),
brown(_("Cannot run External trigger for")),
bold(mykey),
brown(_("Please report it")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return 0
def do_trigger_call_ext_generic(self):
# if mute, supress portage output
if etpUi['mute']:
oldsystderr = sys.stderr
oldsysstdout = sys.stdout
stdfile = open("/dev/null","w")
sys.stdout = stdfile
sys.stderr = stdfile
triggerfile = etpConst['entropyunpackdir']+"/trigger-"+str(self.Entropy.entropyTools.getRandomNumber())
while os.path.isfile(triggerfile):
triggerfile = etpConst['entropyunpackdir']+"/trigger-"+str(self.Entropy.entropyTools.getRandomNumber())
triggerdir = os.path.dirname(triggerfile)
if not os.path.isdir(triggerdir):
os.makedirs(triggerdir)
f = open(triggerfile,"w")
for x in self.pkgdata['trigger']:
f.write(x)
f.close()
# if mute, restore old stdout/stderr
if etpUi['mute']:
sys.stderr = oldsystderr
sys.stdout = oldsysstdout
stdfile.close()
stage = self.phase
pkgdata = self.pkgdata
# since I am sick of seeing pychecker reporting this
# let me do a nasty thing
x = type(stage),type(pkgdata)
del x
my_ext_status = 0
execfile(triggerfile)
os.remove(triggerfile)
return my_ext_status
def trigger_purgecache(self):
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Purging Entropy cache..."
)
mytxt = "%s: %s." % (_("Please remember"),_("It is always better to leave Entropy updates isolated"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
mytxt = "%s ..." % (_("Purging Entropy cache"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
self.Entropy.purge_cache(False)
def trigger_conftouch(self):
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Updating {conf.d,init.d} mtime..."
)
mytxt = "%s ..." % (_("Updating {conf.d,init.d} mtime"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
for item in self.pkgdata['content']:
if not (item.startswith("/etc/conf.d") or item.startswith("/etc/conf.d")):
continue
if not os.path.isfile(item):
continue
if not os.access(item,os.W_OK):
continue
try:
f = open(item,"abw")
f.flush()
f.close()
except (OSError,IOError,):
pass
def trigger_binutilsswitch(self):
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Configuring Binutils Profile..."
)
mytxt = "%s ..." % (_("Configuring Binutils Profile"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
# get binutils profile
pkgsplit = self.Entropy.entropyTools.catpkgsplit(
self.pkgdata['category'] + "/" + self.pkgdata['name'] + "-" + self.pkgdata['version']
)
profile = self.pkgdata['chost']+"-"+pkgsplit[2]
self.trigger_set_binutils_profile(profile)
def trigger_kernelmod(self):
if self.pkgdata['category'] != "sys-kernel":
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Updating moduledb..."
)
mytxt = "%s ..." % (_("Updating moduledb"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
item = 'a:1:'+self.pkgdata['category']+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']
self.trigger_update_moduledb(item)
mytxt = "%s ..." % (_("Running depmod"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
# get kernel modules dir name
name = ''
for item in self.pkgdata['content']:
item = etpConst['systemroot']+item
if item.startswith(etpConst['systemroot']+"/lib/modules/"):
name = item[len(etpConst['systemroot']):]
name = name.split("/")[3]
break
if name:
self.trigger_run_depmod(name)
def trigger_initdisable(self):
for item in self.pkgdata['removecontent']:
item = etpConst['systemroot']+item
if item.startswith(etpConst['systemroot']+"/etc/init.d/") and os.path.isfile(item):
myroot = "/"
if etpConst['systemroot']:
myroot = etpConst['systemroot']+"/"
runlevels_dir = etpConst['systemroot']+"/etc/runlevels"
runlevels = []
if os.path.isdir(runlevels_dir) and os.access(runlevels_dir,os.R_OK):
runlevels = [x for x in os.listdir(runlevels_dir) \
if os.path.isdir(os.path.join(runlevels_dir,x)) \
and os.path.isfile(os.path.join(runlevels_dir,x,os.path.basename(item)))
]
for runlevel in runlevels:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Removing boot service: %s, runlevel: %s" % (os.path.basename(item),runlevel,)
)
mytxt = "%s: %s : %s" % (brown(_("Removing boot service")),os.path.basename(item),runlevel,)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
cmd = 'ROOT="%s" rc-update del %s %s' % (myroot, os.path.basename(item), runlevel)
subprocess.call(cmd, shell = True)
def trigger_initinform(self):
for item in self.pkgdata['content']:
item = etpConst['systemroot']+item
if item.startswith(etpConst['systemroot']+"/etc/init.d/") and not os.path.isfile(etpConst['systemroot']+item):
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] A new service will be installed: %s" % (item,)
)
mytxt = "%s: %s" % (brown(_("A new service will be installed")),item,)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
def trigger_openglsetup(self):
opengl = "xorg-x11"
if self.pkgdata['name'] == "nvidia-drivers":
opengl = "nvidia"
elif self.pkgdata['name'] == "ati-drivers":
opengl = "ati"
# is there eselect ?
eselect = subprocess.call("eselect opengl &> /dev/null", shell = True)
if eselect == 0:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Reconfiguring OpenGL to %s ..." % (opengl,)
)
mytxt = "%s ..." % (brown(_("Reconfiguring OpenGL")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
quietstring = ''
if etpUi['quiet']: quietstring = " &>/dev/null"
if etpConst['systemroot']:
subprocess.call('echo "eselect opengl set --use-old %s" | chroot %s %s' % (opengl,etpConst['systemroot'],quietstring,), shell = True)
else:
subprocess.call('eselect opengl set --use-old %s %s' % (opengl,quietstring,), shell = True)
else:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Eselect NOT found, cannot run OpenGL trigger"
)
mytxt = "%s !" % (brown(_("Eselect NOT found, cannot run OpenGL trigger")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ##")
)
def trigger_openglsetup_xorg(self):
eselect = subprocess.call("eselect opengl &> /dev/null", shell = True)
if eselect == 0:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Reconfiguring OpenGL to fallback xorg-x11 ..."
)
mytxt = "%s ..." % (brown(_("Reconfiguring OpenGL")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
quietstring = ''
if etpUi['quiet']: quietstring = " &>/dev/null"
if etpConst['systemroot']:
subprocess.call('echo "eselect opengl set xorg-x11" | chroot %s %s' % (etpConst['systemroot'],quietstring,), shell = True)
else:
subprocess.call('eselect opengl set xorg-x11 %s' % (quietstring,), shell = True)
else:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Eselect NOT found, cannot run OpenGL trigger"
)
mytxt = "%s !" % (brown(_("Eselect NOT found, cannot run OpenGL trigger")),)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ##")
)
# FIXME: this only supports grub (no lilo support)
def trigger_addbootablekernel(self):
boot_mount = False
if os.path.ismount("/boot"):
boot_mount = True
kernels = [x for x in self.pkgdata['content'] if x.startswith("/boot/kernel-")]
if boot_mount:
kernels = [x[len("/boot"):] for x in kernels]
for kernel in kernels:
mykernel = kernel.split('/kernel-')[1]
initramfs = "/boot/initramfs-"+mykernel
if initramfs not in self.pkgdata['content']:
initramfs = ''
elif boot_mount:
initramfs = initramfs[len("/boot"):]
# configure GRUB
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Configuring GRUB bootloader. Adding the new kernel..."
)
mytxt = "%s. %s ..." % (
_("Configuring GRUB bootloader"),
_("Adding the new kernel"),
)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
self.trigger_configure_boot_grub(kernel,initramfs)
# FIXME: this only supports grub (no lilo support)
def trigger_removebootablekernel(self):
kernels = [x for x in self.pkgdata['content'] if x.startswith("/boot/kernel-")]
for kernel in kernels:
initramfs = "/boot/initramfs-"+kernel[13:]
if initramfs not in self.pkgdata['content']:
initramfs = ''
# configure GRUB
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Configuring GRUB bootloader. Removing the selected kernel..."
)
mytxt = "%s. %s ..." % (
_("Configuring GRUB bootloader"),
_("Removing the selected kernel"),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
self.trigger_remove_boot_grub(kernel,initramfs)
def trigger_mountboot(self):
# is in fstab?
if etpConst['systemroot']:
return
if os.path.isfile("/etc/fstab"):
f = open("/etc/fstab","r")
fstab = f.readlines()
fstab = self.Entropy.entropyTools.listToUtf8(fstab)
f.close()
for line in fstab:
fsline = line.split()
if len(fsline) > 1:
if fsline[1] == "/boot":
if not os.path.ismount("/boot"):
# trigger mount /boot
rc = subprocess.call("mount /boot &> /dev/null", shell = True)
if rc == 0:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] Mounted /boot successfully"
)
self.Entropy.updateProgress(
brown(_("Mounted /boot successfully")),
importance = 0,
header = red(" ## ")
)
elif rc != 8192: # already mounted
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] Cannot mount /boot automatically !!"
)
self.Entropy.updateProgress(
brown(_("Cannot mount /boot automatically !!")),
importance = 0,
header = red(" ## ")
)
break
def trigger_cleanpy(self):
pyfiles = [x for x in self.pkgdata['content'] if x.endswith(".py")]
for item in pyfiles:
item = etpConst['systemroot']+item
if os.path.isfile(item+"o"):
try: os.remove(item+"o")
except OSError: pass
if os.path.isfile(item+"c"):
try: os.remove(item+"c")
except OSError: pass
def trigger_createkernelsym(self):
for item in self.pkgdata['content']:
item = etpConst['systemroot']+item
if item.startswith(etpConst['systemroot']+"/usr/src/"):
# extract directory
try:
todir = item[len(etpConst['systemroot']):]
todir = todir.split("/")[3]
except:
continue
if os.path.isdir(etpConst['systemroot']+"/usr/src/"+todir):
# link to /usr/src/linux
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Creating kernel symlink "+etpConst['systemroot']+"/usr/src/linux for /usr/src/"+todir
)
mytxt = "%s %s %s %s" % (
_("Creating kernel symlink"),
etpConst['systemroot']+"/usr/src/linux",
_("for"),
"/usr/src/"+todir,
)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
if os.path.isfile(etpConst['systemroot']+"/usr/src/linux") or \
os.path.islink(etpConst['systemroot']+"/usr/src/linux"):
os.remove(etpConst['systemroot']+"/usr/src/linux")
if os.path.isdir(etpConst['systemroot']+"/usr/src/linux"):
mydir = etpConst['systemroot']+"/usr/src/linux."+str(self.Entropy.entropyTools.getRandomNumber())
while os.path.isdir(mydir):
mydir = etpConst['systemroot']+"/usr/src/linux."+str(self.Entropy.entropyTools.getRandomNumber())
shutil.move(etpConst['systemroot']+"/usr/src/linux",mydir)
try:
os.symlink(todir,etpConst['systemroot']+"/usr/src/linux")
except OSError: # not important in the end
pass
break
def trigger_run_ldconfig(self):
if not etpConst['systemroot']:
myroot = "/"
else:
myroot = etpConst['systemroot']+"/"
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Running ldconfig"
)
mytxt = "%s %s" % (_("Regenerating"),"/etc/ld.so.cache",)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
subprocess.call("ldconfig -r %s &> /dev/null" % (myroot,), shell = True)
def trigger_env_update(self):
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Running env-update"
)
if os.access(etpConst['systemroot']+"/usr/sbin/env-update",os.X_OK):
mytxt = "%s ..." % (_("Updating environment"),)
self.Entropy.updateProgress(
brown(mytxt),
importance = 0,
header = red(" ## ")
)
if etpConst['systemroot']:
subprocess.call("echo 'env-update --no-ldconfig' | chroot %s &> /dev/null" % (etpConst['systemroot'],), shell = True)
else:
subprocess.call('env-update --no-ldconfig &> /dev/null', shell = True)
def trigger_ebuild_postinstall(self):
stdfile = open("/dev/null","w")
oldstderr = sys.stderr
oldstdout = sys.stdout
sys.stderr = stdfile
myebuild = [self.pkgdata['xpakdir']+"/"+x for x in os.listdir(self.pkgdata['xpakdir']) if x.endswith(".ebuild")]
if myebuild:
myebuild = myebuild[0]
portage_atom = self.pkgdata['category']+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']
self.Entropy.updateProgress(
brown("Ebuild: pkg_postinst()"),
importance = 0,
header = red(" ## ")
)
try:
if not os.path.isfile(self.pkgdata['unpackdir']+"/portage/"+portage_atom+"/temp/environment"):
# if environment is not yet created, we need to run pkg_setup()
sys.stdout = stdfile
rc = self.Spm.spm_doebuild(
myebuild,
mydo = "setup",
tree = "bintree",
cpv = portage_atom,
portage_tmpdir = self.pkgdata['unpackdir'],
licenses = self.pkgdata['accept_license']
)
if rc == 1:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] ATTENTION Cannot properly run Gentoo postinstall (pkg_setup())"
" trigger for "+str(portage_atom)+". Something bad happened."
)
sys.stdout = oldstdout
rc = self.Spm.spm_doebuild(
myebuild,
mydo = "postinst",
tree = "bintree",
cpv = portage_atom,
portage_tmpdir = self.pkgdata['unpackdir'],
licenses = self.pkgdata['accept_license']
)
if rc == 1:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] ATTENTION Cannot properly run Gentoo postinstall (pkg_postinst()) trigger for " + \
str(portage_atom) + ". Something bad happened."
)
except Exception, e:
sys.stdout = oldstdout
self.entropyTools.printTraceback()
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] ATTENTION Cannot run Portage trigger for "+portage_atom+"!! "+str(Exception)+": "+str(e)
)
mytxt = "%s: %s %s. %s. %s: %s" % (
bold(_("QA")),
brown(_("Cannot run Portage trigger for")),
bold(str(portage_atom)),
brown(_("Please report it")),
bold(_("Attach this")),
darkred(etpConst['spmlogfile']),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
sys.stderr = oldstderr
sys.stdout = oldstdout
stdfile.close()
return 0
def trigger_ebuild_preinstall(self):
stdfile = open("/dev/null","w")
oldstderr = sys.stderr
oldstdout = sys.stdout
sys.stderr = stdfile
myebuild = [self.pkgdata['xpakdir']+"/"+x for x in os.listdir(self.pkgdata['xpakdir']) if x.endswith(".ebuild")]
if myebuild:
myebuild = myebuild[0]
portage_atom = self.pkgdata['category']+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']
self.Entropy.updateProgress(
brown(" Ebuild: pkg_preinst()"),
importance = 0,
header = red(" ##")
)
try:
sys.stdout = stdfile
rc = self.Spm.spm_doebuild(
myebuild,
mydo = "setup",
tree = "bintree",
cpv = portage_atom,
portage_tmpdir = self.pkgdata['unpackdir'],
licenses = self.pkgdata['accept_license']
) # create mysettings["T"]+"/environment"
if rc == 1:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot properly run Portage preinstall (pkg_setup()) trigger for " + \
str(portage_atom) + ". Something bad happened."
)
sys.stdout = oldstdout
rc = self.Spm.spm_doebuild(
myebuild,
mydo = "preinst",
tree = "bintree",
cpv = portage_atom,
portage_tmpdir = self.pkgdata['unpackdir'],
licenses = self.pkgdata['accept_license']
)
if rc == 1:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot properly run Gentoo preinstall (pkg_preinst()) trigger for " + \
str(portage_atom)+". Something bad happened."
)
except Exception, e:
sys.stdout = oldstdout
self.entropyTools.printTraceback()
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot run Gentoo preinst trigger for "+portage_atom+"!! "+str(Exception)+": "+str(e)
)
mytxt = "%s: %s %s. %s. %s: %s" % (
bold(_("QA")),
brown(_("Cannot run Portage trigger for")),
bold(str(portage_atom)),
brown(_("Please report it")),
bold(_("Attach this")),
darkred(etpConst['spmlogfile']),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
sys.stderr = oldstderr
sys.stdout = oldstdout
stdfile.close()
return 0
def trigger_ebuild_preremove(self):
stdfile = open("/dev/null","w")
oldstderr = sys.stderr
sys.stderr = stdfile
portage_atom = self.pkgdata['category']+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']
try:
myebuild = self.Spm.get_vdb_path()+portage_atom+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']+".ebuild"
except:
myebuild = ''
self.myebuild_moved = None
if os.path.isfile(myebuild):
try:
myebuild = self._setup_remove_ebuild_environment(myebuild, portage_atom)
except EOFError, e:
sys.stderr = oldstderr
stdfile.close()
# stuff on system is broken, ignore it
self.Entropy.updateProgress(
darkred("!!! Ebuild: pkg_prerm() failed, EOFError: ")+str(e)+darkred(" - ignoring"),
importance = 1,
type = "warning",
header = red(" ## ")
)
return 0
except ImportError, e:
sys.stderr = oldstderr
stdfile.close()
# stuff on system is broken, ignore it
self.Entropy.updateProgress(
darkred("!!! Ebuild: pkg_prerm() failed, ImportError: ")+str(e)+darkred(" - ignoring"),
importance = 1,
type = "warning",
header = red(" ## ")
)
return 0
if os.path.isfile(myebuild):
self.Entropy.updateProgress(
brown(" Ebuild: pkg_prerm()"),
importance = 0,
header = red(" ##")
)
try:
rc = self.Spm.spm_doebuild(
myebuild,
mydo = "prerm",
tree = "bintree",
cpv = portage_atom,
portage_tmpdir = etpConst['entropyunpackdir'] + "/" + portage_atom
)
if rc == 1:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot properly run Portage trigger for " + \
str(portage_atom)+". Something bad happened."
)
except Exception, e:
sys.stderr = oldstderr
stdfile.close()
self.entropyTools.printTraceback()
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot run Portage preremove trigger for "+portage_atom+"!! "+str(Exception)+": "+str(e)
)
mytxt = "%s: %s %s. %s. %s: %s" % (
bold(_("QA")),
brown(_("Cannot run Portage trigger for")),
bold(str(portage_atom)),
brown(_("Please report it")),
bold(_("Attach this")),
darkred(etpConst['spmlogfile']),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return 0
sys.stderr = oldstderr
stdfile.close()
self._remove_overlayed_ebuild()
return 0
def trigger_ebuild_postremove(self):
stdfile = open("/dev/null","w")
oldstderr = sys.stderr
sys.stderr = stdfile
portage_atom = self.pkgdata['category']+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']
try:
myebuild = self.Spm.get_vdb_path()+portage_atom+"/"+self.pkgdata['name']+"-"+self.pkgdata['version']+".ebuild"
except:
myebuild = ''
self.myebuild_moved = None
if os.path.isfile(myebuild):
try:
myebuild = self._setup_remove_ebuild_environment(myebuild, portage_atom)
except EOFError, e:
sys.stderr = oldstderr
stdfile.close()
# stuff on system is broken, ignore it
self.Entropy.updateProgress(
darkred("!!! Ebuild: pkg_postrm() failed, EOFError: ")+str(e)+darkred(" - ignoring"),
importance = 1,
type = "warning",
header = red(" ## ")
)
return 0
except ImportError, e:
sys.stderr = oldstderr
stdfile.close()
# stuff on system is broken, ignore it
self.Entropy.updateProgress(
darkred("!!! Ebuild: pkg_postrm() failed, ImportError: ")+str(e)+darkred(" - ignoring"),
importance = 1,
type = "warning",
header = red(" ## ")
)
return 0
if os.path.isfile(myebuild):
self.Entropy.updateProgress(
brown(" Ebuild: pkg_postrm()"),
importance = 0,
header = red(" ##")
)
try:
rc = self.Spm.spm_doebuild(
myebuild,
mydo = "postrm",
tree = "bintree",
cpv = portage_atom,
portage_tmpdir = etpConst['entropyunpackdir']+"/"+portage_atom
)
if rc == 1:
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot properly run Gentoo postremove trigger for " + \
str(portage_atom)+". Something bad happened."
)
except Exception, e:
sys.stderr = oldstderr
stdfile.close()
self.entropyTools.printTraceback()
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[PRE] ATTENTION Cannot run Gentoo postremove trigger for " + \
portage_atom+"!! "+str(Exception)+": "+str(e)
)
mytxt = "%s: %s %s. %s. %s: %s" % (
bold(_("QA")),
brown(_("Cannot run Portage trigger for")),
bold(str(portage_atom)),
brown(_("Please report it")),
bold(_("Attach this")),
darkred(etpConst['spmlogfile']),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return 0
sys.stderr = oldstderr
stdfile.close()
self._remove_overlayed_ebuild()
return 0
def _setup_remove_ebuild_environment(self, myebuild, portage_atom):
ebuild_dir = os.path.dirname(myebuild)
ebuild_file = os.path.basename(myebuild)
# copy the whole directory in a safe place
dest_dir = os.path.join(etpConst['entropyunpackdir'],"vardb/"+portage_atom)
if os.path.exists(dest_dir):
if os.path.isdir(dest_dir):
shutil.rmtree(dest_dir,True)
elif os.path.isfile(dest_dir) or os.path.islink(dest_dir):
os.remove(dest_dir)
os.makedirs(dest_dir)
items = os.listdir(ebuild_dir)
for item in items:
myfrom = os.path.join(ebuild_dir,item)
myto = os.path.join(dest_dir,item)
shutil.copy2(myfrom,myto)
newmyebuild = os.path.join(dest_dir,ebuild_file)
if os.path.isfile(newmyebuild):
myebuild = newmyebuild
self.myebuild_moved = myebuild
self._ebuild_env_setup_hook(myebuild)
return myebuild
def _ebuild_env_setup_hook(self, myebuild):
ebuild_path = os.path.dirname(myebuild)
if not etpConst['systemroot']:
myroot = "/"
else:
myroot = etpConst['systemroot']+"/"
# we need to fix ROOT= if it's set inside environment
bz2envfile = os.path.join(ebuild_path,"environment.bz2")
if os.path.isfile(bz2envfile) and os.path.isdir(myroot):
import bz2
envfile = self.Entropy.entropyTools.unpackBzip2(bz2envfile)
bzf = bz2.BZ2File(bz2envfile,"w")
f = open(envfile,"r")
line = f.readline()
while line:
if line.startswith("ROOT="):
line = "ROOT=%s\n" % (myroot,)
bzf.write(line)
line = f.readline()
f.close()
bzf.close()
os.remove(envfile)
def _remove_overlayed_ebuild(self):
if not self.myebuild_moved:
return
if os.path.isfile(self.myebuild_moved):
mydir = os.path.dirname(self.myebuild_moved)
shutil.rmtree(mydir,True)
mydir = os.path.dirname(mydir)
content = os.listdir(mydir)
while not content:
os.rmdir(mydir)
mydir = os.path.dirname(mydir)
content = os.listdir(mydir)
'''
Internal ones
'''
'''
@description: set chosen gcc profile
@output: returns int() as exit status
'''
def trigger_set_gcc_profile(self, profile):
if os.access(etpConst['systemroot']+'/usr/bin/gcc-config',os.X_OK):
redirect = ""
if etpUi['quiet']:
redirect = " &> /dev/null"
if etpConst['systemroot']:
subprocess.call("echo '/usr/bin/gcc-config %s' | chroot %s %s" % (profile,etpConst['systemroot'],redirect,), shell = True)
else:
subprocess.call('/usr/bin/gcc-config %s %s' % (profile,redirect,), shell = True)
return 0
'''
@description: set chosen binutils profile
@output: returns int() as exit status
'''
def trigger_set_binutils_profile(self, profile):
if os.access(etpConst['systemroot']+'/usr/bin/binutils-config',os.X_OK):
redirect = ""
if etpUi['quiet']:
redirect = " &> /dev/null"
if etpConst['systemroot']:
subprocess.call("echo '/usr/bin/binutils-config %s' | chroot %s %s" % (profile,etpConst['systemroot'],redirect,), shell = True)
else:
subprocess.call('/usr/bin/binutils-config %s %s' % (profile,redirect,), shell = True)
return 0
'''
@description: updates moduledb
@output: returns int() as exit status
'''
def trigger_update_moduledb(self, item):
if os.access(etpConst['systemroot']+'/usr/sbin/module-rebuild',os.X_OK):
if os.path.isfile(etpConst['systemroot']+self.MODULEDB_DIR+'moduledb'):
f = open(etpConst['systemroot']+self.MODULEDB_DIR+'moduledb',"r")
moduledb = f.readlines()
moduledb = self.Entropy.entropyTools.listToUtf8(moduledb)
f.close()
avail = [x for x in moduledb if x.strip() == item]
if (not avail):
f = open(etpConst['systemroot']+self.MODULEDB_DIR+'moduledb',"aw")
f.write(item+"\n")
f.flush()
f.close()
return 0
'''
@description: insert kernel object into kernel modules db
@output: returns int() as exit status
'''
def trigger_run_depmod(self, name):
if os.access('/sbin/depmod',os.X_OK):
if not etpConst['systemroot']:
myroot = "/"
else:
myroot = etpConst['systemroot']+"/"
subprocess.call('/sbin/depmod -a -b %s -r %s &> /dev/null' % (myroot,name,), shell = True)
return 0
def __get_entropy_kernel_grub_line(self, kernel):
return "title="+etpConst['systemname']+" ("+os.path.basename(kernel)+")\n"
'''
@description: append kernel entry to grub.conf
@output: returns int() as exit status
'''
def trigger_configure_boot_grub(self, kernel,initramfs):
if not os.path.isdir(etpConst['systemroot']+"/boot/grub"):
os.makedirs(etpConst['systemroot']+"/boot/grub")
if os.path.isfile(etpConst['systemroot']+"/boot/grub/grub.conf"):
# open in append
grub = open(etpConst['systemroot']+"/boot/grub/grub.conf","aw")
shutil.copy2(etpConst['systemroot']+"/boot/grub/grub.conf",etpConst['systemroot']+"/boot/grub/grub.conf.old.add")
# get boot dev
boot_dev = self.trigger_get_grub_boot_dev()
# test if entry has been already added
grubtest = open(etpConst['systemroot']+"/boot/grub/grub.conf","r")
content = grubtest.readlines()
content = [unicode(x,'raw_unicode_escape') for x in content]
for line in content:
if line.find(self.__get_entropy_kernel_grub_line(kernel)) != -1:
grubtest.close()
return
# also check if we have the same kernel listed
if (line.find("kernel") != 1) and (line.find(os.path.basename(kernel)) != -1) and not line.strip().startswith("#"):
grubtest.close()
return
else:
# create
boot_dev = "(hd0,0)"
grub = open(etpConst['systemroot']+"/boot/grub/grub.conf","w")
# write header - guess (hd0,0)... since it is weird having a running system without a bootloader, at least, grub.
grub_header = '''
default=0
timeout=10
'''
grub.write(grub_header)
cmdline = ' '
if os.path.isfile("/proc/cmdline"):
f = open("/proc/cmdline","r")
cmdline = " "+f.readline().strip()
params = cmdline.split()
if "dolvm" not in params: # support new kernels >= 2.6.23
cmdline += " dolvm "
f.close()
grub.write(self.__get_entropy_kernel_grub_line(kernel))
grub.write("\troot "+boot_dev+"\n")
grub.write("\tkernel "+kernel+cmdline+"\n")
if initramfs:
grub.write("\tinitrd "+initramfs+"\n")
grub.write("\n")
grub.flush()
grub.close()
def trigger_remove_boot_grub(self, kernel,initramfs):
if os.path.isdir(etpConst['systemroot']+"/boot/grub") and os.path.isfile(etpConst['systemroot']+"/boot/grub/grub.conf"):
shutil.copy2(etpConst['systemroot']+"/boot/grub/grub.conf",etpConst['systemroot']+"/boot/grub/grub.conf.old.remove")
f = open(etpConst['systemroot']+"/boot/grub/grub.conf","r")
grub_conf = f.readlines()
f.close()
content = [unicode(x,'raw_unicode_escape') for x in grub_conf]
try:
kernel, initramfs = (unicode(kernel,'raw_unicode_escape'),unicode(initramfs,'raw_unicode_escape'))
except TypeError:
pass
#kernelname = os.path.basename(kernel)
new_conf = []
skip = False
for line in content:
if (line.find(self.__get_entropy_kernel_grub_line(kernel)) != -1):
skip = True
continue
if line.strip().startswith("title"):
skip = False
if not skip or line.strip().startswith("#"):
new_conf.append(line)
f = open(etpConst['systemroot']+"/boot/grub/grub.conf","w")
for line in new_conf:
try:
f.write(line)
except UnicodeEncodeError:
f.write(line.encode('utf-8'))
f.flush()
f.close()
def trigger_get_grub_boot_dev(self):
if etpConst['systemroot']:
return "(hd0,0)"
import re
df_avail = subprocess.call("which df &> /dev/null", shell = True)
if df_avail != 0:
mytxt = "%s: %s! %s. %s (hd0,0)." % (
bold(_("QA")),
brown(_("Cannot find df")),
brown(_("Cannot properly configure the kernel")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
grub_avail = subprocess.call("which grub &> /dev/null", shell = True)
if grub_avail != 0:
mytxt = "%s: %s! %s. %s (hd0,0)." % (
bold(_("QA")),
brown(_("Cannot find grub")),
brown(_("Cannot properly configure the kernel")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
gboot = commands.getoutput("df /boot").split("\n")[-1].split()[0]
if gboot.startswith("/dev/"):
# it's ok - handle /dev/md
if gboot.startswith("/dev/md"):
md = os.path.basename(gboot)
if not md.startswith("md"):
md = "md"+md
f = open("/proc/mdstat","r")
mdstat = f.readlines()
mdstat = [x for x in mdstat if x.startswith(md)]
f.close()
if mdstat:
mdstat = mdstat[0].strip().split()
mddevs = []
for x in mdstat:
if x.startswith("sd"):
mddevs.append(x[:-3])
mddevs = sorted(mddevs)
if mddevs:
gboot = "/dev/"+mddevs[0]
else:
gboot = "/dev/sda1"
else:
gboot = "/dev/sda1"
# get disk
match = re.subn("[0-9]","",gboot)
gdisk = match[0]
if gdisk == '':
mytxt = "%s: %s %s %s. %s! %s (hd0,0)." % (
bold(_("QA")),
brown(_("cannot match device")),
brown(str(gboot)),
brown(_("with a grub one")), # 'cannot match device /dev/foo with a grub one'
brown(_("Cannot properly configure the kernel")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
match = re.subn("[a-z/]","",gboot)
try:
gpartnum = str(int(match[0])-1)
except ValueError:
mytxt = "%s: %s: %s. %s. %s (hd0,0)." % (
bold(_("QA")),
brown(_("grub translation not supported for")),
brown(str(gboot)),
brown(_("Cannot properly configure grub.conf")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
# now match with grub
device_map = etpConst['packagestmpdir']+"/grub.map"
if os.path.isfile(device_map):
os.remove(device_map)
# generate device.map
subprocess.call('echo "quit" | grub --device-map="%s" --no-floppy --batch &> /dev/null' % (device_map,), shell = True)
if os.path.isfile(device_map):
f = open(device_map,"r")
device_map_file = f.readlines()
f.close()
grub_dev = [x for x in device_map_file if (x.find(gdisk) != -1)]
if grub_dev:
grub_disk = grub_dev[0].strip().split()[0]
grub_dev = grub_disk[:-1]+","+gpartnum+")"
return grub_dev
else:
mytxt = "%s: %s. %s! %s (hd0,0)." % (
bold(_("QA")),
brown(_("cannot match grub device with a Linux one")),
brown(_("Cannot properly configure the kernel")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
else:
mytxt = "%s: %s. %s! %s (hd0,0)." % (
bold(_("QA")),
brown(_("cannot find generated device.map")),
brown(_("Cannot properly configure the kernel")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
else:
mytxt = "%s: %s. %s! %s (hd0,0)." % (
bold(_("QA")),
brown(_("cannot run df /boot")),
brown(_("Cannot properly configure the kernel")),
brown(_("Defaulting to")),
)
self.Entropy.updateProgress(
mytxt,
importance = 0,
header = red(" ## ")
)
return "(hd0,0)"
class SystemSettings:
import entropyTools
def __init__(self, EquoInstance):
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid Equo instance or subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = EquoInstance
self.etpSettingFiles = {
'keywords': etpConst['confpackagesdir']+"/package.keywords", # keywording configuration files
'unmask': etpConst['confpackagesdir']+"/package.unmask", # unmasking configuration files
'mask': etpConst['confpackagesdir']+"/package.mask", # masking configuration files
'license_mask': etpConst['confpackagesdir']+"/license.mask", # masking configuration files
'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",
}
## 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.etpSettingFiles['system_dirs'] = trunk_fsdirs_conf
if os.path.isfile(trunk_fsdirsmask_conf):
self.etpSettingFiles['system_dirs_mask'] = trunk_fsdirsmask_conf
self.etpMtimeFiles = {
'keywords_mtime': etpConst['dumpstoragedir']+"/keywords.mtime",
'unmask_mtime': etpConst['dumpstoragedir']+"/unmask.mtime",
'mask_mtime': etpConst['dumpstoragedir']+"/mask.mtime",
'license_mask_mtime': etpConst['dumpstoragedir']+"/license_mask.mtime",
'system_mask_mtime': etpConst['dumpstoragedir']+"/system_mask.mtime",
'repos_system_mask': {},
'repos_mask': {},
'repos_license_whitelist': {},
}
self.__settings = None
self.__persistent_settings = {
'live_packagemasking': {
'unmask_matches': set(),
'mask_matches': set(),
},
'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'),
9: _('repository in branch packages.db.mask'), # FIXME: this has been removed
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,
},
}
def clear(self):
self.__settings = None
def keys(self):
return self.__settings.keys()
def scan(self):
self.__settings = self.parse()
# merge universal keywords
for x in self.__settings['keywords']['universal']:
etpConst['keywords'].add(x)
# match installed packages of system_mask
self.__settings['repos_system_mask_installed'] = []
self.__settings['repos_system_mask_installed_keys'] = {}
if isinstance(self.Entropy.clientDbconn,EntropyDatabaseInterface):
while 1:
try:
self.Entropy.clientDbconn.validateDatabase()
except exceptionTools.SystemDatabaseError:
break
mc_cache = set()
m_list = self.__settings['repos_system_mask']+self.__settings['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 self.__settings['repos_system_mask_installed_keys']:
self.__settings['repos_system_mask_installed_keys'][mykey] = set()
for xm in m_ids:
if xm in mc_cache: continue
mc_cache.add(xm)
self.__settings['repos_system_mask_installed'].append(xm)
self.__settings['repos_system_mask_installed_keys'][mykey].add(xm)
break
# Live package masking
self.__settings.update(self.__persistent_settings)
def __setitem__(self, mykey, myvalue):
if self.__settings == None: self.scan()
if mykey in self.__persistent_settings: # backup here too
self.__persistent_settings[mykey] = myvalue
self.__settings.update(self.__persistent_settings)
self.__settings[mykey] = myvalue
def __getitem__(self, mykey):
if self.__settings == None: self.scan()
if mykey in self.__persistent_settings: # backup here too
return self.__persistent_settings[mykey]
return self.__settings[mykey]
def __contains__(self, mykey):
if self.__settings == None: self.scan()
here = mykey in self.__persistent_settings
if here: return here
return mykey in self.__settings
def __cmp__(self, other):
if self.__settings == None: self.scan()
return cmp(self.__settings,other)
def get(self, mykey):
if self.__settings == None: self.scan()
if mykey in self.__persistent_settings:
return self.__persistent_settings.get(mykey)
return self.__settings.get(mykey)
def has_key(self, mykey):
if self.__settings == None: self.scan()
here = mykey in self.__persistent_settings
if here: return True
return mykey in self.__settings
def parse(self):
# append repositories mask files
# append repositories mtime files
for repoid in etpRepositoriesOrder:
maskpath = os.path.join(etpRepositories[repoid]['dbpath'],etpConst['etpdatabasemaskfile'])
wlpath = os.path.join(etpRepositories[repoid]['dbpath'],etpConst['etpdatabaselicwhitelistfile'])
sm_path = os.path.join(etpRepositories[repoid]['dbpath'],etpConst['etpdatabasesytemmaskfile'])
ct_path = os.path.join(etpRepositories[repoid]['dbpath'],etpConst['etpdatabaseconflictingtaggedfile'])
if os.path.isfile(maskpath) and os.access(maskpath,os.R_OK):
self.etpSettingFiles['repos_mask'][repoid] = maskpath
self.etpMtimeFiles['repos_mask'][repoid] = etpConst['dumpstoragedir']+"/repo_"+repoid+"_"+etpConst['etpdatabasemaskfile']+".mtime"
if os.path.isfile(wlpath) and os.access(wlpath,os.R_OK):
self.etpSettingFiles['repos_license_whitelist'][repoid] = wlpath
self.etpMtimeFiles['repos_license_whitelist'][repoid] = etpConst['dumpstoragedir']+"/repo_"+repoid+"_"+etpConst['etpdatabaselicwhitelistfile']+".mtime"
if os.path.isfile(sm_path) and os.access(sm_path,os.R_OK):
self.etpSettingFiles['repos_system_mask'][repoid] = sm_path
self.etpMtimeFiles['repos_system_mask'][repoid] = etpConst['dumpstoragedir']+"/repo_"+repoid+"_"+etpConst['etpdatabasesytemmaskfile']+".mtime"
if os.path.isfile(ct_path) and os.access(ct_path,os.R_OK):
self.etpSettingFiles['conflicting_tagged_packages'][repoid] = ct_path
# user defined package sets
sets_dir = etpConst['confsetsdir']
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
self.etpSettingFiles['system_package_sets'][set_file] = os.path.join(sets_dir,set_file)
data = {}
for item in self.etpSettingFiles:
myattr = '%s_parser' % (item,)
if not hasattr(self,myattr): continue
f = getattr(self,myattr)
data[item] = f()
return data
'''
parser of package.keywords file
'''
def keywords_parser(self):
data = {
'universal': set(),
'packages': {},
'repositories': {},
}
self.__validateEntropyCache(self.etpSettingFiles['keywords'],self.etpMtimeFiles['keywords_mtime'])
content = [x.split() for x in self.__generic_parser(self.etpSettingFiles['keywords']) if len(x.split()) < 4]
for keywordinfo in content:
# skip wrong lines
if len(keywordinfo) > 3: continue
if len(keywordinfo) == 1: # inversal keywording, check if it's not repo=
# repo=?
if keywordinfo[0].startswith("repo="): continue
#kinfo = keywordinfo[0]
if keywordinfo[0] == "**": keywordinfo[0] = "" # convert into entropy format
data['universal'].add(keywordinfo[0])
continue # needed?
if len(keywordinfo) in (2,3): # inversal keywording, check if it's not repo=
# repo=?
if keywordinfo[0].startswith("repo="): continue
# add to repo?
items = keywordinfo[1:]
if keywordinfo[0] == "**": keywordinfo[0] = "" # convert into entropy format
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('*')
else:
if "*" 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])
else:
# it's going to be a faulty line!!??
if len(items) == 2: # can't have two items and no repo=
continue
# 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):
self.__validateEntropyCache(self.etpSettingFiles['unmask'],self.etpMtimeFiles['unmask_mtime'])
return self.__generic_parser(self.etpSettingFiles['unmask'])
def mask_parser(self):
self.__validateEntropyCache(self.etpSettingFiles['mask'],self.etpMtimeFiles['mask_mtime'])
return self.__generic_parser(self.etpSettingFiles['mask'])
def system_mask_parser(self):
self.__validateEntropyCache(self.etpSettingFiles['system_mask'],self.etpMtimeFiles['system_mask_mtime'])
return self.__generic_parser(self.etpSettingFiles['system_mask'])
def license_mask_parser(self):
self.__validateEntropyCache(self.etpSettingFiles['license_mask'],self.etpMtimeFiles['license_mask_mtime'])
return self.__generic_parser(self.etpSettingFiles['license_mask'])
def repos_license_whitelist_parser(self):
data = {}
for repoid in self.etpSettingFiles['repos_license_whitelist']:
self.__validateEntropyCache(self.etpSettingFiles['repos_license_whitelist'][repoid],self.etpMtimeFiles['repos_license_whitelist'][repoid], repoid = repoid)
data[repoid] = self.__generic_parser(self.etpSettingFiles['repos_license_whitelist'][repoid])
return data
def repos_mask_parser(self):
data = {}
for repoid in self.etpSettingFiles['repos_mask']:
self.__validateEntropyCache(self.etpSettingFiles['repos_mask'][repoid],self.etpMtimeFiles['repos_mask'][repoid], repoid = repoid)
data[repoid] = self.__generic_parser(self.etpSettingFiles['repos_mask'][repoid])
# why ? line = line.split()[0] in the previous one?
return data
def repos_system_mask_parser(self):
data = []
for repoid in self.etpSettingFiles['repos_system_mask']:
self.__validateEntropyCache(self.etpSettingFiles['repos_system_mask'][repoid],self.etpMtimeFiles['repos_system_mask'][repoid], repoid = repoid)
data += [x for x in self.__generic_parser(self.etpSettingFiles['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):
data = {}
for set_name in self.etpSettingFiles['system_package_sets']:
set_filepath = self.etpSettingFiles['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):
return self.__generic_parser(self.etpSettingFiles['system_dirs'])
def system_dirs_mask_parser(self):
return self.__generic_parser(self.etpSettingFiles['system_dirs_mask'])
def conflicting_tagged_packages_parser(self):
data = {}
# keep priority order
repoids = [x for x in etpRepositoriesOrder if x in self.etpSettingFiles['conflicting_tagged_packages']]
for repoid in repoids:
filepath = self.etpSettingFiles['conflicting_tagged_packages'].get(repoid)
if os.path.isfile(filepath) and os.access(filepath,os.R_OK):
f = open(filepath,"r")
content = f.readlines()
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
'''
internal functions
'''
def __generic_parser(self, filepath):
data = []
if os.path.isfile(filepath) and os.access(filepath,os.R_OK):
f = open(filepath,"r")
content = f.readlines()
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 __removeRepoCache(self, repoid = None):
if os.path.isdir(etpConst['dumpstoragedir']):
if repoid:
self.Entropy.repository_move_clear_cache(repoid)
return
for repoid in etpRepositoriesOrder:
self.Entropy.repository_move_clear_cache(repoid)
else:
os.makedirs(etpConst['dumpstoragedir'])
def __saveFileMtime(self,toread,tosaveinto):
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'])
f = open(tosaveinto,"w")
f.write(str(currmtime))
f.flush()
f.close()
os.chmod(tosaveinto,0664)
if etpConst['entropygid'] != None:
os.chown(tosaveinto,0,etpConst['entropygid'])
def __validateEntropyCache(self, maskfile, mtimefile, repoid = None):
if os.getuid() != 0: # can't validate if running as user, moreover users can't make changes, so...
return
# handle on-disk cache validation
# in this case, repositories cache
# if package.keywords is changed, we must destroy cache
if not os.path.isfile(mtimefile):
# we can't know if package.keywords has been updated
# remove repositories caches
self.__removeRepoCache(repoid = repoid)
self.__saveFileMtime(maskfile,mtimefile)
else:
# check mtime
try:
f = open(mtimefile,"r")
mtime = float(f.readline().strip())
f.close()
# compare with current mtime
try:
currmtime = os.path.getmtime(maskfile)
except OSError:
currmtime = 0.0
if mtime != currmtime:
self.__removeRepoCache(repoid = repoid)
self.__saveFileMtime(maskfile,mtimefile)
except:
self.__removeRepoCache(repoid = repoid)
self.__saveFileMtime(maskfile,mtimefile)
class Callable:
def __init__(self, anycallable):
self.__call__ = anycallable
class MultipartPostHandler(urllib2.BaseHandler):
handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
def http_request(self, request):
import urllib
doseq = 1
data = request.get_data()
if data is not None and type(data) != str:
v_files = []
v_vars = []
try:
for(key, value) in data.items():
if type(value) == file:
v_files.append((key, value))
else:
v_vars.append((key, value))
except TypeError:
systype, value, traceback = sys.exc_info()
raise TypeError, "not a valid non-string sequence or mapping object", traceback
if len(v_files) == 0:
data = urllib.urlencode(v_vars, doseq)
else:
boundary, data = self.multipart_encode(v_vars, v_files)
contenttype = 'multipart/form-data; boundary=%s' % boundary
'''
if (request.has_header('Content-Type')
and request.get_header('Content-Type').find('multipart/form-data') != 0):
print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
'''
request.add_unredirected_header('Content-Type', contenttype)
request.add_data(data)
return request
def multipart_encode(vars, files, boundary = None, buf = None):
from cStringIO import StringIO
import mimetools, mimetypes
if boundary is None:
boundary = mimetools.choose_boundary()
if buf is None:
buf = StringIO()
for(key, value) in vars:
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"' % key)
buf.write('\r\n\r\n' + value + '\r\n')
for(key, fd) in files:
file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
filename = fd.name.split('/')[-1]
contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
buf.write('Content-Type: %s\r\n' % contenttype)
# buffer += 'Content-Length: %s\r\n' % file_size
fd.seek(0)
buf.write('\r\n' + fd.read() + '\r\n')
buf.write('--' + boundary + '--\r\n\r\n')
buf = buf.getvalue()
return boundary, buf
multipart_encode = Callable(multipart_encode)
https_request = http_request
class ErrorReportInterface:
import entropyTools
def __init__(self, post_url = etpConst['handlers']['errorsend']):
self.url = post_url
self.opener = urllib2.build_opener(MultipartPostHandler)
self.generated = False
self.params = {}
mydict = {}
if etpConst['proxy']['ftp']:
mydict['ftp'] = etpConst['proxy']['ftp']
if etpConst['proxy']['http']:
mydict['http'] = etpConst['proxy']['http']
if mydict:
mydict['username'] = etpConst['proxy']['username']
mydict['password'] = etpConst['proxy']['password']
self.entropyTools.add_proxy_opener(urllib2,mydict)
else:
# unset
urllib2._opener = None
def prepare(self, tb_text, name, email, report_data = "", description = ""):
self.params['arch'] = etpConst['currentarch']
self.params['stacktrace'] = tb_text
self.params['name'] = name
self.params['email'] = email
self.params['version'] = etpConst['entropyversion']
self.params['errordata'] = report_data
self.params['description'] = description
self.params['arguments'] = ' '.join(sys.argv)
self.params['uid'] = etpConst['uid']
self.params['system_version'] = "N/A"
if os.access(etpConst['systemreleasefile'],os.R_OK):
f = open(etpConst['systemreleasefile'],"r")
self.params['system_version'] = f.readlines()
f.close()
self.params['processes'] = commands.getoutput('ps auxf')
self.params['lspci'] = commands.getoutput('/usr/sbin/lspci')
self.params['dmesg'] = commands.getoutput('dmesg')
self.params['locale'] = commands.getoutput('locale -v')
self.params['stacktrace'] += "\n\n"+self.params['locale'] # just for a while, won't hurt
self.generated = True
# params is a dict, key(HTTP post item name): value
def submit(self):
if self.generated:
result = self.opener.open(self.url, self.params).read()
if result.strip() == "1":
return True
return False
else:
mytxt = _("Not prepared yet")
raise exceptionTools.PermissionDenied("PermissionDenied: %s" % (mytxt,))
'''
~~ GIVES YOU WINGS ~~
'''
class SecurityInterface:
# thanks to Gentoo "gentoolkit" package, License below:
# This program is licensed under the GPL, version 2
# WARNING: this code is only tested by a few people and should NOT be used
# on production systems at this stage. There are possible security holes and probably
# bugs in this code. If you test it please report ANY success or failure to
# me (genone@gentoo.org).
# The following planned features are currently on hold:
# - getting GLSAs from http/ftp servers (not really useful without the fixed ebuilds)
# - GPG signing/verification (until key policy is clear)
def __init__(self, EquoInstance):
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid EquoInterface instance or subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = EquoInstance
self.lastfetch = None
self.previous_checksum = "0"
self.advisories_changed = None
self.adv_metadata = None
self.affected_atoms = None
from xml.dom import minidom
self.minidom = minidom
self.op_mappings = {
"le": "<=",
"lt": "<",
"eq": "=",
"gt": ">",
"ge": ">=",
"rge": ">=", # >=~
"rle": "<=", # <=~
"rgt": ">", # >~
"rlt": "<" # <~
}
self.unpackdir = os.path.join(etpConst['entropyunpackdir'],"security-"+str(self.Entropy.entropyTools.getRandomNumber()))
self.security_url = etpConst['securityurl']
self.unpacked_package = os.path.join(self.unpackdir,"glsa_package")
self.security_url_checksum = etpConst['securityurl']+etpConst['packageshashfileext']
self.download_package = os.path.join(self.unpackdir,os.path.basename(etpConst['securityurl']))
self.download_package_checksum = self.download_package+etpConst['packageshashfileext']
self.old_download_package_checksum = os.path.join(etpConst['dumpstoragedir'],os.path.basename(etpConst['securityurl']))+etpConst['packageshashfileext']
self.security_package = os.path.join(etpConst['securitydir'],os.path.basename(etpConst['securityurl']))
self.security_package_checksum = self.security_package+etpConst['packageshashfileext']
try:
if os.path.isfile(etpConst['securitydir']) or os.path.islink(etpConst['securitydir']):
os.remove(etpConst['securitydir'])
if not os.path.isdir(etpConst['securitydir']):
os.makedirs(etpConst['securitydir'],0775)
except OSError:
pass
const_setup_perms(etpConst['securitydir'],etpConst['entropygid'])
if os.path.isfile(self.old_download_package_checksum):
f = open(self.old_download_package_checksum)
try:
self.previous_checksum = f.readline().strip().split()[0]
except:
pass
f.close()
def __prepare_unpack(self):
if os.path.isfile(self.unpackdir) or os.path.islink(self.unpackdir):
os.remove(self.unpackdir)
if os.path.isdir(self.unpackdir):
shutil.rmtree(self.unpackdir,True)
try:
os.rmdir(self.unpackdir)
except OSError:
pass
os.makedirs(self.unpackdir,0775)
const_setup_perms(self.unpackdir,etpConst['entropygid'])
def __download_glsa_package(self):
return self.__generic_download(self.security_url, self.download_package)
def __download_glsa_package_checksum(self):
return self.__generic_download(self.security_url_checksum, self.download_package_checksum, showSpeed = False)
def __generic_download(self, url, save_to, showSpeed = True):
fetchConn = self.Entropy.urlFetcher(url, save_to, resume = False, showSpeed = showSpeed)
fetchConn.progress = self.Entropy.progress
rc = fetchConn.download()
del fetchConn
if rc in ("-1","-2","-3"):
return False
# setup permissions
self.Entropy.setup_default_file_perms(save_to)
return True
def __verify_checksum(self):
# read checksum
if not os.path.isfile(self.download_package_checksum) or not os.access(self.download_package_checksum,os.R_OK):
return 1
f = open(self.download_package_checksum)
try:
checksum = f.readline().strip().split()[0]
f.close()
except:
return 2
if checksum == self.previous_checksum:
self.advisories_changed = False
else:
self.advisories_changed = True
md5res = self.Entropy.entropyTools.compareMd5(self.download_package,checksum)
if not md5res:
return 3
return 0
def __unpack_advisories(self):
rc = self.Entropy.entropyTools.uncompressTarBz2(
self.download_package,
self.unpacked_package,
catchEmpty = True
)
const_setup_perms(self.unpacked_package,etpConst['entropygid'])
return rc
def __clear_previous_advisories(self):
if os.listdir(etpConst['securitydir']):
shutil.rmtree(etpConst['securitydir'],True)
if not os.path.isdir(etpConst['securitydir']):
os.makedirs(etpConst['securitydir'],0775)
const_setup_perms(self.unpackdir,etpConst['entropygid'])
def __put_advisories_in_place(self):
for advfile in os.listdir(self.unpacked_package):
from_file = os.path.join(self.unpacked_package,advfile)
to_file = os.path.join(etpConst['securitydir'],advfile)
shutil.move(from_file,to_file)
def __cleanup_garbage(self):
shutil.rmtree(self.unpackdir,True)
def clear(self, xcache = False):
self.adv_metadata = None
if xcache:
self.Entropy.clear_dump_cache(etpCache['advisories'])
def get_advisories_cache(self):
if self.adv_metadata != None:
return self.adv_metadata
if self.Entropy.xcache:
dir_checksum = self.Entropy.entropyTools.md5sum_directory(etpConst['securitydir'])
c_hash = "%s%s" % (etpCache['advisories'],hash("%s|%s|%s" % (hash(etpConst['branch']),hash(dir_checksum),hash(etpConst['systemroot']),)),)
adv_metadata = self.Entropy.Cacher.pop(c_hash)
if adv_metadata != None:
self.adv_metadata = adv_metadata.copy()
return self.adv_metadata
def set_advisories_cache(self, adv_metadata):
if self.Entropy.xcache:
dir_checksum = self.Entropy.entropyTools.md5sum_directory(etpConst['securitydir'])
c_hash = "%s%s" % (etpCache['advisories'],hash("%s|%s|%s" % (hash(etpConst['branch']),hash(dir_checksum),hash(etpConst['systemroot']),)),)
self.Entropy.Cacher.push(c_hash,adv_metadata.copy())
def get_advisories_list(self):
if not self.check_advisories_availability():
return []
xmls = os.listdir(etpConst['securitydir'])
xmls = sorted([x for x in xmls if x.endswith(".xml") and x.startswith("glsa-")])
return xmls
def get_advisories_metadata(self):
cached = self.get_advisories_cache()
if cached != None:
return cached
adv_metadata = {}
xmls = self.get_advisories_list()
maxlen = len(xmls)
count = 0
for xml in xmls:
count += 1
if not etpUi['quiet']: self.Entropy.updateProgress(":: "+str(round((float(count)/maxlen)*100,1))+"% ::", importance = 0, type = "info", back = True)
xml_metadata = None
exc_string = ""
exc_err = ""
try:
xml_metadata = self.get_xml_metadata(xml)
except KeyboardInterrupt:
return {}
except Exception, e:
exc_string = str(Exception)
exc_err = str(e)
if xml_metadata == None:
more_info = ""
if exc_string:
mytxt = _("Error")
more_info = " %s: %s: %s" % (mytxt,exc_string,exc_err,)
mytxt = "%s: %s: %s! %s" % (
blue(_("Warning")),
bold(xml),
blue(_("advisory broken")),
more_info,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = red(" !!! ")
)
continue
elif not xml_metadata:
continue
adv_metadata.update(xml_metadata)
adv_metadata = self.filter_advisories(adv_metadata)
self.set_advisories_cache(adv_metadata)
self.adv_metadata = adv_metadata.copy()
return adv_metadata
# this function filters advisories for packages that aren't
# in the repositories. Note: only keys will be matched
def filter_advisories(self, adv_metadata):
keys = adv_metadata.keys()
for key in keys:
valid = True
if adv_metadata[key]['affected']:
affected = adv_metadata[key]['affected']
affected_keys = affected.keys()
valid = False
skipping_keys = set()
for a_key in affected_keys:
match = self.Entropy.atomMatch(a_key)
if match[0] != -1:
# it's in the repos, it's valid
valid = True
else:
skipping_keys.add(a_key)
if not valid:
del adv_metadata[key]
for a_key in skipping_keys:
try:
del adv_metadata[key]['affected'][a_key]
except KeyError:
pass
try:
if not adv_metadata[key]['affected']:
del adv_metadata[key]
except KeyError:
pass
return adv_metadata
def is_affected(self, adv_key, adv_data = {}):
if not adv_data:
adv_data = self.get_advisories_metadata()
if adv_key not in adv_data:
return False
mydata = adv_data[adv_key].copy()
del adv_data
if not mydata['affected']:
return False
for key in mydata['affected']:
vul_atoms = mydata['affected'][key][0]['vul_atoms']
unaff_atoms = mydata['affected'][key][0]['unaff_atoms']
unaffected_atoms = set()
if not vul_atoms:
return False
# XXX: does multimatch work correctly?
for atom in unaff_atoms:
matches = self.Entropy.clientDbconn.atomMatch(atom, multiMatch = True)
if matches[1] == 0:
for idpackage in matches[0]:
unaffected_atoms.add((idpackage,0))
for atom in vul_atoms:
match = self.Entropy.clientDbconn.atomMatch(atom)
if (match[0] != -1) and (match not in unaffected_atoms):
if self.affected_atoms == None:
self.affected_atoms = set()
self.affected_atoms.add(atom)
return True
return False
def get_vulnerabilities(self):
return self.get_affection()
def get_fixed_vulnerabilities(self):
return self.get_affection(affected = False)
# if not affected: not affected packages will be returned
# if affected: affected packages will be returned
def get_affection(self, affected = True):
adv_data = self.get_advisories_metadata()
adv_data_keys = adv_data.keys()
valid_keys = set()
for adv in adv_data_keys:
is_affected = self.is_affected(adv,adv_data)
if affected == is_affected:
valid_keys.add(adv)
# we need to filter our adv_data and return
for key in adv_data_keys:
if key not in valid_keys:
try:
del adv_data[key]
except KeyError:
pass
# now we need to filter packages in adv_dat
for adv in adv_data:
for key in adv_data[adv]['affected'].keys():
atoms = adv_data[adv]['affected'][key][0]['vul_atoms']
applicable = True
for atom in atoms:
if atom in self.affected_atoms:
applicable = False
break
if applicable == affected:
del adv_data[adv]['affected'][key]
return adv_data
def get_affected_atoms(self):
adv_data = self.get_advisories_metadata()
adv_data_keys = adv_data.keys()
del adv_data
self.affected_atoms = set()
for key in adv_data_keys:
self.is_affected(key)
return self.affected_atoms
def get_xml_metadata(self, xmlfilename):
xml_data = {}
xmlfile = os.path.join(etpConst['securitydir'],xmlfilename)
try:
xmldoc = self.minidom.parse(xmlfile)
except:
return None
# get base data
glsa_tree = xmldoc.getElementsByTagName("glsa")[0]
glsa_product = glsa_tree.getElementsByTagName("product")[0]
if glsa_product.getAttribute("type") != "ebuild":
return {}
glsa_id = glsa_tree.getAttribute("id")
glsa_title = glsa_tree.getElementsByTagName("title")[0].firstChild.data
glsa_synopsis = glsa_tree.getElementsByTagName("synopsis")[0].firstChild.data
glsa_announced = glsa_tree.getElementsByTagName("announced")[0].firstChild.data
glsa_revised = glsa_tree.getElementsByTagName("revised")[0].firstChild.data
xml_data['filename'] = xmlfilename
xml_data['url'] = "http://www.gentoo.org/security/en/glsa/%s" % (xmlfilename,)
xml_data['title'] = glsa_title.strip()
xml_data['synopsis'] = glsa_synopsis.strip()
xml_data['announced'] = glsa_announced.strip()
xml_data['revised'] = glsa_revised.strip()
xml_data['bugs'] = ["https://bugs.gentoo.org/show_bug.cgi?id="+x.firstChild.data.strip() for x in glsa_tree.getElementsByTagName("bug")]
xml_data['access'] = ""
try:
xml_data['access'] = glsa_tree.getElementsByTagName("access")[0].firstChild.data.strip()
except IndexError:
pass
# references
references = glsa_tree.getElementsByTagName("references")[0]
xml_data['references'] = [x.getAttribute("link").strip() for x in references.getElementsByTagName("uri")]
try:
xml_data['description'] = ""
xml_data['description_items'] = []
desc = glsa_tree.getElementsByTagName("description")[0].getElementsByTagName("p")[0].firstChild.data.strip()
xml_data['description'] = desc
items = glsa_tree.getElementsByTagName("description")[0].getElementsByTagName("ul")
for item in items:
li_items = item.getElementsByTagName("li")
for li_item in li_items:
xml_data['description_items'].append(' '.join([x.strip() for x in li_item.firstChild.data.strip().split("\n")]))
except IndexError:
xml_data['description'] = ""
xml_data['description_items'] = []
try:
workaround = glsa_tree.getElementsByTagName("workaround")[0]
xml_data['workaround'] = workaround.getElementsByTagName("p")[0].firstChild.data.strip()
except IndexError:
xml_data['workaround'] = ""
try:
xml_data['resolution'] = []
resolution = glsa_tree.getElementsByTagName("resolution")[0]
p_elements = resolution.getElementsByTagName("p")
for p_elem in p_elements:
xml_data['resolution'].append(p_elem.firstChild.data.strip())
except IndexError:
xml_data['resolution'] = []
try:
impact = glsa_tree.getElementsByTagName("impact")[0]
xml_data['impact'] = impact.getElementsByTagName("p")[0].firstChild.data.strip()
except IndexError:
xml_data['impact'] = ""
xml_data['impacttype'] = glsa_tree.getElementsByTagName("impact")[0].getAttribute("type").strip()
try:
background = glsa_tree.getElementsByTagName("background")[0]
xml_data['background'] = background.getElementsByTagName("p")[0].firstChild.data.strip()
except IndexError:
xml_data['background'] = ""
# affection information
affected = glsa_tree.getElementsByTagName("affected")[0]
affected_packages = {}
# we will then filter affected_packages using repositories information
# if not affected_packages: advisory will be dropped
for p in affected.getElementsByTagName("package"):
name = p.getAttribute("name")
if not affected_packages.has_key(name):
affected_packages[name] = []
pdata = {}
pdata["arch"] = p.getAttribute("arch").strip()
pdata["auto"] = (p.getAttribute("auto") == "yes")
pdata["vul_vers"] = [self.__make_version(v) for v in p.getElementsByTagName("vulnerable")]
pdata["unaff_vers"] = [self.__make_version(v) for v in p.getElementsByTagName("unaffected")]
pdata["vul_atoms"] = [self.__make_atom(name, v) for v in p.getElementsByTagName("vulnerable")]
pdata["unaff_atoms"] = [self.__make_atom(name, v) for v in p.getElementsByTagName("unaffected")]
affected_packages[name].append(pdata)
xml_data['affected'] = affected_packages.copy()
return {glsa_id: xml_data}
def __make_version(self, vnode):
"""
creates from the information in the I{versionNode} a
version string (format <op><version>).
@type vnode: xml.dom.Node
@param vnode: a <vulnerable> or <unaffected> Node that
contains the version information for this atom
@rtype: String
@return: the version string
"""
return self.op_mappings[vnode.getAttribute("range")] + vnode.firstChild.data.strip()
def __make_atom(self, pkgname, vnode):
"""
creates from the given package name and information in the
I{versionNode} a (syntactical) valid portage atom.
@type pkgname: String
@param pkgname: the name of the package for this atom
@type vnode: xml.dom.Node
@param vnode: a <vulnerable> or <unaffected> Node that
contains the version information for this atom
@rtype: String
@return: the portage atom
"""
return str(self.op_mappings[vnode.getAttribute("range")] + pkgname + "-" + vnode.firstChild.data.strip())
def check_advisories_availability(self):
if not os.path.lexists(etpConst['securitydir']):
return False
if not os.path.isdir(etpConst['securitydir']):
return False
else:
return True
return False
def fetch_advisories(self):
mytxt = "%s: %s" % (bold(_("Security Advisories")),blue(_("testing service connection")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "info",
header = red(" @@ "),
footer = red(" ...")
)
# Test network connectivity
conntest = self.Entropy.entropyTools.get_remote_data(etpConst['conntestlink'])
if not conntest:
mytxt = _("Cannot connect to %s") % (etpConst['conntestlink'],)
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: %s" % (mytxt,))
mytxt = "%s: %s %s" % (bold(_("Security Advisories")),blue(_("getting latest GLSAs")),red("..."),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "info",
header = red(" @@ ")
)
gave_up = self.Entropy.lock_check(self.Entropy._resources_run_check_lock)
if gave_up:
return 7
locked = self.Entropy.application_lock_check()
if locked:
self.Entropy._resources_run_remove_lock()
return 4
# lock
self.Entropy._resources_run_create_lock()
try:
rc = self.run_fetch()
except:
self.Entropy._resources_run_remove_lock()
raise
if rc != 0: return rc
self.Entropy._resources_run_remove_lock()
if self.advisories_changed:
advtext = "%s: %s" % (bold(_("Security Advisories")),darkgreen(_("updated successfully")),)
else:
advtext = "%s: %s" % (bold(_("Security Advisories")),darkgreen(_("already up to date")),)
self.Entropy.updateProgress(
advtext,
importance = 2,
type = "info",
header = red(" @@ ")
)
return 0
def run_fetch(self):
# prepare directories
self.__prepare_unpack()
# download package
status = self.__download_glsa_package()
self.lastfetch = status
if not status:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkred(_("unable to download the package, sorry")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "error",
header = red(" ## ")
)
self.Entropy._resources_run_remove_lock()
return 1
mytxt = "%s: %s %s" % (bold(_("Security Advisories")),blue(_("Verifying checksum")),red("..."),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" # "),
back = True
)
# download digest
status = self.__download_glsa_package_checksum()
if not status:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkred(_("cannot download the checksum, sorry")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "error",
header = red(" ## ")
)
self.Entropy._resources_run_remove_lock()
return 2
# verify digest
status = self.__verify_checksum()
if status == 1:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkred(_("cannot open packages, sorry")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "error",
header = red(" ## ")
)
self.Entropy._resources_run_remove_lock()
return 3
elif status == 2:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkred(_("cannot read the checksum, sorry")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "error",
header = red(" ## ")
)
self.Entropy._resources_run_remove_lock()
return 4
elif status == 3:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkred(_("digest verification failed, sorry")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "error",
header = red(" ## ")
)
self.Entropy._resources_run_remove_lock()
return 5
elif status == 0:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkgreen(_("verification Successful")),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" # ")
)
else:
mytxt = _("Return status not valid")
raise exceptionTools.InvalidData("InvalidData: %s." % (mytxt,))
# save downloaded md5
if os.path.isfile(self.download_package_checksum) and os.path.isdir(etpConst['dumpstoragedir']):
if os.path.isfile(self.old_download_package_checksum):
os.remove(self.old_download_package_checksum)
shutil.copy2(self.download_package_checksum,self.old_download_package_checksum)
self.Entropy.setup_default_file_perms(self.old_download_package_checksum)
# now unpack in place
status = self.__unpack_advisories()
if status != 0:
mytxt = "%s: %s." % (bold(_("Security Advisories")),darkred(_("digest verification failed, try again later")),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "error",
header = red(" ## ")
)
self.Entropy._resources_run_remove_lock()
return 6
mytxt = "%s: %s %s" % (bold(_("Security Advisories")),blue(_("installing")),red("..."),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" # ")
)
# clear previous
self.__clear_previous_advisories()
# copy over
self.__put_advisories_in_place()
# remove temp stuff
self.__cleanup_garbage()
return 0
class SpmInterface:
def __init__(self, OutputInterface):
if not isinstance(OutputInterface, (EquoInterface, TextInterface, ServerInterface)):
if OutputInterface == None:
OutputInterface = TextInterface()
else:
mytxt = _("A valid TextInterface based instance is needed")
raise exceptionTools.IncorrectParameter(
"IncorrectParameter: %s" % (mytxt,)
)
self.spm_backend = etpConst['spm']['backend']
self.valid_backends = etpConst['spm']['available_backends']
if self.spm_backend not in self.valid_backends:
mytxt = "%s: %s" % (_("Invalid backend"),self.spm_backend,)
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
if self.spm_backend == "portage":
self.intf = PortageInterface(OutputInterface)
@staticmethod
def get_spm_interface():
backend = etpConst['spm']['backend']
if backend == "portage":
return PortageInterface
class PortageInterface:
import entropyTools
class paren_normalize(list):
"""Take a dependency structure as returned by paren_reduce or use_reduce
and generate an equivalent structure that has no redundant lists."""
def __init__(self, src):
list.__init__(self)
self._zap_parens(src, self)
def _zap_parens(self, src, dest, disjunction=False):
if not src:
return dest
i = iter(src)
for x in i:
if isinstance(x, basestring):
if x == '||':
x = self._zap_parens(i.next(), [], disjunction=True)
if len(x) == 1:
dest.append(x[0])
else:
dest.append("||")
dest.append(x)
elif x.endswith("?"):
dest.append(x)
dest.append(self._zap_parens(i.next(), []))
else:
dest.append(x)
else:
if disjunction:
x = self._zap_parens(x, [])
if len(x) == 1:
dest.append(x[0])
else:
dest.append(x)
else:
self._zap_parens(x, dest)
return dest
def __init__(self, OutputInterface):
if not isinstance(OutputInterface, (EquoInterface, TextInterface, ServerInterface)):
mytxt = _("A valid TextInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
# interface only needed OutputInterface functions
self.updateProgress = OutputInterface.updateProgress
self.askQuestion = OutputInterface.askQuestion
sys.path.append("/usr/lib/gentoolkit/pym")
# importing portage stuff
import portage
self.portage = portage
self.EAPI = 1
try:
import portage.const as portage_const
except ImportError:
import portage_const
if hasattr(portage_const,"EAPI"):
self.EAPI = portage_const.EAPI
self.portage_const = portage_const
try:
from portage.versions import best
self.portage_best = best
except ImportError:
from portage_versions import best
self.portage_best = best
try:
import portage.util as portage_util
except ImportError:
import portage_util
self.portage_util = portage_util
try:
import portage.sets as portage_sets
self.portage_sets = portage_sets
except ImportError:
self.portage_sets = None
try:
import glsa
self.glsa = glsa
except ImportError:
self.glsa = None
if hasattr(self.portage,'exception'):
self.portage_exception = self.portage.exception
else: # portage <2.2 workaround
self.portage_exception = Exception
self.builtin_pkg_sets = [
"system","world","installed","module-rebuild",
"security","preserved-rebuild","live-rebuild",
"downgrade","unavailable"
]
def write_to_log(self, message):
spmLog = LogFile(
level = etpConst['spmloglevel'],
filename = etpConst['spmlogfile'],
header = "[spm]"
)
spmLog.write(message)
spmLog.flush()
spmLog.close()
def write_traceback_to_log(self):
spmLog = LogFile(
level = etpConst['spmloglevel'],
filename = etpConst['spmlogfile'],
header = "[spm]"
)
self.entropyTools.printTraceback(f = spmLog)
spmLog.flush()
spmLog.close()
def list_glsa_packages(self, command = "affected"):
if not self.glsa: return
if command not in ['new','all','affected']: return
glsaconfig = self.glsa.checkconfig(self.portage.config(clone=self.portage.settings))
completelist = self.glsa.get_glsa_list(glsaconfig["GLSA_DIR"], glsaconfig)
glsalist = []
if command == "new":
checklist = []
if os.access(glsaconfig["CHECKFILE"], os.R_OK):
checklist = [line.strip() for line in open(glsaconfig["CHECKFILE"], "r").readlines()]
glsalist = [e for e in completelist if e not in checklist]
elif command == "all":
glsalist = completelist
elif command == "affected":
# maybe this should be todolist instead
for x in completelist:
try:
myglsa = self.glsa.Glsa(x, glsaconfig)
except (self.glsa.GlsaTypeException, self.glsa.GlsaFormatException), e:
continue
if not myglsa.isVulnerable():
continue
glsalist.append(x)
return glsalist
def get_glsa_id_information(self, glsa_id):
if not self.glsa: return {}
glsaconfig = self.glsa.checkconfig(self.portage.config(clone=self.portage.settings))
try:
myglsa = self.glsa.Glsa(glsa_id, glsaconfig)
except (self.glsa.GlsaTypeException, self.glsa.GlsaFormatException):
return {}
mydict = {
'glsa_id': glsa_id,
'number': myglsa.nr,
'access': myglsa.access,
'title': myglsa.title,
'synopsis': myglsa.synopsis,
'announced': myglsa.announced,
'revised': myglsa.revised,
'bugs': myglsa.bugs,
'description': myglsa.description,
'resolution': myglsa.resolution,
'impact': myglsa.impact_text,
'impacttype': myglsa.impact_type,
'affected': myglsa.affected,
'background': myglsa.background,
'glsatype': myglsa.glsatype,
'packages': myglsa.packages,
'services': myglsa.services,
'product': myglsa.product,
'references': myglsa.references,
'workaround': myglsa.workaround,
}
if myglsa.isApplied():
status = "[A]"
elif myglsa.isVulnerable():
status = "[N]"
else:
status = "[U]"
mydict['status'] = status
return mydict.copy()
def run_fixpackages(self, myroot = None):
if myroot == None:
myroot = etpConst['systemroot']+"/"
mydb = {}
mydb[myroot] = {}
mydb[myroot]['vartree'] = self._get_portage_vartree(myroot)
mydb[myroot]['porttree'] = self._get_portage_portagetree(myroot)
mydb[myroot]['bintree'] = self._get_portage_binarytree(myroot)
mydb[myroot]['virtuals'] = self.portage.settings.getvirtuals(myroot)
if etpUi['mute']:
pid = os.fork()
if pid > 0:
os.waitpid(pid, 0)
else:
f = open("/dev/null","w")
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = f
sys.stderr = f
self.portage._global_updates(mydb, {})
sys.stdout = old_stdout
sys.stderr = old_stderr
f.close()
os._exit(0)
else:
self.portage._global_updates(mydb, {}) # always force
def get_world_file(self):
return os.path.join(etpConst['systemroot'],"/",self.portage_const.WORLD_FILE)
def get_third_party_mirrors(self, mirrorname):
x = []
if self.portage.thirdpartymirrors.has_key(mirrorname):
x = self.portage.thirdpartymirrors[mirrorname]
return x
def get_spm_setting(self, var):
return self.portage.settings[var]
def get_config_protect_and_mask(self):
config_protect = self.portage.settings['CONFIG_PROTECT']
config_protect = config_protect.split()
config_protect_mask = self.portage.settings['CONFIG_PROTECT_MASK']
config_protect_mask = config_protect_mask.split()
# explode
protect = []
for x in config_protect:
x = os.path.expandvars(x)
protect.append(x)
mask = []
for x in config_protect_mask:
x = os.path.expandvars(x)
mask.append(x)
return ' '.join(protect),' '.join(mask)
def _get_portage_vartree(self, root):
if not etpConst['spm']['cache'].has_key('portage'):
etpConst['spm']['cache']['portage'] = {}
if not etpConst['spm']['cache']['portage'].has_key('vartree'):
etpConst['spm']['cache']['portage']['vartree'] = {}
cached = etpConst['spm']['cache']['portage']['vartree'].get(root)
if cached != None:
return cached
try:
mytree = self.portage.vartree(root=root)
except Exception, e:
raise exceptionTools.SPMError("SPMError: %s: %s" % (Exception,e,))
etpConst['spm']['cache']['portage']['vartree'][root] = mytree
return mytree
def _get_portage_portagetree(self, root):
if not etpConst['spm']['cache'].has_key('portage'):
etpConst['spm']['cache']['portage'] = {}
if not etpConst['spm']['cache']['portage'].has_key('portagetree'):
etpConst['spm']['cache']['portage']['portagetree'] = {}
cached = etpConst['spm']['cache']['portage']['portagetree'].get(root)
if cached != None:
return cached
try:
mytree = self.portage.portagetree(root=root)
except Exception, e:
raise exceptionTools.SPMError("SPMError: %s: %s" % (Exception,e,))
etpConst['spm']['cache']['portage']['portagetree'][root] = mytree
return mytree
def _get_portage_binarytree(self, root):
if not etpConst['spm']['cache'].has_key('portage'):
etpConst['spm']['cache']['portage'] = {}
if not etpConst['spm']['cache']['portage'].has_key('binarytree'):
etpConst['spm']['cache']['portage']['binarytree'] = {}
cached = etpConst['spm']['cache']['portage']['binarytree'].get(root)
if cached != None:
return cached
pkgdir = root+self.portage.settings['PKGDIR']
try:
mytree = self.portage.binarytree(root,pkgdir)
except Exception, e:
raise exceptionTools.SPMError("SPMError: %s: %s" % (Exception,e,))
etpConst['spm']['cache']['portage']['binarytree'][root] = mytree
return mytree
def _get_portage_config(self, config_root, root, use_cache = True):
if use_cache:
if not etpConst['spm']['cache'].has_key('portage'):
etpConst['spm']['cache']['portage'] = {}
if not etpConst['spm']['cache']['portage'].has_key('config'):
etpConst['spm']['cache']['portage']['config'] = {}
cached = etpConst['spm']['cache']['portage']['config'].get((config_root,root))
if cached != None:
return cached
try:
mysettings = self.portage.config(config_root = config_root, target_root = root, config_incrementals = self.portage_const.INCREMENTALS)
except Exception, e:
raise exceptionTools.SPMError("SPMError: %s: %s" % (Exception,e,))
if use_cache:
etpConst['spm']['cache']['portage']['config'][(config_root,root)] = mysettings
return mysettings
# resolve atoms automagically (best, not current!)
# sys-libs/application --> sys-libs/application-1.2.3-r1
def get_best_atom(self, atom, match = "bestmatch-visible"):
try:
return self.portage.portdb.xmatch(match,str(atom))
except ValueError:
return None
# same as above but includes masked ebuilds
def get_best_masked_atom(self, atom):
atoms = self.portage.portdb.xmatch("match-all",str(atom))
return self.portage_best(atoms)
def get_category_description_data(self, category):
from xml.dom import minidom
data = {}
portdir = self.portage.settings['PORTDIR']
myfile = os.path.join(portdir,category,"metadata.xml")
if os.access(myfile,os.R_OK) and os.path.isfile(myfile):
doc = minidom.parse(myfile)
longdescs = doc.getElementsByTagName("longdescription")
for longdesc in longdescs:
data[longdesc.getAttribute("lang").strip()] = ' '.join([x.strip() for x in longdesc.firstChild.data.strip().split("\n")])
return data
def get_atom_category(self, atom):
try:
return self.portage.portdb.xmatch("match-all",str(atom))[0].split("/")[0]
except:
return None
# Packages in system (in the Portage language -> emerge system, remember?)
def get_atoms_in_system(self):
system = self.portage.settings.packages
sysoutput = []
for x in system:
y = self.get_installed_atoms(x)
if (y != None):
for z in y:
sysoutput.append(z)
sysoutput.extend(etpConst['spm']['system_packages']) # add our packages
return sysoutput
def get_installed_atom(self, atom):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
rc = mytree.dep_match(str(atom))
if rc: return rc[-1]
def get_package_description(self, atom):
if atom.startswith("="): atom = atom[1:]
return self.portage.portdb.aux_get(atom,['DESCRIPTION'])[0]
def get_installed_package_description(self, atom):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
if atom.startswith("="): atom = atom[1:]
rc = mytree.dbapi.aux_get(atom, ["DESCRIPTION"])[0]
if rc: return rc
def get_package_slot(self, atom):
if atom.startswith("="): atom = atom[1:]
return self.portage.portdb.aux_get(atom,['SLOT'])[0]
def get_installed_package_slot(self, atom):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
if atom.startswith("="): atom = atom[1:]
rc = mytree.getslot(atom)
if rc: return rc
def get_installed_atoms(self, atom):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
rc = mytree.dep_match(str(atom))
if rc: return rc
def search_keys(self, key):
key_split = key.split("/")
cat = key_split[0]
name = key_split[1]
cat_dir = os.path.join(self.get_vdb_path(),cat)
if not os.path.isdir(cat_dir):
return None
dir_content = [os.path.join(cat,x) for x in os.listdir(cat_dir) if x.startswith(name)]
if not dir_content:
return None
return dir_content
# create a .tbz2 file in the specified path
def quickpkg(self, atom, dirpath):
# getting package info
pkgname = atom.split("/")[1]
pkgcat = atom.split("/")[0]
#pkgfile = pkgname+".tbz2"
if not os.path.isdir(dirpath):
os.makedirs(dirpath)
dirpath += "/"+pkgname+etpConst['packagesext']
dbdir = self.get_vdb_path()+"/"+pkgcat+"/"+pkgname+"/"
import tarfile
import stat
trees = self.portage.db["/"]
vartree = trees["vartree"]
dblnk = self.portage.dblink(pkgcat, pkgname, "/", vartree.settings, treetype="vartree", vartree=vartree)
dblnk.lockdb()
tar = tarfile.open(dirpath,"w:bz2")
contents = dblnk.getcontents()
id_strings = {}
paths = sorted(contents.keys())
for path in paths:
try:
exist = os.lstat(path)
except OSError:
continue # skip file
ftype = contents[path][0]
lpath = path
arcname = path[1:]
if 'dir' == ftype and \
not stat.S_ISDIR(exist.st_mode) and \
os.path.isdir(lpath):
lpath = os.path.realpath(lpath)
tarinfo = tar.gettarinfo(lpath, arcname)
tarinfo.uname = id_strings.setdefault(tarinfo.uid, str(tarinfo.uid))
tarinfo.gname = id_strings.setdefault(tarinfo.gid, str(tarinfo.gid))
if stat.S_ISREG(exist.st_mode):
tarinfo.type = tarfile.REGTYPE
f = open(path)
try:
tar.addfile(tarinfo, f)
finally:
f.close()
else:
tar.addfile(tarinfo)
tar.close()
# appending xpak informations
import etpXpak
tbz2 = etpXpak.tbz2(dirpath)
tbz2.recompose(dbdir)
dblnk.unlockdb()
if os.path.isfile(dirpath):
return dirpath
else:
raise exceptionTools.FileNotFound("FileNotFound: Spm:quickpkg %s: %s %s" % (
_("error"),
dirpath,
_("not found"),
)
)
def get_package_use_file(self):
return os.path.join(self.portage_const.USER_CONFIG_PATH,'package.use')
def enable_package_useflags(self, atom, useflags):
result = self.unset_package_useflags(atom, useflags)
if not result: return False
return self._handle_new_useflags(atom, useflags, "")
def disable_package_useflags(self, atom, useflags):
result = self.unset_package_useflags(atom, useflags)
if not result: return False
return self._handle_new_useflags(atom, useflags, "-")
def _handle_new_useflags(self, atom, useflags, mark):
matched_atom = self.get_best_atom(atom)
if not matched_atom:
return False
use_file = self.get_package_use_file()
if not (os.path.isfile(use_file) and os.access(use_file,os.W_OK)):
return False
f = open(use_file,"r")
content = [x.strip() for x in f.readlines()]
f.close()
def handle_line(line, useflags):
data = line.split()
if len(data) < 2:
return False, line
myatom = data[0]
if matched_atom != self.get_best_atom(myatom):
return False, line
flags = data[1:]
base_flags = []
added_flags = []
for flag in flags:
myflag = flag
if myflag.startswith("+"):
myflag = myflag[1:]
elif myflag.startswith("-"):
myflag = myflag[1:]
if not myflag:
continue
base_flags.append(myflag)
for useflag in useflags:
if mark+useflag in base_flags:
continue
added_flags.append(mark+useflag)
new_line = "%s %s" % (myatom, ' '.join(flags+added_flags))
return True, new_line
atom_found = False
new_content = []
for line in content:
changed, elaborated_line = handle_line(line, useflags)
if changed: atom_found = True
new_content.append(elaborated_line)
if not atom_found:
myline = "%s %s" % (atom, ' '.join([mark+x for x in useflags]))
new_content.append(myline)
f = open(use_file+".tmp","w")
for line in new_content:
f.write(line+"\n")
f.flush()
f.close()
shutil.move(use_file+".tmp",use_file)
return True
def unset_package_useflags(self, atom, useflags):
matched_atom = self.get_best_atom(atom)
if not matched_atom:
return False
use_file = self.get_package_use_file()
if not (os.path.isfile(use_file) and os.access(use_file,os.W_OK)):
return False
f = open(use_file,"r")
content = [x.strip() for x in f.readlines()]
f.close()
new_content = []
for line in content:
data = line.split()
if len(data) < 2:
new_content.append(line)
continue
myatom = data[0]
if matched_atom != self.get_best_atom(myatom):
new_content.append(line)
continue
flags = data[1:]
new_flags = []
for flag in flags:
myflag = flag
if myflag.startswith("+"):
myflag = myflag[1:]
elif myflag.startswith("-"):
myflag = myflag[1:]
if myflag in useflags:
continue
elif not flag:
continue
new_flags.append(flag)
if new_flags:
new_line = "%s %s" % (myatom, ' '.join(new_flags))
new_content.append(new_line)
f = open(use_file+".tmp","w")
for line in new_content:
f.write(line+"\n")
f.flush()
f.close()
shutil.move(use_file+".tmp",use_file)
return True
def get_package_useflags(self, atom):
matched_atom = self.get_best_atom(atom)
if not matched_atom:
return {}
global_useflags = self.get_useflags()
use_force = self.get_useflags_force()
use_mask = self.get_useflags_mask()
package_use_useflags = self.get_package_use_useflags(atom)
data = {}
data['use_force'] = use_force.copy()
data['use_mask'] = use_mask.copy()
data['global_use'] = global_useflags.split()
iuse = self.get_package_setting(atom, "IUSE")
if not isinstance(iuse,basestring):
iuse = ''
data['iuse'] = iuse.split()[:]
iuse = set()
for myiuse in data['iuse']:
if myiuse.startswith("+"):
myiuse = myiuse[1:]
iuse.add(myiuse)
use = [f for f in data['global_use']+list(package_use_useflags['enabled']) if (f in iuse) and (f not in use_mask) and (f not in package_use_useflags['disabled'])]
use_disabled = [f for f in iuse if (f not in data['global_use']) and (f not in use_mask) and (f not in package_use_useflags['enabled'])]
data['use'] = use[:]
data['use_disabled'] = use_disabled[:]
matched_slot = self.get_package_slot(matched_atom)
try:
installed_atom = self.get_installed_atom("%s:%s" % (self.entropyTools.dep_getkey(atom),matched_slot,))
except self.portage_exception:
installed_atom = None
if installed_atom:
# get its useflags
previous_iuse = self.get_installed_package_setting(installed_atom, "IUSE").split()
previous_use = self.get_installed_package_setting(installed_atom, "USE").split()
new_previous_iuse = set()
for myuse in previous_iuse:
if myuse.startswith("+"):
myuse = myuse[1:]
new_previous_iuse.add(myuse)
previous_iuse = list(new_previous_iuse)
inst_use = [f for f in previous_iuse if (f in previous_use) and (f not in use_mask)]
#inst_use_disabled = [f for f in previous_use if (f not in previous_iuse) and (f not in use_mask)]
# check removed use
use_removed = []
for myuse in inst_use:
if myuse not in use:
use_removed.append(myuse)
# use not available
use_not_avail = []
for myuse in previous_iuse:
if (myuse not in iuse) and (myuse not in use_removed):
use_not_avail.append(myuse)
# check new use
t_use = []
for myuse in use:
if myuse not in inst_use:
myuse = "+%s*" % (myuse,)
t_use.append(myuse)
use = t_use
# check disabled use
t_use_disabled = []
for myuse in use_disabled:
if myuse in inst_use:
if myuse in use_removed+use_not_avail:
continue
myuse = "-%s*" % (myuse,)
else:
myuse = "-%s" % (myuse,)
t_use_disabled.append(myuse)
use_disabled = t_use_disabled
for myuse in use_removed:
use_disabled.append("(-%s*)" % (myuse,))
for myuse in use_not_avail:
use_disabled.append("(-%s)" % (myuse,))
else:
use_disabled = ["-"+x for x in use_disabled]
data['use_string'] = ' '.join(sorted(use)+sorted([x for x in use_disabled]))
data['use_string_colored'] = ' '.join(
sorted([darkred(x) for x in use if not x.startswith("+")] + \
[darkgreen(x) for x in use if x.startswith("+")]) + \
sorted([darkblue(x) for x in use_disabled if x.startswith("-")] + \
[brown(x) for x in use_disabled if x.startswith("(") and (x.find("*") == -1)] + \
[purple(x) for x in use_disabled if x.startswith("(") and (x.find("*") != -1)]
)
)
return data
def get_installed_package_useflags(self, atom):
matched_atom = self.get_installed_atom(atom)
if not matched_atom:
return {}
global_use = self.get_installed_package_setting(matched_atom, "USE")
use_mask = self.get_useflags_mask()
data = {}
data['use_mask'] = use_mask.copy()
data['global_use'] = global_use.split()
iuse = self.get_installed_package_setting(matched_atom, "IUSE")
if not isinstance(iuse,basestring): iuse = ''
data['iuse'] = iuse.split()[:]
iuse = set()
for myiuse in data['iuse']:
if myiuse.startswith("+"):
myiuse = myiuse[1:]
iuse.add(myiuse)
use = [f for f in data['global_use'] if (f in iuse) and (f not in use_mask)]
use_disabled = [f for f in iuse if (f not in data['global_use']) and (f not in use_mask)]
data['use'] = use[:]
data['use_disabled'] = use_disabled[:]
data['use_string'] = ' '.join(sorted(use)+sorted([x for x in use_disabled]))
data['use_string_colored'] = ' '.join(
sorted([darkred(x) for x in use if not x.startswith("+")] + \
[darkgreen(x) for x in use if x.startswith("+")]) + \
sorted([darkblue(x) for x in use_disabled if x.startswith("-")] + \
[brown(x) for x in use_disabled if x.startswith("(") and (x.find("*") == -1)] + \
[purple(x) for x in use_disabled if x.startswith("(") and (x.find("*") != -1)]
)
)
return data
# package.use
def get_package_use_useflags(self, atom):
data = {
'enabled': set(),
'disabled': set(),
}
matched_atom = self.get_best_atom(atom)
if not matched_atom:
return data
use_file = self.get_package_use_file()
if not (os.path.isfile(use_file) and os.access(use_file,os.W_OK)):
return data
use_data = self.portage_util.grabdict(use_file)
for myatom in use_data:
mymatch = self.get_best_atom(myatom)
if mymatch != matched_atom:
continue
for flag in use_data[myatom]:
if flag.startswith("-"):
myflag = flag[1:]
data['enabled'].discard(myflag)
data['disabled'].add(myflag)
else:
myflag = flag
if myflag.startswith("+"):
myflag = myflag[1:]
data['disabled'].discard(myflag)
data['enabled'].add(myflag)
return data
def get_useflags(self):
return self.portage.settings['USE']
def get_useflags_force(self):
return self.portage.settings.useforce
def get_useflags_mask(self):
return self.portage.settings.usemask
def get_installed_package_setting(self, atom, setting):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
if atom.startswith("="): atom = atom[1:]
return mytree.dbapi.aux_get(atom, [setting])[0]
def get_package_setting(self, atom, setting):
if atom.startswith("="): atom = atom[1:]
return self.portage.portdb.aux_get(atom,[setting])[0]
def query_files(self, atom):
mypath = etpConst['systemroot']+"/"
mysplit = atom.split("/")
content = self.portage.dblink(mysplit[0], mysplit[1], mypath, self.portage.settings).getcontents()
return content.keys()
def query_belongs(self, filename, like = False):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
packages = mytree.dbapi.cpv_all()
matches = set()
for package in packages:
mysplit = package.split("/")
content = self.portage.dblink(mysplit[0], mysplit[1], mypath, self.portage.settings).getcontents()
if not like:
if filename in content:
matches.add(package)
else:
for myfile in content:
if myfile.find(filename) != -1:
matches.add(package)
return matches
def query_belongs_multiple(self, filenames, like = False):
mypath = etpConst['systemroot']+"/"
mytree = self._get_portage_vartree(mypath)
packages = mytree.dbapi.cpv_all()
matches = {}
filenames = filenames.copy()
for package in packages:
cat, pkgv = package.split("/")
content = self.portage.dblink(cat, pkgv, mypath, self.portage.settings).getcontents()
if not like:
for filename in filenames:
if filename in content:
myslot = self.get_installed_package_slot(package)
if not matches.has_key((package,myslot)):
matches[(package,myslot)] = set()
matches[(package,myslot)].add(filename)
else:
for filename in filenames:
for myfile in content:
if myfile.find(filename) != -1:
myslot = self.get_installed_package_slot(package)
if not matches.has_key((package,myslot)):
matches[(package,myslot)] = set()
matches[(package,myslot)].add(filename)
return matches
def calculate_dependencies(self, my_iuse, my_use, my_license, my_depend, my_rdepend, my_pdepend, my_provide, my_src_uri):
metadata = {}
metadata['USE'] = my_use
metadata['IUSE'] = my_iuse
metadata['LICENSE'] = my_license
metadata['DEPEND'] = my_depend
metadata['PDEPEND'] = my_pdepend
metadata['RDEPEND'] = my_rdepend
metadata['PROVIDE'] = my_provide
metadata['SRC_URI'] = my_src_uri
use = metadata['USE'].split()
raw_use = use
metadata['USE_MASK'] = self.get_useflags_mask()
iuse = set()
for myiuse in metadata['IUSE'].split():
if myiuse.startswith("+"):
myiuse = myiuse[1:]
if (myiuse not in use) and ("-"+myiuse not in use):
use.append(myiuse)
elif myiuse.startswith("-"):
myiuse = myiuse[1:]
iuse.add(myiuse)
use = sorted([f for f in use if f in iuse])
metadata['USE'] = " ".join(use)
for k in "LICENSE", "RDEPEND", "DEPEND", "PDEPEND", "PROVIDE", "SRC_URI":
try:
deps = self.paren_reduce(metadata[k])
deps = self.use_reduce(deps, uselist=raw_use)
deps = self.paren_normalize(deps)
if k == "LICENSE":
deps = self.paren_license_choose(deps)
else:
deps = self.paren_choose(deps)
if k.endswith("DEPEND"):
deps = self.usedeps_reduce(deps)
deps = ' '.join(deps)
except Exception, e:
self.entropyTools.printTraceback()
self.updateProgress(
darkred("%s: %s: %s :: %s") % (
_("Error calculating dependencies"),
str(Exception),
k,
e,
),
importance = 1,
type = "error",
header = red(" !!! ")
)
deps = ''
continue
metadata[k] = deps
return metadata
def usedeps_reduce(self, dependencies):
newlist = []
for dependency in dependencies:
use_deps = self.entropyTools.dep_getusedeps(dependency)
if use_deps:
use_deps = [x for x in use_deps if (x[0] not in ("!",)) and (x[-1] not in ("=","?",))]
if use_deps:
dependency = "%s[%s]" % (self.entropyTools.remove_usedeps(dependency),','.join(use_deps),)
else:
dependency = self.entropyTools.remove_usedeps(dependency)
newlist.append(dependency)
return newlist
def paren_reduce(self, mystr,tokenize=1):
"""
# deps.py -- Portage dependency resolution functions
# Copyright 2003-2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: portage_dep.py 9174 2008-01-11 05:49:02Z zmedico $
Take a string and convert all paren enclosed entities into sublists, optionally
futher splitting the list elements by spaces.
Example usage:
>>> paren_reduce('foobar foo ( bar baz )',1)
['foobar', 'foo', ['bar', 'baz']]
>>> paren_reduce('foobar foo ( bar baz )',0)
['foobar foo ', [' bar baz ']]
@param mystr: The string to reduce
@type mystr: String
@param tokenize: Split on spaces to produces further list breakdown
@type tokenize: Integer
@rtype: Array
@return: The reduced string in an array
"""
mylist = []
while mystr:
left_paren = mystr.find("(")
has_left_paren = left_paren != -1
right_paren = mystr.find(")")
has_right_paren = right_paren != -1
if not has_left_paren and not has_right_paren:
freesec = mystr
subsec = None
tail = ""
elif mystr[0] == ")":
return [mylist,mystr[1:]]
elif has_left_paren and not has_right_paren:
raise exceptionTools.InvalidDependString(
"InvalidDependString: %s: '%s'" % (_("missing right parenthesis"),mystr,))
elif has_left_paren and left_paren < right_paren:
freesec,subsec = mystr.split("(",1)
subsec,tail = self.paren_reduce(subsec,tokenize)
else:
subsec,tail = mystr.split(")",1)
if tokenize:
subsec = self.strip_empty(subsec.split(" "))
return [mylist+subsec,tail]
return mylist+[subsec],tail
mystr = tail
if freesec:
if tokenize:
mylist = mylist + self.strip_empty(freesec.split(" "))
else:
mylist = mylist + [freesec]
if subsec is not None:
mylist = mylist + [subsec]
return mylist
def strip_empty(self, myarr):
"""
# deps.py -- Portage dependency resolution functions
# Copyright 2003-2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: portage_dep.py 9174 2008-01-11 05:49:02Z zmedico $
Strip all empty elements from an array
@param myarr: The list of elements
@type myarr: List
@rtype: Array
@return: The array with empty elements removed
"""
for x in range(len(myarr)-1, -1, -1):
if not myarr[x]:
del myarr[x]
return myarr
def use_reduce(self, deparray, uselist=[], masklist=[], matchall=0, excludeall=[]):
"""
# deps.py -- Portage dependency resolution functions
# Copyright 2003-2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: portage_dep.py 9174 2008-01-11 05:49:02Z zmedico $
Takes a paren_reduce'd array and reduces the use? conditionals out
leaving an array with subarrays
@param deparray: paren_reduce'd list of deps
@type deparray: List
@param uselist: List of use flags
@type uselist: List
@param masklist: List of masked flags
@type masklist: List
@param matchall: Resolve all conditional deps unconditionally. Used by repoman
@type matchall: Integer
@rtype: List
@return: The use reduced depend array
"""
# Quick validity checks
for x in range(len(deparray)):
if deparray[x] in ["||","&&"]:
if len(deparray) - 1 == x or not isinstance(deparray[x+1], list):
mytxt = _("missing atom list in")
raise exceptionTools.InvalidDependString(deparray[x]+" "+mytxt+" \""+str(deparray)+"\"")
if deparray and deparray[-1] and deparray[-1][-1] == "?":
mytxt = _("Conditional without target in")
raise exceptionTools.InvalidDependString("InvalidDependString: "+mytxt+" \""+str(deparray)+"\"")
# This is just for use by emerge so that it can enable a backward compatibility
# mode in order to gracefully deal with installed packages that have invalid
# atoms or dep syntax. For backward compatibility with api consumers, strict
# behavior will be explicitly enabled as necessary.
_dep_check_strict = False
mydeparray = deparray[:]
rlist = []
while mydeparray:
head = mydeparray.pop(0)
if isinstance(head,list):
additions = self.use_reduce(head, uselist, masklist, matchall, excludeall)
if additions:
rlist.append(additions)
elif rlist and rlist[-1] == "||":
#XXX: Currently some DEPEND strings have || lists without default atoms.
# raise portage_exception.InvalidDependString("No default atom(s) in \""+paren_enclose(deparray)+"\"")
rlist.append([])
else:
if head[-1] == "?": # Use reduce next group on fail.
# Pull any other use conditions and the following atom or list into a separate array
newdeparray = [head]
while isinstance(newdeparray[-1], str) and newdeparray[-1][-1] == "?":
if mydeparray:
newdeparray.append(mydeparray.pop(0))
else:
raise ValueError, _("Conditional with no target")
# Deprecation checks
warned = 0
if len(newdeparray[-1]) == 0:
mytxt = "%s. (%s)" % (_("Empty target in string"),_("Deprecated"),)
self.updateProgress(
darkred("PortageInterface.use_reduce(): %s" % (mytxt,)),
importance = 0,
type = "error",
header = bold(" !!! ")
)
warned = 1
if len(newdeparray) != 2:
mytxt = "%s. (%s)" % (_("Nested use flags without parenthesis"),_("Deprecated"),)
self.updateProgress(
darkred("PortageInterface.use_reduce(): %s" % (mytxt,)),
importance = 0,
type = "error",
header = bold(" !!! ")
)
warned = 1
if warned:
self.updateProgress(
darkred("PortageInterface.use_reduce(): "+" ".join(map(str,[head]+newdeparray))),
importance = 0,
type = "error",
header = bold(" !!! ")
)
# Check that each flag matches
ismatch = True
missing_flag = False
for head in newdeparray[:-1]:
head = head[:-1]
if not head:
missing_flag = True
break
if head.startswith("!"):
head_key = head[1:]
if not head_key:
missing_flag = True
break
if not matchall and head_key in uselist or \
head_key in excludeall:
ismatch = False
break
elif head not in masklist:
if not matchall and head not in uselist:
ismatch = False
break
else:
ismatch = False
if missing_flag:
mytxt = _("Conditional without flag")
raise exceptionTools.InvalidDependString(
"InvalidDependString: "+mytxt+": \"" + \
str([head+"?", newdeparray[-1]])+"\"")
# If they all match, process the target
if ismatch:
target = newdeparray[-1]
if isinstance(target, list):
additions = self.use_reduce(target, uselist, masklist, matchall, excludeall)
if additions:
rlist.append(additions)
elif not _dep_check_strict:
# The old deprecated behavior.
rlist.append(target)
else:
mytxt = _("Conditional without parenthesis")
raise exceptionTools.InvalidDependString(
"InvalidDependString: "+mytxt+": '%s?'" % head)
else:
rlist += [head]
return rlist
def paren_choose(self, dep_list):
newlist = []
do_skip = False
for idx in range(len(dep_list)):
if do_skip:
do_skip = False
continue
item = dep_list[idx]
if item == "||": # or
next_item = dep_list[idx+1]
if not next_item: # || ( asd? ( atom ) dsa? ( atom ) ) => [] if use asd and dsa are disabled
do_skip = True
continue
item = self.dep_or_select(next_item) # must be a list
if not item:
# no matches, transform to string and append, so reagent will fail
newlist.append(str(next_item))
else:
newlist += item
do_skip = True
elif isinstance(item, list): # and
item = self.dep_and_select(item)
newlist += item
else:
newlist.append(item)
return newlist
def dep_and_select(self, and_list):
do_skip = False
newlist = []
for idx in range(len(and_list)):
if do_skip:
do_skip = False
continue
x = and_list[idx]
if x == "||":
x = self.dep_or_select(and_list[idx+1])
do_skip = True
if not x:
x = str(and_list[idx+1])
else:
newlist += x
elif isinstance(x, list):
x = self.dep_and_select(x)
newlist += x
else:
newlist.append(x)
# now verify if all are satisfied
for x in newlist:
match = self.get_installed_atom(x)
if match == None:
return []
return newlist
def dep_or_select(self, or_list):
do_skip = False
for idx in range(len(or_list)):
if do_skip:
do_skip = False
continue
x = or_list[idx]
if x == "||": # or
x = self.dep_or_select(or_list[idx+1])
do_skip = True
elif isinstance(x, list): # and
x = self.dep_and_select(x)
if not x:
continue
# found
return x
else:
x = [x]
for y in x:
match = self.get_installed_atom(y)
if match != None:
return [y]
return []
def paren_license_choose(self, dep_list):
newlist = set()
for item in dep_list:
if isinstance(item, list):
# match the first
data = set(self.paren_license_choose(item))
newlist.update(data)
else:
if item not in ["||"]:
newlist.add(item)
return list(newlist)
def get_vdb_path(self):
rc = etpConst['systemroot']+"/"+self.portage_const.VDB_PATH
if (not rc.endswith("/")):
return rc+"/"
return rc
def get_available_packages(self, categories = [], filter_reinstalls = True):
mypath = etpConst['systemroot']+"/"
mysettings = self._get_portage_config("/",mypath)
portdb = self.portage.portdbapi(mysettings["PORTDIR"], mysettings = mysettings)
cps = portdb.cp_all()
visibles = set()
for cp in cps:
if categories and cp.split("/")[0] not in categories:
continue
# get slots
slots = set()
atoms = self.get_best_atom(cp, "match-visible")
if atoms:
for atom in atoms:
slots.add(portdb.aux_get(atom, ["SLOT"])[0])
for slot in slots:
visibles.add(cp+":"+slot)
del cps
# now match visibles
available = set()
for visible in visibles:
match = self.get_best_atom(visible)
if match == None:
continue
if filter_reinstalls:
installed = self.get_installed_atom(visible)
# if not installed, installed == None
if installed != match:
available.add(match)
else:
available.add(match)
del visibles
return available
# Collect installed packages
def get_installed_packages(self, dbdir = None, categories = []):
if not dbdir:
appDbDir = self.get_vdb_path()
else:
appDbDir = dbdir
dbDirs = os.listdir(appDbDir)
installedAtoms = set()
for pkgsdir in dbDirs:
if os.path.isdir(appDbDir+pkgsdir):
pkgdir = os.listdir(appDbDir+pkgsdir)
for pdir in pkgdir:
pkgcat = pkgsdir.split("/")[-1]
if categories and (pkgcat not in categories):
continue
pkgatom = pkgcat+"/"+pdir
if pkgatom.find("-MERGING-") == -1:
installedAtoms.add(pkgatom)
return sorted(list(installedAtoms)), len(installedAtoms)
def get_installed_packages_counter(self, dbdir = None):
if not dbdir:
appDbDir = self.get_vdb_path()
else:
appDbDir = dbdir
installedAtoms = set()
for current_dirpath, subdirs, files in os.walk(appDbDir):
pvs = os.listdir(current_dirpath)
for mypv in pvs:
if mypv.startswith("-MERGING-"):
continue
mypvpath = current_dirpath+"/"+mypv
if not os.path.isdir(mypvpath):
continue
mycounter_file = mypvpath+"/"+etpConst['spm']['xpak_entries']['counter']
if not os.access(mycounter_file,os.R_OK):
continue
f = open(mycounter_file)
try:
counter = int(f.readline().strip())
except (IOError, ValueError):
f.close()
continue
installedAtoms.add((os.path.basename(current_dirpath)+"/"+mypv,counter))
return installedAtoms
def _load_sets_config(self, settings, trees):
# from portage.const import USER_CONFIG_PATH, GLOBAL_CONFIG_PATH
setconfigpaths = [os.path.join(self.portage_const.GLOBAL_CONFIG_PATH, etpConst['setsconffilename'])]
setconfigpaths.append(os.path.join(settings["PORTDIR"], etpConst['setsconffilename']))
setconfigpaths += [os.path.join(x, etpConst['setsconffilename']) for x in settings["PORTDIR_OVERLAY"].split()]
setconfigpaths.append(os.path.join(settings["PORTAGE_CONFIGROOT"],
self.portage_const.USER_CONFIG_PATH.lstrip(os.path.sep), etpConst['setsconffilename']))
return self.portage_sets.SetConfig(setconfigpaths, settings, trees)
def get_set_config(self):
# old portage
if self.portage_sets == None: return
myroot = etpConst['systemroot']+"/"
return self._load_sets_config(
self.portage.settings,
self.portage.db[myroot]
)
def get_sets(self, builtin_sets):
config = self.get_set_config()
if config == None: return {}
mysets = config.getSets()
if not builtin_sets:
builtin_pkg_sets = [x for x in self.builtin_pkg_sets if x in mysets]
for pkg_set in builtin_pkg_sets: mysets.pop(pkg_set)
return mysets
def get_sets_expanded(self, builtin_sets = True):
config = self.get_set_config()
if config == None: return {}
mysets = {}
sets = config.getSets()
if not builtin_sets:
builtin_pkg_sets = [x for x in self.builtin_pkg_sets if x in sets]
for pkg_set in builtin_pkg_sets: sets.pop(pkg_set)
for myset in sorted(sets):
try: atoms = config.getSetAtoms(myset).copy()
except: continue
mysets[myset] = atoms
return mysets
def refill_counter(self, dbdir = None):
if not dbdir:
appDbDir = self.get_vdb_path()
else:
appDbDir = dbdir
counters = set()
for catdir in os.listdir(appDbDir):
catdir = appDbDir+catdir
if not os.path.isdir(catdir):
continue
for pkgdir in os.listdir(catdir):
pkgdir = catdir+"/"+pkgdir
if not os.path.isdir(pkgdir):
continue
counterfile = pkgdir+"/"+etpConst['spm']['xpak_entries']['counter']
if not os.path.isfile(pkgdir+"/"+etpConst['spm']['xpak_entries']['counter']):
continue
try:
f = open(counterfile,"r")
counter = int(f.readline().strip())
counters.add(counter)
f.close()
except:
continue
if counters:
newcounter = max(counters)
else:
newcounter = 0
if not os.path.isdir(os.path.dirname(etpConst['edbcounter'])):
os.makedirs(os.path.dirname(etpConst['edbcounter']))
try:
f = open(etpConst['edbcounter'],"w")
except IOError, e:
if e[0] == 21:
shutil.rmtree(etpConst['edbcounter'],True)
try:
os.rmdir(etpConst['edbcounter'])
except:
pass
f = open(etpConst['edbcounter'],"w")
f.write(str(newcounter))
f.flush()
f.close()
del counters
return newcounter
def spm_doebuild(self, myebuild, mydo, tree, cpv, portage_tmpdir = None, licenses = [], fork = False):
if fork:
# memory leak: some versions of portage were memleaking here
return self.entropyTools.spawnFunction(
self._portage_doebuild, myebuild,
mydo, tree, cpv,
portage_tmpdir, licenses
)
return self._portage_doebuild(myebuild, mydo, tree, cpv, portage_tmpdir, licenses)
def _portage_doebuild(self, myebuild, mydo, tree, cpv, portage_tmpdir = None, licenses = []):
# myebuild = path/to/ebuild.ebuild with a valid unpacked xpak metadata
# tree = "bintree"
# cpv = atom
# mydbapi = portage.fakedbapi(settings=portage.settings)
# vartree = portage.vartree(root=myroot)
oldsystderr = sys.stderr
f = open("/dev/null","w")
if not etpUi['debug']:
sys.stderr = f
### SETUP ENVIRONMENT
# if mute, supress portage output
domute = False
if etpUi['mute']:
domute = True
oldsysstdout = sys.stdout
sys.stdout = f
mypath = etpConst['systemroot']+"/"
os.environ["SKIP_EQUO_SYNC"] = "1"
os.environ["CD_ROOT"] = "/tmp" # workaround for scripts asking for user intervention
os.environ["ROOT"] = mypath
if licenses:
os.environ["ACCEPT_LICENSE"] = str(' '.join(licenses)) # we already do this early
# load metadata
myebuilddir = os.path.dirname(myebuild)
keys = self.portage.auxdbkeys
metadata = {}
for key in keys:
mykeypath = os.path.join(myebuilddir,key)
if os.path.isfile(mykeypath) and os.access(mykeypath,os.R_OK):
f = open(mykeypath,"r")
metadata[key] = f.readline().strip()
f.close()
### END SETUP ENVIRONMENT
# find config
mysettings = self._get_portage_config("/",mypath)
mysettings['EBUILD_PHASE'] = mydo
mysettings['EAPI'] = "0"
if metadata.has_key('EAPI'):
mysettings['EAPI'] = metadata['EAPI']
mysettings.backup_changes("EAPI")
try: # this is a >portage-2.1.4_rc11 feature
mysettings._environ_whitelist = set(mysettings._environ_whitelist)
# put our vars into whitelist
mysettings._environ_whitelist.add("SKIP_EQUO_SYNC")
mysettings._environ_whitelist.add("ACCEPT_LICENSE")
mysettings._environ_whitelist.add("CD_ROOT")
mysettings._environ_whitelist.add("ROOT")
mysettings._environ_whitelist = frozenset(mysettings._environ_whitelist)
except:
self.write_traceback_to_log()
cpv = str(cpv)
mysettings.setcpv(cpv)
portage_tmpdir_created = False # for pkg_postrm, pkg_prerm
if portage_tmpdir:
if not os.path.isdir(portage_tmpdir):
os.makedirs(portage_tmpdir)
portage_tmpdir_created = True
mysettings['PORTAGE_TMPDIR'] = str(portage_tmpdir)
mysettings.backup_changes("PORTAGE_TMPDIR")
mydbapi = self.portage.fakedbapi(settings=mysettings)
mydbapi.cpv_inject(cpv, metadata = metadata)
# cached vartree class
vartree = self._get_portage_vartree(mypath)
try:
rc = self.portage.doebuild(
myebuild = str(myebuild),
mydo = str(mydo),
myroot = mypath,
tree = tree,
mysettings = mysettings,
mydbapi = mydbapi,
vartree = vartree,
use_cache = 0
)
except:
self.write_traceback_to_log()
raise
# if mute, restore old stdout/stderr
if domute:
sys.stdout = oldsysstdout
sys.stderr = oldsystderr
f.close()
if portage_tmpdir_created:
shutil.rmtree(portage_tmpdir,True)
del mydbapi
del metadata
del keys
return rc
class LogFile:
def __init__ (self, level = 0, filename = None, header = "[LOG]"):
self.handler = self.default_handler
self.level = level
self.header = header
self.logFile = None
self.open(filename)
def __del__(self):
self.close()
def close(self):
try:
self.logFile.close()
except (IOError,OSError,):
pass
def flush(self):
self.logFile.flush()
def fileno(self):
return self.getFile()
def isatty(self):
return False
def read(self, a):
return ''
def readline(self):
return ''
def readlines(self):
return []
def seek(self, a):
return self.logFile.seek(a)
def tell(self):
return self.logFile.tell()
def truncate(self):
return self.logFile.truncate()
def open (self, file = None):
if isinstance(file,basestring):
if os.access(file,os.W_OK) and os.path.isfile(file):
self.logFile = open(file, "aw")
else:
self.logFile = open("/dev/null", "aw")
elif hasattr(file,'write'):
self.logFile = file
else:
self.logFile = sys.stderr
def getFile (self):
return self.logFile.fileno()
def __call__(self, format, *args):
self.handler (format % args)
def default_handler (self, mystr):
try:
self.logFile.write ("* %s\n" % (mystr))
except UnicodeEncodeError:
self.logFile.write ("* %s\n" % (mystr.encode('utf-8'),))
self.logFile.flush()
def set_loglevel(self, level):
self.level = level
def log(self, messagetype, level, message):
if self.level >= level and not etpUi['nolog']:
self.handler("%s%s %s %s" % (self.getTimeDateHeader(),messagetype,self.header,message,))
def write(self, s):
self.handler(s)
def writelines(self, lst):
for s in lst:
self.write(s)
def getTimeDateHeader(self):
return time.strftime('[%X %x %Z] ')
def ladd(self, level, file, message):
if self.level >= level:
self.handler("++ %s \t%s" % (file, message))
def ldel(self, level, file, message):
if self.level >= level:
self.handler("-- %s \t%s" % (file, message))
def lch(self, level, file, message):
if self.level >= level:
self.handler("-+ %s \t%s" % (file, message))
class SocketCommandsSkel:
def __str__(self):
return self.inst_name
inst_name = 'command-skel'
def __init__(self, HostInterface, inst_name = 'command-skel'):
self.HostInterface = HostInterface
self.no_acked_commands = []
self.termination_commands = []
self.initialization_commands = []
self.login_pass_commands = []
self.no_session_commands = []
self.raw_commands = []
self.config_commands = []
self.valid_commands = set()
self.inst_name = inst_name
def register(
self,
valid_commands,
no_acked_commands,
termination_commands,
initialization_commands,
login_pass_commads,
no_session_commands,
raw_commands,
config_commands
):
valid_commands.update(self.valid_commands)
no_acked_commands.extend(self.no_acked_commands)
termination_commands.extend(self.termination_commands)
initialization_commands.extend(self.initialization_commands)
login_pass_commads.extend(self.login_pass_commands)
no_session_commands.extend(self.no_session_commands)
raw_commands.extend(self.raw_commands)
config_commands.extend(self.config_commands)
class SocketAuthenticatorSkel:
def __init__(self, HostInterface):
self.HostInterface = HostInterface
self.session = None
def set_session(self, session):
self.session = session
class SocketHostInterface:
import socket
import SocketServer
import entropyTools
from threading import Thread
class BasicPamAuthenticator(SocketAuthenticatorSkel):
import entropyTools
def __init__(self, HostInterface, *args, **kwargs):
self.valid_auth_types = [ "plain", "shadow", "md5" ]
SocketAuthenticatorSkel.__init__(self, HostInterface)
def docmd_login(self, arguments):
with self.HostInterface.AuthenticatorLock:
# filter n00bs
if not arguments or (len(arguments) != 3):
return False,None,None,'wrong arguments'
user = arguments[0]
auth_type = arguments[1]
auth_string = arguments[2]
# check auth type validity
if auth_type not in self.valid_auth_types:
return False,user,None,'invalid auth type'
udata = self.__get_user_data(user)
if udata == None:
return False,user,None,'invalid user'
uid = udata[2]
# check if user is in the Entropy group
if not self.entropyTools.is_user_in_entropy_group(uid):
return False,user,uid,'user not in %s group' % (etpConst['sysgroup'],)
# now validate password
valid = self.__validate_auth(user,auth_type,auth_string)
if not valid:
return False,user,uid,'auth failed'
if not uid:
self.HostInterface.sessions[self.session]['admin'] = True
else:
self.HostInterface.sessions[self.session]['user'] = True
return True,user,uid,"ok"
# it we get here is because user is logged in
def docmd_userdata(self):
with self.HostInterface.AuthenticatorLock:
auth_uid = self.HostInterface.sessions[self.session]['auth_uid']
mydata = {}
udata = self.__get_uid_data(auth_uid)
if udata:
mydata['username'] = udata[0]
mydata['uid'] = udata[2]
mydata['gid'] = udata[3]
mydata['references'] = udata[4]
mydata['home'] = udata[5]
mydata['shell'] = udata[6]
return True,mydata,'ok'
def __get_uid_data(self, user_id):
import pwd
# check user validty
try:
udata = pwd.getpwuid(user_id)
except KeyError:
return None
return udata
def __get_user_data(self, user):
import pwd
# check user validty
try:
udata = pwd.getpwnam(user)
except KeyError:
return None
return udata
def __validate_auth(self, user, auth_type, auth_string):
valid = False
if auth_type == "plain":
valid = self.__do_auth(user, auth_string)
elif auth_type == "shadow":
valid = self.__do_auth(user, auth_string, auth_type = "shadow")
elif auth_type == "md5":
valid = self.__do_auth(user, auth_string, auth_type = "md5")
return valid
def __do_auth(self, user, password, auth_type = None):
import spwd
try:
enc_pass = spwd.getspnam(user)[1]
except KeyError:
return False
if auth_type == None: # plain
import crypt
generated_pass = crypt.crypt(str(password), enc_pass)
elif auth_type == "shadow":
generated_pass = password
elif auth_type == "md5": # md5
import hashlib
m = hashlib.md5()
m.update(enc_pass)
enc_pass = m.hexdigest()
generated_pass = str(password)
else: # haha, fuck!
generated_pass = None
if generated_pass == enc_pass:
return True
return False
def docmd_logout(self, myargs):
with self.HostInterface.AuthenticatorLock:
# filter n00bs
if (len(myargs) < 1) or (len(myargs) > 1):
return False,None,'wrong arguments'
user = myargs[0]
# filter n00bs
if not user or not isinstance(user,basestring):
return False,None,"wrong user"
return True,user,"ok"
def set_exc_permissions(self, uid, gid):
with self.HostInterface.AuthenticatorLock:
if gid != None:
os.setgid(gid)
if uid != None:
os.setuid(uid)
def hide_login_data(self, args):
myargs = args[:]
myargs[-1] = 'hidden'
return myargs
def terminate_instance(self):
pass
class HostServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
class ConnWrapper:
'''
Base class for implementing the rest of the wrappers in this module.
Operates by taking a connection argument which is used when 'self' doesn't
provide the functionality being requested.
'''
def __init__(self, connection) :
self.connection = connection
def __getattr__(self, function) :
return getattr(self.connection, function)
import socket as socket_mod
import select
import SocketServer
# This means the main server will not do the equivalent of a
# pthread_join() on the new threads. With this set, Ctrl-C will
# kill the server reliably.
daemon_threads = True
# By setting this we allow the server to re-bind to the address by
# setting SO_REUSEADDR, meaning you don't have to wait for
# timeouts when you kill the server and the sockets don't get
# closed down correctly.
allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass, processor, HostInterface, authorized_clients_only = False):
self.alive = True
self.socket = self.socket_mod
self.processor = processor
self.server_address = server_address
self.HostInterface = HostInterface
self.SSL = self.HostInterface.SSL
self.real_sock = None
self.ssl_authorized_clients_only = authorized_clients_only
if self.SSL:
self.SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass)
self.load_ssl_context()
self.make_ssl_connection_alive()
else:
try:
self.SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
except self.socket_mod.error, e:
if e[0] == 13:
raise exceptionTools.ConnectionError('ConnectionError: %s' % (_("Cannot bind the service"),))
raise
def load_ssl_context(self):
# setup an SSL context.
self.context = self.SSL['m'].Context(self.SSL['m'].SSLv23_METHOD)
self.context.set_verify(self.SSL['m'].VERIFY_PEER, self.verify_ssl_cb) # ask for a certificate
self.context.set_options(self.SSL['m'].OP_NO_SSLv2)
# load up certificate stuff.
self.context.use_privatekey_file(self.SSL['key'])
self.context.use_certificate_file(self.SSL['cert'])
self.context.load_verify_locations(self.SSL['ca_cert'])
self.context.load_client_ca(self.SSL['ca_cert'])
self.HostInterface.updateProgress('SSL context loaded, key: %s - cert: %s, CA cert: %s, CA pkey: %s' % (
self.SSL['key'],
self.SSL['cert'],
self.SSL['ca_cert'],
self.SSL['ca_pkey']
)
)
def make_ssl_connection_alive(self):
self.real_sock = self.socket_mod.socket(self.address_family, self.socket_type)
self.socket = self.ConnWrapper(self.SSL['m'].Connection(self.context, self.real_sock))
self.server_bind()
self.server_activate()
# this function should do the authentication checking to see that
# the client is who they say they are.
def verify_ssl_cb(self, conn, cert, errnum, depth, ok) :
return ok
def verify_request(self, request, client_address):
self.do_ssl = self.HostInterface.SSL
if self.do_ssl: self.do_ssl = True
else: self.do_ssl = False
allowed = self.ip_blacklist_check(client_address[0])
if allowed: allowed = self.ip_max_connections_check(client_address[0])
if not allowed:
self.HostInterface.updateProgress(
'[from: %s | SSL: %s] connection refused, ip blacklisted or maximum connections per IP reached' % (
client_address,
self.do_ssl,
)
)
return False
allowed = self.max_connections_check(request)
if not allowed:
self.HostInterface.updateProgress(
'[from: %s | SSL: %s] connection refused (max connections reached: %s)' % (
client_address,
self.do_ssl,
self.HostInterface.max_connections,
)
)
return False
### let's go!
self.HostInterface.connections += 1
self.HostInterface.updateProgress(
'[from: %s | SSL: %s] connection established (%s of %s max connections)' % (
client_address,
self.do_ssl,
self.HostInterface.connections,
self.HostInterface.max_connections,
)
)
return True
def ip_blacklist_check(self, client_addr):
if client_addr in self.HostInterface.ip_blacklist:
return False
return True
def ip_max_connections_check(self, ip_address):
max_conn_per_ip = self.HostInterface.max_connections_per_host
max_conn_per_ip_barrier = self.HostInterface.max_connections_per_host_barrier
per_host_connections = self.HostInterface.per_host_connections
conn_data = per_host_connections.get(ip_address)
if conn_data == None:
per_host_connections[ip_address] = 1
else:
conn_data += 1
per_host_connections[ip_address] += 1
if conn_data > max_conn_per_ip:
self.HostInterface.updateProgress(
'[from: %s] ------- :EEK: !! connection closed too many simultaneous connections from host (current: %s | limit: %s) -------' % (
ip_address,
conn_data,
max_conn_per_ip,
)
)
return False
elif conn_data > max_conn_per_ip_barrier:
times = [5,6,7,8]
self.HostInterface.updateProgress(
'[from: %s] ------- :EEEK: !! connection warning simultaneous connection barrier reached from host (current: %s | soft limit: %s) -------' % (
ip_address,
conn_data,
max_conn_per_ip_barrier,
)
)
time.sleep(times[abs(hash(os.urandom(1)))%len(times)])
return True
def max_connections_check(self, request):
current = self.HostInterface.connections
maximum = self.HostInterface.max_connections
if current >= maximum:
self.HostInterface.transmit(
request,
self.HostInterface.answers['mcr']
)
return False
else:
return True
def serve_forever(self):
while self.alive:
#r,w,e = self.select.select([self.socket], [], [], 1)
#if r:
self.handle_request()
# taken from SocketServer.py
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
self.HostInterface.updateProgress(
'[from: %s] connection closed (%s of %s max connections)' % (
client_address,
self.HostInterface.connections - 1,
self.HostInterface.max_connections,
)
)
per_host_connections = self.HostInterface.per_host_connections
conn_data = per_host_connections.get(client_address[0])
if conn_data != None:
if conn_data < 1:
del per_host_connections[client_address[0]]
else:
per_host_connections[client_address[0]] -= 1
def close_request(self, request):
if self.HostInterface.connections > 0:
self.HostInterface.connections -= 1
class RequestHandler(SocketServer.BaseRequestHandler):
import SocketServer
import select
import socket
import entropyTools
import gc
timed_out = False
def __init__(self, request, client_address, server):
self.SocketServer.BaseRequestHandler.__init__(self, request, client_address, server)
def data_receiver(self):
if self.timed_out:
return True
self.timed_out = True
try:
ready_to_read, ready_to_write, in_error = self.select.select([self.request], [], [], self.default_timeout)
except KeyboardInterrupt:
self.timed_out = True
return True
if len(ready_to_read) == 1 and ready_to_read[0] == self.request:
self.timed_out = False
try:
data = self.request.recv(1024)
if self.ssl:
while self.request.pending():
data += self.request.recv(1024)
if self.data_counter == None:
if data == '': # client wants to close
return True
elif data == self.server.processor.HostInterface.answers['noop']:
return False
elif len(data) < len(self.myeos):
self.server.processor.HostInterface.updateProgress(
'interrupted: %s, reason: %s - from client: %s - data: "%s" - counter: %s' % (
self.server.server_address,
"malformed EOS",
self.client_address,
repr(data),
self.data_counter,
)
)
self.buffered_data = ''
return True
mystrlen = data.split(self.myeos)[0]
self.data_counter = int(mystrlen)
data = data[len(mystrlen)+1:]
self.data_counter -= len(data)
self.buffered_data += data
# command length exceeds our command length limit
if self.data_counter > self.max_command_length:
raise exceptionTools.InterruptError('InterruptError: command too long: %s, limit: %s' % (self.data_counter,self.max_command_length,))
while self.data_counter > 0:
if self.ssl:
x = ''
while self.request.pending():
x += self.request.recv(1024)
else:
x = self.request.recv(1024)
xlen = len(x)
self.data_counter -= xlen
self.buffered_data += x
if not xlen:
break
self.data_counter = None
except ValueError:
#self.entropyTools.printTraceback()
self.server.processor.HostInterface.updateProgress(
'interrupted: %s, reason: %s - from client: %s' % (
self.server.server_address,
"malformed transmission",
self.client_address,
)
)
return True
except self.socket.timeout, e:
self.server.processor.HostInterface.updateProgress(
'interrupted: %s, reason: %s - from client: %s' % (
self.server.server_address,
e,
self.client_address,
)
)
return True
except self.socket.sslerror, e:
self.server.processor.HostInterface.updateProgress(
'interrupted: %s, SSL socket error reason: %s - from client: %s' % (
self.server.server_address,
e,
self.client_address,
)
)
return True
except (self.ssl_exceptions['WantReadError'], self.ssl_exceptions['WantX509LookupError'],):
return False
except self.ssl_exceptions['ZeroReturnError']:
return True
except self.ssl_exceptions['Error'], e:
self.server.processor.HostInterface.updateProgress(
'interrupted: SSL Error, reason: %s - from client: %s' % (
e,
self.client_address,
)
)
return True
except exceptionTools.InterruptError, e:
self.server.processor.HostInterface.updateProgress(
'interrupted: Command Error, reason: %s - from client: %s' % (
e,
self.client_address,
)
)
return True
if not self.buffered_data:
return True
cmd = self.server.processor.process(self.buffered_data, self.request, self.client_address)
if cmd == 'close':
# send KAPUTT signal JA!
self.server.processor.transmit(self.server.processor.HostInterface.answers['cl'])
return True
self.buffered_data = ''
return False
def handle(self):
# not using spawnFunction because it causes some mess
# forking this way avoids having memory leaks
if self.server.processor.HostInterface.fork_requests:
my_timeout = self.server.processor.HostInterface.fork_request_timeout_seconds
pid = os.fork()
seconds = 0
if pid > 0:
# pid killer after timeout
passed_away = False
while 1:
time.sleep(1)
seconds += 1
try:
dead = os.waitpid(pid, os.WNOHANG)[0]
except OSError, e:
if e.errno != 10: raise
dead = True
if passed_away:
break
if dead: break
if seconds > my_timeout:
self.server.processor.HostInterface.updateProgress(
'interrupted: forked request timeout: %s,%s from client: %s' % (
seconds,
dead,
self.client_address,
)
)
if not dead:
import signal
os.kill(pid,signal.SIGKILL)
passed_away = True # in this way, the process table should be clean
continue
break
else:
self.do_handle()
os._exit(0)
else:
self.do_handle()
#self.entropyTools.spawnFunction(self.do_handle)
def do_handle(self):
self.default_timeout = self.server.processor.HostInterface.timeout
self.ssl = self.server.processor.HostInterface.SSL
self.ssl_exceptions = self.server.processor.HostInterface.SSL_exceptions
self.myeos = self.server.processor.HostInterface.answers['eos']
self.max_command_length = self.server.processor.HostInterface.max_command_length
while 1:
try:
dobreak = self.data_receiver()
if dobreak: break
except Exception, e:
self.server.processor.HostInterface.updateProgress(
'interrupted: Unhandled exception: %s, error: %s - from client: %s' % (
Exception,
e,
self.client_address,
)
)
# print exception
self.entropyTools.printTraceback()
self.entropyTools.printTraceback(f = self.server.processor.HostInterface.socketLog)
break
self.request.close()
def setup(self):
self.data_counter = None
self.buffered_data = ''
class CommandProcessor:
import entropyTools
import socket
import gc
def __init__(self, HostInterface):
self.HostInterface = HostInterface
self.channel = None
self.lastoutput = None
def handle_termination_commands(self, data):
if data.strip() in self.HostInterface.termination_commands:
self.HostInterface.updateProgress('close: %s' % (self.client_address,))
self.transmit(self.HostInterface.answers['cl'])
return "close"
if not data.strip():
return "ignore"
def handle_command_string(self, string):
# validate command
args = string.strip().split()
session = args[0]
if (session in self.HostInterface.initialization_commands) or len(args) < 2:
cmd = args[0]
session = None
else:
cmd = args[1]
args = args[1:] # remove session
stream_enabled = False
if (session != None) and self.HostInterface.sessions.has_key(session):
stream_enabled = self.HostInterface.sessions[session].get('stream_mode')
if stream_enabled and (cmd not in self.HostInterface.config_commands):
session_len = 0
if session: session_len = len(session)+1
return cmd,[string[session_len+len(cmd)+1:]],session
else:
myargs = []
if len(args) > 1:
myargs = args[1:]
return cmd,myargs,session
def handle_end_answer(self, cmd, whoops, valid_cmd):
if not valid_cmd:
self.transmit(self.HostInterface.answers['no'])
elif whoops:
self.transmit(self.HostInterface.answers['er'])
elif cmd not in self.HostInterface.no_acked_commands:
self.transmit(self.HostInterface.answers['ok'])
def validate_command(self, cmd, args, session):
# answer to invalid commands
if (cmd not in self.HostInterface.valid_commands):
return False,"not a valid command"
if session == None:
if cmd not in self.HostInterface.no_session_commands:
return False,"need a valid session"
elif session not in self.HostInterface.sessions:
return False,"session is not alive"
# check if command needs authentication
if session != None:
auth = self.HostInterface.valid_commands[cmd]['auth']
if auth:
# are we?
authed = self.HostInterface.sessions[session]['auth_uid']
if authed == None:
# nope
return False,"not authenticated"
# keep session alive
if session != None:
self.HostInterface.set_session_running(session)
self.HostInterface.update_session_time(session)
return True,"all good"
def load_authenticator(self):
f, args, kwargs = self.HostInterface.AuthenticatorInst
myinst = f(*args,**kwargs)
return myinst
def load_service_interface(self, session):
uid = None
if session != None:
uid = self.HostInterface.sessions[session]['auth_uid']
intf = self.HostInterface.EntropyInstantiation[0]
args = self.HostInterface.EntropyInstantiation[1]
kwds = self.HostInterface.EntropyInstantiation[2]
Entropy = intf(*args, **kwds)
Entropy.urlFetcher = SocketUrlFetcher
Entropy.updateProgress = self.remoteUpdateProgress
try:
Entropy.clientDbconn.updateProgress = self.remoteUpdateProgress
except AttributeError:
pass
Entropy.progress = self.remoteUpdateProgress
return Entropy
def process(self, data, channel, client_address):
self.channel = channel
self.client_address = client_address
term = self.handle_termination_commands(data)
if term:
del authenticator
return term
cmd, args, session = self.handle_command_string(data)
valid_cmd, reason = self.validate_command(cmd, args, session)
# decide if we need to load authenticator or Entropy
authenticator = None
cmd_data = self.HostInterface.valid_commands.get(cmd)
if not isinstance(cmd_data,dict):
self.HostInterface.updateProgress(
'[from: %s] command error: invalid command: %s' % (
self.client_address,
cmd,
)
)
return "close"
elif (("authenticator" in cmd_data['args']) or (cmd in self.HostInterface.login_pass_commands)):
try:
authenticator = self.load_authenticator()
except exceptionTools.ConnectionError, e:
self.HostInterface.updateProgress(
'[from: %s] authenticator error: cannot load: %s' % (
self.client_address,
e,
)
)
self.entropyTools.printTraceback()
self.entropyTools.printTraceback(f = self.HostInterface.socketLog)
return "close"
except Exception, e:
self.HostInterface.updateProgress(
'[from: %s] authenticator error: cannot load: %s - unknown error' % (
self.client_address,
e,
)
)
self.entropyTools.printTraceback()
self.entropyTools.printTraceback(f = self.HostInterface.socketLog)
return "close"
p_args = args
if (cmd in self.HostInterface.login_pass_commands) and authenticator != None:
p_args = authenticator.hide_login_data(p_args)
elif cmd in self.HostInterface.raw_commands:
p_args = ['raw data']
self.HostInterface.updateProgress(
'[from: %s] command validation :: called %s: length: %s, args: %s, session: %s, valid: %s, reason: %s' % (
self.client_address,
cmd,
len(data),
p_args,
session,
valid_cmd,
reason,
)
)
whoops = False
if valid_cmd:
if authenticator != None:
# now set session
authenticator.set_session(session)
Entropy = None
if "Entropy" in cmd_data['args']:
Entropy = self.load_service_interface(session)
try:
self.run_task(cmd, args, session, Entropy, authenticator)
except self.socket.timeout:
self.HostInterface.updateProgress(
'[from: %s] command error: timeout, closing connection' % (
self.client_address,
)
)
# close connection
del authenticator
del Entropy
return "close"
except self.socket.error, e:
self.HostInterface.updateProgress(
'[from: %s] command error: socket error: %s' % (
self.client_address,
e,
)
)
# close connection
del authenticator
del Entropy
return "close"
except self.HostInterface.SSL_exceptions['SysCallError'], e:
self.HostInterface.updateProgress(
'[from: %s] command error: SSL SysCallError: %s' % (
self.client_address,
e,
)
)
# close connection
del authenticator
del Entropy
return "close"
except Exception, e:
# write to self.HostInterface.socketLog
self.entropyTools.printTraceback()
self.entropyTools.printTraceback(f = self.HostInterface.socketLog)
# store error
self.HostInterface.updateProgress(
'[from: %s] command error: %s, type: %s' % (
self.client_address,
e,
type(e),
)
)
if session != None:
self.HostInterface.store_rc(str(e),session)
whoops = True
del Entropy
if session != None:
self.HostInterface.update_session_time(session)
self.HostInterface.unset_session_running(session)
rcmd = None
try:
self.handle_end_answer(cmd, whoops, valid_cmd)
except (self.socket.error, self.socket.timeout,self.HostInterface.SSL_exceptions['SysCallError'],):
rcmd = "close"
if authenticator != None:
authenticator.terminate_instance()
del authenticator
if not self.HostInterface.fork_requests:
self.gc.collect()
return rcmd
def transmit(self, data):
self.HostInterface.transmit(self.channel, data)
def remoteUpdateProgress(
self,
text,
header = "",
footer = "",
back = False,
importance = 0,
type = "info",
count = [],
percent = False
):
if text != self.lastoutput:
text = chr(27)+"[2K\r"+text
if not back:
text += "\n"
self.transmit(text)
self.lastoutput = text
def run_task(self, cmd, args, session, Entropy, authenticator):
p_args = args
if cmd in self.HostInterface.login_pass_commands:
p_args = authenticator.hide_login_data(p_args)
elif cmd in self.HostInterface.raw_commands:
p_args = ['raw data']
self.HostInterface.updateProgress(
'[from: %s] run_task :: called %s: args: %s, session: %s' % (
self.client_address,
cmd,
p_args,
session,
)
)
myargs = args
mykwargs = {}
if cmd not in self.HostInterface.raw_commands:
myargs, mykwargs = self._get_args_kwargs(args)
rc = self.spawn_function(cmd, myargs, mykwargs, session, Entropy, authenticator)
if session != None and self.HostInterface.sessions.has_key(session):
self.HostInterface.store_rc(rc, session)
return rc
def _get_args_kwargs(self, args):
myargs = []
mykwargs = {}
def is_int(x):
try:
int(x)
except ValueError:
return False
return True
for arg in args:
if (arg.find("=") != -1) and not arg.startswith("="):
x = arg.split("=")
a = x[0]
b = ''.join(x[1:])
if (b in ("True","False",)) or is_int(b):
mykwargs[a] = eval(b)
else:
myargs.append(arg)
else:
if (arg in ("True","False",)) or is_int(arg):
myargs.append(eval(arg))
else:
myargs.append(arg)
return myargs, mykwargs
def spawn_function(self, cmd, myargs, mykwargs, session, Entropy, authenticator):
p_args = myargs
if cmd in self.HostInterface.login_pass_commands:
p_args = authenticator.hide_login_data(p_args)
elif cmd in self.HostInterface.raw_commands:
p_args = ['raw data']
self.HostInterface.updateProgress(
'[from: %s] called %s: args: %s, kwargs: %s' % (
self.client_address,
cmd,
p_args,
mykwargs,
)
)
return self.do_spawn(cmd, myargs, mykwargs, session, Entropy, authenticator)
def do_spawn(self, cmd, myargs, mykwargs, session, Entropy, authenticator):
cmd_data = self.HostInterface.valid_commands.get(cmd)
do_fork = cmd_data['as_user']
f = cmd_data['cb']
func_args = []
for arg in cmd_data['args']:
try:
func_args.append(eval(arg))
except (NameError, SyntaxError):
func_args.append(str(arg))
if do_fork:
myfargs = func_args[:]
myfargs.extend(myargs)
return self.fork_task(f, session, authenticator, *myfargs, **mykwargs)
else:
return f(*func_args)
def fork_task(self, f, session, authenticator, *args, **kwargs):
gid = None
uid = None
if session != None:
logged_in = self.HostInterface.sessions[session]['auth_uid']
if logged_in != None:
uid = logged_in
gid = etpConst['entropygid']
return self.entropyTools.spawnFunction(self._do_fork, f, authenticator, uid, gid, *args, **kwargs)
def _do_fork(self, f, authenticator, uid, gid, *args, **kwargs):
authenticator.set_exc_permissions(uid,gid)
rc = f(*args,**kwargs)
return rc
class BuiltInCommands(SocketCommandsSkel):
import dumpTools
import zlib
def __init__(self, HostInterface):
SocketCommandsSkel.__init__(self, HostInterface, inst_name = "builtin")
self.valid_commands = {
'begin': {
'auth': False, # does it need authentication ?
'built_in': True, # is it built-in ?
'cb': self.docmd_begin, # function to call
'args': ["self.transmit", "self.client_address"], # arguments to be passed before *args and **kwards, in SocketHostInterface.do_spawn()
'as_user': False, # do I have to fork the process and run it as logged user?
# needs auth = True
'desc': "instantiate a session", # description
'syntax': "begin", # syntax
'from': unicode(self), # from what class
},
'end': {
'auth': False,
'built_in': True,
'cb': self.docmd_end,
'args': ["self.transmit", "session"],
'as_user': False,
'desc': "end a session",
'syntax': "<SESSION_ID> end",
'from': unicode(self),
},
'session_config': {
'auth': False,
'built_in': True,
'cb': self.docmd_session_config,
'args': ["session","myargs"],
'as_user': False,
'desc': "set session configuration options",
'syntax': "<SESSION_ID> session_config <option> [parameters]",
'from': unicode(self),
},
'rc': {
'auth': False,
'built_in': True,
'cb': self.docmd_rc,
'args': ["self.transmit","session"],
'as_user': False,
'desc': "get data returned by the last valid command (streamed python object)",
'syntax': "<SESSION_ID> rc",
'from': unicode(self),
},
'hello': {
'auth': False,
'built_in': True,
'cb': self.docmd_hello,
'args': ["self.transmit"],
'as_user': False,
'desc': "get server status",
'syntax': "hello",
'from': unicode(self),
},
'alive': {
'auth': True,
'built_in': True,
'cb': self.docmd_alive,
'args': ["self.transmit","session"],
'as_user': False,
'desc': "check if a session is still alive",
'syntax': "<SESSION_ID> alive",
'from': unicode(self),
},
'login': {
'auth': False,
'built_in': True,
'cb': self.docmd_login,
'args': ["self.transmit", "authenticator", "session", "self.client_address", "myargs"],
'as_user': False,
'desc': "login on the running server (allows running extra commands)",
'syntax': "<SESSION_ID> login <authenticator parameters, default: <user> <auth_type> <password> >",
'from': unicode(self),
},
'user_data': {
'auth': True,
'built_in': True,
'cb': self.docmd_userdata,
'args': ["self.transmit", "authenticator", "session"],
'as_user': False,
'desc': "get general user information, user must be logged in",
'syntax': "<SESSION_ID> user_data",
'from': unicode(self),
},
'logout': {
'auth': True,
'built_in': True,
'cb': self.docmd_logout,
'args': ["self.transmit", "authenticator", "session", "self.client_address", "myargs"],
'as_user': False,
'desc': "logout on the running server",
'syntax': "<SESSION_ID> logout <USER>",
'from': unicode(self),
},
'help': {
'auth': False,
'built_in': True,
'cb': self.docmd_help,
'args': ["self.transmit"],
'as_user': False,
'desc': "this output",
'syntax': "help",
'from': unicode(self),
},
'available_commands': {
'auth': False,
'built_in': True,
'cb': self.docmd_available_commands,
'args': ["self.HostInterface"],
'as_user': False,
'desc': "get info about available commands (you must retrieve this using the 'rc' command)",
'syntax': "available_commands",
'from': unicode(self),
},
'stream': {
'auth': True,
'built_in': True,
'cb': self.docmd_stream,
'args': ["session", "myargs"],
'as_user': False,
'desc': "send a chunk of data to be saved on the session temp file path (will be removed on session expiration)",
'syntax': "<SESSION_ID> stream <chunk of byte-string to write to file>",
'from': unicode(self),
},
}
self.no_acked_commands = ["rc", "begin", "end", "hello", "alive", "login", "logout","help"]
self.termination_commands = ["quit","close"]
self.initialization_commands = ["begin"]
self.login_pass_commands = ["login"]
self.no_session_commands = ["begin","hello","alive","help"]
self.raw_commands = ["stream"]
self.config_commands = ["session_config"]
def docmd_session_config(self, session, myargs):
if not myargs:
return False,"not enough parameters"
option = myargs[0]
myopts = myargs[1:]
if option == "compression":
docomp = True
do_zlib = False
if "zlib" in myopts:
do_zlib = True
if myopts:
if isinstance(myopts[0],bool):
docomp = myopts[0]
else:
try:
docomp = eval(myopts[0])
except (NameError, TypeError,):
pass
if docomp and do_zlib:
docomp = "zlib"
elif docomp and not do_zlib:
docomp = "gzip"
else:
docomp = None
self.HostInterface.sessions[session]['compression'] = docomp
return True,"compression now: %s" % (docomp,)
elif option == "stream":
dostream = True
if "off" in myopts:
dostream = False
self.HostInterface.sessions[session]['stream_mode'] = dostream
return True,'stream mode: %s' % (dostream,)
else:
return False,"invalid config option"
def docmd_available_commands(self, host_interface):
def copy_obj(obj):
if isinstance(obj,set) or isinstance(obj,dict):
return obj.copy()
elif isinstance(obj,list) or isinstance(obj,tuple):
return obj[:]
return obj
def can_be_streamed(obj):
if isinstance(obj,(bool,basestring,int,float,list,tuple,set,dict,)):
return True
return False
mydata = {}
mydata['disabled_commands'] = copy_obj(host_interface.disabled_commands)
valid_cmds = copy_obj(host_interface.valid_commands)
mydata['valid_commands'] = {}
for cmd in valid_cmds:
mydict = {}
for item in valid_cmds[cmd]:
param = valid_cmds[cmd][item]
if not can_be_streamed(param):
continue
mydict[item] = param
mydata['valid_commands'][cmd] = mydict.copy()
return mydata
def docmd_stream(self, session, myargs):
if not self.HostInterface.sessions[session]['stream_mode']:
return False,'not in stream mode'
if not myargs:
return False,'no stream sent'
compression = self.HostInterface.sessions[session]['compression']
stream = myargs[0]
stream_path = self.HostInterface.sessions[session]['stream_path']
stream_dir = os.path.dirname(stream_path)
if not os.path.isdir(os.path.dirname(stream_path)):
try:
os.makedirs(stream_dir)
if etpConst['entropygid'] != None:
const_setup_perms(stream_dir,etpConst['entropygid'])
except OSError:
return False,'cannot initialize stream directory'
f = open(stream_path,'abw')
if compression:
stream = self.zlib.decompress(stream)
f.write(stream)
f.flush()
f.close()
return True,'ok'
def docmd_login(self, transmitter, authenticator, session, client_address, myargs):
# is already auth'd?
auth_uid = self.HostInterface.sessions[session]['auth_uid']
if auth_uid != None:
return False,"already authenticated"
status, user, uid, reason = authenticator.docmd_login(myargs)
if status:
self.HostInterface.updateProgress(
'[from: %s] user %s logged in successfully, session: %s' % (
client_address,
user,
session,
)
)
self.HostInterface.sessions[session]['auth_uid'] = uid
transmitter(self.HostInterface.answers['ok'])
return True,reason
elif user == None:
self.HostInterface.updateProgress(
'[from: %s] user -not specified- login failed, session: %s, reason: %s' % (
client_address,
session,
reason,
)
)
transmitter(self.HostInterface.answers['no'])
return False,reason
else:
self.HostInterface.updateProgress(
'[from: %s] user %s login failed, session: %s, reason: %s' % (
client_address,
user,
session,
reason,
)
)
transmitter(self.HostInterface.answers['no'])
return False,reason
def docmd_userdata(self, transmitter, authenticator, session):
auth_uid = self.HostInterface.sessions[session]['auth_uid']
if auth_uid == None:
return False,None,"not authenticated"
return authenticator.docmd_userdata()
def docmd_logout(self, transmitter, authenticator, session, client_address, myargs):
status, user, reason = authenticator.docmd_logout(myargs)
if status:
self.HostInterface.updateProgress(
'[from: %s] user %s logged out successfully, session: %s, args: %s ' % (
client_address,
user,
session,
myargs,
)
)
self.HostInterface.sessions[session]['auth_uid'] = None
transmitter(self.HostInterface.answers['ok'])
return True,reason
elif user == None:
self.HostInterface.updateProgress(
'[from: %s] user -not specified- logout failed, session: %s, args: %s, reason: %s' % (
client_address,
session,
myargs,
reason,
)
)
transmitter(self.HostInterface.answers['no'])
return False,reason
else:
self.HostInterface.updateProgress(
'[from: %s] user %s logout failed, session: %s, args: %s, reason: %s' % (
client_address,
user,
session,
myargs,
reason,
)
)
transmitter(self.HostInterface.answers['no'])
return False,reason
def docmd_alive(self, transmitter, session):
cmd = self.HostInterface.answers['no']
if session in self.HostInterface.sessions:
cmd = self.HostInterface.answers['ok']
transmitter(cmd)
def docmd_hello(self, transmitter):
uname = os.uname()
kern_string = uname[2]
running_host = uname[1]
running_arch = uname[4]
load_stats = commands.getoutput('uptime').split("\n")[0]
text = "Entropy Server %s, connections: %s ~ running on: %s ~ host: %s ~ arch: %s, kernel: %s, stats: %s\n" % (
etpConst['entropyversion'],
self.HostInterface.connections,
etpConst['systemname'],
running_host,
running_arch,
kern_string,
load_stats
)
transmitter(text)
def docmd_help(self, transmitter):
text = '\nEntropy Socket Interface Help Menu\n' + \
'Available Commands:\n\n'
valid_cmds = sorted(self.HostInterface.valid_commands.keys())
for cmd in valid_cmds:
if self.HostInterface.valid_commands[cmd].has_key('desc'):
desc = self.HostInterface.valid_commands[cmd]['desc']
else:
desc = 'no description available'
if self.HostInterface.valid_commands[cmd].has_key('syntax'):
syntax = self.HostInterface.valid_commands[cmd]['syntax']
else:
syntax = 'no syntax available'
if self.HostInterface.valid_commands[cmd].has_key('from'):
myfrom = self.HostInterface.valid_commands[cmd]['from']
else:
myfrom = 'N/A'
text += "[%s] %s\n %s: %s\n %s: %s\n" % (
myfrom,
blue(cmd),
red("description"),
desc.strip(),
darkgreen("syntax"),
syntax,
)
transmitter(text)
def docmd_end(self, transmitter, session):
rc = self.HostInterface.destroy_session(session)
cmd = self.HostInterface.answers['no']
if rc: cmd = self.HostInterface.answers['ok']
transmitter(cmd)
return rc
def docmd_begin(self, transmitter, client_address):
session = self.HostInterface.get_new_session(client_address[0])
transmitter(session)
return session
def docmd_rc(self, transmitter, session):
rc = self.HostInterface.get_rc(session)
comp = self.HostInterface.sessions[session]['compression']
myserialized = self.dumpTools.serialize_string(rc)
if comp == "zlib": # new shiny zlib
myserialized = self.zlib.compress(myserialized, 7) # compression level 1-9
elif comp == "gzip": # old and burried
import gzip
try:
import cStringIO as stringio
except ImportError:
import StringIO as stringio
f = stringio.StringIO()
self.dumpTools.serialize(rc, f)
myf = stringio.StringIO()
mygz = gzip.GzipFile(
mode = 'wb',
fileobj = myf
)
f.seek(0)
chunk = f.read(8192)
while chunk:
mygz.write(chunk)
chunk = f.read(8192)
mygz.flush()
mygz.close()
myserialized = myf.getvalue()
f.close()
myf.close()
transmitter(myserialized)
return rc
import gc, threading
def __init__(self, service_interface, *args, **kwds):
self.Server = None
self.Gc = None
self.PythonGarbageCollector = None
self.AuthenticatorInst = None
self.args = args
self.kwds = kwds
self.socketLog = LogFile(
level = etpConst['socketloglevel'],
filename = etpConst['socketlogfile'],
header = "[Socket]"
)
# settings
self.SessionsLock = self.threading.Lock()
self.fork_requests = True # used by the command processor
self.fork_request_timeout_seconds = etpConst['socket_service']['forked_requests_timeout']
self.stdout_logging = True
self.timeout = etpConst['socket_service']['timeout']
self.hostname = etpConst['socket_service']['hostname']
self.session_ttl = etpConst['socket_service']['session_ttl']
if self.hostname == "*": self.hostname = ''
self.port = etpConst['socket_service']['port']
self.threads = etpConst['socket_service']['threads'] # maximum number of allowed sessions
self.max_connections = etpConst['socket_service']['max_connections']
self.max_connections_per_host = etpConst['socket_service']['max_connections_per_host']
self.max_connections_per_host_barrier = etpConst['socket_service']['max_connections_per_host_barrier']
self.max_command_length = etpConst['socket_service']['max_command_length']
self.disabled_commands = etpConst['socket_service']['disabled_cmds']
self.ip_blacklist = etpConst['socket_service']['ip_blacklist']
self.connections = 0
self.per_host_connections = {}
self.sessions = {}
self.answers = etpConst['socket_service']['answers']
self.__output = None
self.SSL = {}
self.SSL_exceptions = {}
self.SSL_exceptions['WantReadError'] = None
self.SSL_exceptions['WantWriteError'] = None
self.SSL_exceptions['WantX509LookupError'] = None
self.SSL_exceptions['ZeroReturnError'] = None
self.SSL_exceptions['SysCallError'] = None
self.SSL_exceptions['Error'] = []
self.last_print = ''
self.valid_commands = {}
self.no_acked_commands = []
self.raw_commands = []
self.config_commands = []
self.termination_commands = []
self.initialization_commands = []
self.login_pass_commands = []
self.no_session_commands = []
self.command_classes = [self.BuiltInCommands]
self.command_instances = []
self.EntropyInstantiation = (service_interface, self.args, self.kwds)
self.setup_external_command_classes()
self.start_local_output_interface()
self.setup_authenticator()
self.setup_hostname()
self.setup_commands()
self.disable_commands()
self.start_session_garbage_collector()
self.setup_ssl()
self.start_python_garbage_collector()
def killall(self):
if hasattr(self,'socketLog'):
self.socketLog.close()
if self.Server != None:
self.Server.alive = False
if self.Gc != None:
self.Gc.kill()
try:
self.Gc.nuke()
except exceptionTools.InterruptError:
pass
if self.PythonGarbageCollector != None:
self.PythonGarbageCollector.kill()
try:
self.PythonGarbageCollector.nuke()
except exceptionTools.InterruptError:
pass
def append_eos(self, data):
return str(len(data)) + \
self.answers['eos'] + \
data
def setup_ssl(self):
do_ssl = False
if self.kwds.has_key('ssl'):
do_ssl = self.kwds.pop('ssl')
if not do_ssl:
return
try:
from OpenSSL import SSL, crypto
except ImportError, e:
self.updateProgress('Unable to load OpenSSL, error: %s' % (repr(e),))
return
self.SSL_exceptions['WantReadError'] = SSL.WantReadError
self.SSL_exceptions['Error'] = SSL.Error
self.SSL_exceptions['WantWriteError'] = SSL.WantWriteError
self.SSL_exceptions['WantX509LookupError'] = SSL.WantX509LookupError
self.SSL_exceptions['ZeroReturnError'] = SSL.ZeroReturnError
self.SSL_exceptions['SysCallError'] = SSL.SysCallError
self.SSL['m'] = SSL
self.SSL['crypto'] = crypto
self.SSL['key'] = etpConst['socket_service']['ssl_key']
self.SSL['cert'] = etpConst['socket_service']['ssl_cert']
self.SSL['ca_cert'] = etpConst['socket_service']['ssl_ca_cert']
self.SSL['ca_pkey'] = etpConst['socket_service']['ssl_ca_pkey']
# change port
self.port = etpConst['socket_service']['ssl_port']
self.SSL['not_before'] = 0
self.SSL['not_after'] = 60*60*24*365*5 # 5 years
self.SSL['serial'] = 0
self.SSL['digest'] = 'md5'
if not (os.path.isfile(self.SSL['ca_cert']) and \
os.path.isfile(self.SSL['ca_pkey']) and \
os.path.isfile(self.SSL['key']) and \
os.path.isfile(self.SSL['cert'])):
self.create_ca_server_certs(
self.SSL['serial'],
self.SSL['digest'],
self.SSL['not_before'],
self.SSL['not_after'],
self.SSL['ca_pkey'],
self.SSL['ca_cert'],
self.SSL['key'],
self.SSL['cert']
)
os.chmod(self.SSL['ca_cert'],0644)
try:
os.chown(self.SSL['ca_cert'],-1,0)
except OSError:
pass
os.chmod(self.SSL['ca_pkey'],0600)
try:
os.chown(self.SSL['ca_pkey'],-1,0)
except OSError:
pass
os.chmod(self.SSL['key'],0600)
try:
os.chown(self.SSL['key'],-1,0)
except OSError:
pass
os.chmod(self.SSL['cert'],0644)
try:
os.chown(self.SSL['cert'],-1,0)
except OSError:
pass
def create_ca_server_certs(self, serial, digest, not_before, not_after, ca_pkey_dest, ca_cert_dest, server_key, server_cert):
mycn = 'Entropy Repository Service'
cakey = self.create_ssl_key_pair(self.SSL['crypto'].TYPE_RSA, 1024)
careq = self.create_ssl_certificate_request(cakey, digest, CN = mycn)
cert = self.SSL['crypto'].X509()
cert.set_serial_number(serial)
cert.gmtime_adj_notBefore(not_before)
cert.gmtime_adj_notAfter(not_after)
cert.set_issuer(careq.get_subject())
cert.set_subject(careq.get_subject())
cert.sign(cakey, digest)
# now create server key + cert
s_pkey = self.create_ssl_key_pair(self.SSL['crypto'].TYPE_RSA, 1024)
s_req = self.create_ssl_certificate_request(s_pkey, digest, CN = mycn)
s_cert = self.SSL['crypto'].X509()
s_cert.set_serial_number(serial+1)
s_cert.gmtime_adj_notBefore(not_before)
s_cert.gmtime_adj_notAfter(not_after)
s_cert.set_issuer(cert.get_subject())
s_cert.set_subject(s_req.get_subject())
s_cert.set_pubkey(s_req.get_pubkey())
s_cert.sign(cakey, digest)
# write CA
if os.path.isfile(ca_pkey_dest):
shutil.move(ca_pkey_dest,ca_pkey_dest+".moved")
f = open(ca_pkey_dest,"w")
f.write(self.SSL['crypto'].dump_privatekey(self.SSL['crypto'].FILETYPE_PEM, cakey))
f.flush()
f.close()
if os.path.isfile(ca_cert_dest):
shutil.move(ca_cert_dest,ca_cert_dest+".moved")
f = open(ca_cert_dest,"w")
f.write(self.SSL['crypto'].dump_certificate(self.SSL['crypto'].FILETYPE_PEM, cert))
f.flush()
f.close()
if os.path.isfile(server_key):
shutil.move(server_key,server_key+".moved")
# write server
f = open(server_key,"w")
f.write(self.SSL['crypto'].dump_privatekey(self.SSL['crypto'].FILETYPE_PEM, s_pkey))
f.flush()
f.close()
if os.path.isfile(server_cert):
shutil.move(server_cert,server_cert+".moved")
f = open(server_cert,"w")
f.write(self.SSL['crypto'].dump_certificate(self.SSL['crypto'].FILETYPE_PEM, s_cert))
f.flush()
f.close()
def create_ssl_key_pair(self, keytype, bits):
pkey = self.SSL['crypto'].PKey()
pkey.generate_key(keytype, bits)
return pkey
def create_ssl_certificate_request(self, pkey, digest, **name):
req = self.SSL['crypto'].X509Req()
subj = req.get_subject()
for (key,value) in name.items():
setattr(subj, key, value)
req.set_pubkey(pkey)
req.sign(pkey, digest)
return req
def setup_external_command_classes(self):
if self.kwds.has_key('external_cmd_classes'):
ext_commands = self.kwds.pop('external_cmd_classes')
if not isinstance(ext_commands,list):
raise exceptionTools.InvalidDataType("InvalidDataType: external_cmd_classes must be a list")
self.command_classes += ext_commands
def setup_commands(self):
identifiers = set()
for myclass in self.command_classes:
myargs = []
mykwargs = {}
if isinstance(myclass,tuple) or isinstance(myclass,list):
if len(myclass) > 2:
mykwargs = myclass[2]
if len(myclass) > 1:
myargs = myclass[1]
myclass = myclass[0]
myinst = myclass(self, *myargs, **mykwargs)
if str(myinst) in identifiers:
raise exceptionTools.PermissionDenied("PermissionDenied: another command instance is owning this name")
identifiers.add(str(myinst))
self.command_instances.append(myinst)
# now register
myinst.register( self.valid_commands,
self.no_acked_commands,
self.termination_commands,
self.initialization_commands,
self.login_pass_commands,
self.no_session_commands,
self.raw_commands,
self.config_commands
)
def disable_commands(self):
for cmd in self.disabled_commands:
if cmd in self.valid_commands:
self.valid_commands.pop(cmd)
if cmd in self.no_acked_commands:
self.no_acked_commands.remove(cmd)
if cmd in self.termination_commands:
self.termination_commands.remove(cmd)
if cmd in self.initialization_commands:
self.initialization_commands.remove(cmd)
if cmd in self.login_pass_commands:
self.login_pass_commands.remove(cmd)
if cmd in self.no_session_commands:
self.no_session_commands.remove(cmd)
if cmd in self.raw_commands:
self.raw_commands.remove(cmd)
if cmd in self.config_commands:
self.config_commands.remove(cmd)
def start_local_output_interface(self):
if self.kwds.has_key('sock_output'):
outputIntf = self.kwds.pop('sock_output')
self.__output = outputIntf
def setup_authenticator(self):
# lock, if perhaps some implementations need it
self.AuthenticatorLock = self.threading.Lock()
auth_inst = (self.BasicPamAuthenticator, [self], {},) # authentication class, args, keywords
# external authenticator
if self.kwds.has_key('sock_auth'):
authIntf = self.kwds.pop('sock_auth')
if type(authIntf) is tuple:
if len(authIntf) == 3:
auth_inst = authIntf[:]
else:
raise exceptionTools.IncorrectParameter("IncorrectParameter: wront authentication interface specified")
else:
raise exceptionTools.IncorrectParameter("IncorrectParameter: wront authentication interface specified")
# initialize authenticator
self.AuthenticatorInst = (auth_inst[0],[self]+auth_inst[1],auth_inst[2],)
def start_python_garbage_collector(self):
self.PythonGarbageCollector = self.entropyTools.TimeScheduled( self.python_garbage_collect, 3600 )
self.PythonGarbageCollector.setName("Garbage_Collector::"+str(abs(hash(os.urandom(20)))))
self.PythonGarbageCollector.exc = exceptionTools.InterruptError('InterruptError: interrupted')
self.PythonGarbageCollector.accurate = False
self.PythonGarbageCollector.start()
def start_session_garbage_collector(self):
self.Gc = self.entropyTools.TimeScheduled( self.gc_clean, 5 )
self.Gc.setName("Socket_GC::"+str(abs(hash(os.urandom(20)))))
self.Gc.exc = exceptionTools.InterruptError('InterruptError: interrupted')
self.Gc.start()
def python_garbage_collect(self):
self.gc.collect()
self.gc.collect()
self.gc.collect()
def gc_clean(self):
if not self.sessions:
return
self.SessionsLock.acquire()
try:
for session_id in self.sessions.keys():
sess_time = self.sessions[session_id]['t']
is_running = self.sessions[session_id]['running']
auth_uid = self.sessions[session_id]['auth_uid'] # is kept alive?
if (is_running) or (auth_uid == -1):
if auth_uid == -1:
self.updateProgress('not killing session %s, since it is kept alive by auth_uid=-1' % (session_id,) )
continue
cur_time = time.time()
ttl = self.session_ttl
check_time = sess_time + ttl
if cur_time > check_time:
self.updateProgress('killing session %s, ttl: %ss: no activity' % (session_id,ttl,) )
self.destroy_session(session_id, lock = False)
finally:
self.SessionsLock.release()
def setup_hostname(self):
if self.hostname:
try:
self.hostname = self.get_ip_address(self.hostname)
except IOError: # it isn't a device name
pass
def get_ip_address(self, ifname):
import fcntl
import struct
mysock = self.socket.socket ( self.socket.AF_INET, self.socket.SOCK_STREAM )
return self.socket.inet_ntoa(fcntl.ioctl(mysock.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
def get_new_session(self, ip_address = None):
self.SessionsLock.acquire()
try:
if len(self.sessions) > self.threads:
# fuck!
return "0"
rng = str(abs(hash(os.urandom(20))))
while rng in self.sessions:
rng = str(abs(hash(os.urandom(20))))
self.sessions[rng] = {}
self.sessions[rng]['running'] = False
self.sessions[rng]['auth_uid'] = None
self.sessions[rng]['admin'] = False
self.sessions[rng]['moderator'] = False
self.sessions[rng]['user'] = False
self.sessions[rng]['developer'] = False
self.sessions[rng]['compression'] = None
self.sessions[rng]['stream_mode'] = False
try:
self.sessions[rng]['stream_path'] = self.entropyTools.getRandomTempFile()
except (IOError,OSError,):
self.sessions[rng]['stream_path'] = ''
self.sessions[rng]['t'] = time.time()
self.sessions[rng]['ip_address'] = ip_address
return rng
finally:
self.SessionsLock.release()
def update_session_time(self, session):
self.SessionsLock.acquire()
try:
if self.sessions.has_key(session):
self.sessions[session]['t'] = time.time()
self.updateProgress('session time updated for %s' % (session,) )
finally:
self.SessionsLock.release()
def set_session_running(self, session):
self.SessionsLock.acquire()
try:
if self.sessions.has_key(session):
self.sessions[session]['running'] = True
finally:
self.SessionsLock.release()
def unset_session_running(self, session):
self.SessionsLock.acquire()
try:
if self.sessions.has_key(session):
self.sessions[session]['running'] = False
finally:
self.SessionsLock.release()
def destroy_session(self, session, lock = True):
if lock:
self.SessionsLock.acquire()
try:
if self.sessions.has_key(session):
stream_path = self.sessions[session]['stream_path']
del self.sessions[session]
if os.path.isfile(stream_path) and os.access(stream_path,os.W_OK) and not os.path.islink(stream_path):
try:
os.remove(stream_path)
except OSError:
pass
return True
return False
finally:
if lock: self.SessionsLock.release()
def go(self):
self.socket.setdefaulttimeout(self.timeout)
while 1:
try:
self.Server = self.HostServer(
(self.hostname, self.port),
self.RequestHandler,
self.CommandProcessor(self),
self
)
break
except self.socket.error, e:
if e[0] == 98:
# Address already in use
self.updateProgress('address already in use (%s, port: %s), waiting 5 seconds...' % (self.hostname,self.port,))
time.sleep(5)
continue
else:
raise
self.updateProgress('server connected, listening on: %s, port: %s, timeout: %s' % (self.hostname,self.port,self.timeout,))
self.Server.serve_forever()
self.Gc.kill()
def store_rc(self, rc, session):
self.SessionsLock.acquire()
try:
if session in self.sessions:
if type(rc) in (list,tuple,):
rc_item = rc[:]
elif type(rc) in (set,frozenset,dict,):
rc_item = rc.copy()
else:
rc_item = rc
self.sessions[session]['rc'] = rc_item
finally:
self.SessionsLock.release()
def get_rc(self, session):
self.SessionsLock.acquire()
try:
if session in self.sessions:
return self.sessions[session].get('rc')
finally:
self.SessionsLock.release()
def transmit(self, channel, data):
if self.SSL:
mydata = self.append_eos(data)
encode_done = False
while 1:
try:
sent = channel.send(mydata)
if sent == len(mydata):
break
mydata = mydata[sent:]
except (self.SSL_exceptions['WantWriteError'],self.SSL_exceptions['WantReadError']):
time.sleep(0.2)
continue
except UnicodeEncodeError:
if encode_done:
raise
mydata = mydata.encode('utf-8')
encode_done = True
continue
else:
channel.sendall(self.append_eos(data))
def updateProgress(self, *args, **kwargs):
message = args[0]
if message != self.last_print:
self.socketLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,str(args[0]))
if self.__output != None and self.stdout_logging:
self.__output.updateProgress(*args,**kwargs)
self.last_print = message
class SocketUrlFetcher(urlFetcher):
import entropyTools
# reimplementing updateProgress
def updateProgress(self):
kbprogress = " (%s/%s kB @ %s)" % (
str(round(float(self.downloadedsize)/1024,1)),
str(round(self.remotesize,1)),
str(self.entropyTools.bytesIntoHuman(self.datatransfer))+"/sec",
)
mytxt = _("Fetch")
self.progress( mytxt+": "+str((round(float(self.average),1)))+"%"+kbprogress, back = True )
class ServerInterface(TextInterface):
def __init__(self, default_repository = None, save_repository = False, community_repo = False):
if etpConst['uid'] != 0:
mytxt = _("Entropy ServerInterface must be run as root")
raise exceptionTools.PermissionDenied("PermissionDenied: %s" % (mytxt,))
self.serverLog = LogFile(
level = etpConst['entropyloglevel'],
filename = etpConst['entropylogfile'],
header = "[server]"
)
self.default_repository = default_repository
if self.default_repository == None:
self.default_repository = etpConst['officialserverrepositoryid']
if self.default_repository in etpConst['server_repositories']:
self.ensure_paths(self.default_repository)
self.migrate_repository_databases_to_new_branched_path()
self.community_repo = community_repo
self.dbapi2 = dbapi2 # export for third parties
# settings
etpSys['serverside'] = True
self.indexing = False
self.xcache = False
self.MirrorsService = None
self.FtpInterface = FtpInterface
self.rssFeed = rssFeed
self.serverDbCache = {}
self.repository_treeupdate_digests = {}
self.package_match_validator_cache = {}
self.settings_to_backup = []
self.do_save_repository = save_repository
self.rssMessages = {
'added': {},
'removed': {},
'commitmessage': "",
'light': {},
}
if self.default_repository not in etpConst['server_repositories']:
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (
self.default_repository,
_("repository not configured"),
)
)
if etpConst['clientserverrepoid'] in etpConst['server_repositories']:
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (
etpConst['clientserverrepoid'],
_("protected repository id, can't use this, sorry dude..."),
)
)
if self.community_repo:
self.add_client_database_to_repositories()
self.switch_default_repository(self.default_repository)
def destroy(self):
if hasattr(self,'serverLog'):
self.serverLog.close()
if hasattr(self,'ClientService'):
self.ClientService.destroy()
self.close_server_databases()
def __del__(self):
self.destroy()
def ensure_paths(self, repo):
upload_dir = os.path.join(self.get_local_upload_directory(repo),etpConst['branch'])
db_dir = self.get_local_database_dir(repo)
for mydir in [upload_dir,db_dir]:
if (not os.path.isdir(mydir)) and (not os.path.lexists(mydir)):
os.makedirs(mydir)
const_setup_perms(mydir,etpConst['entropygid'])
# FIXME: this will be removed in future, creation date: 2008-10-08
def migrate_repository_databases_to_new_branched_path(self):
migrated_filename = '.branch_migrated'
for repoid in etpConst['server_repositories'].keys():
if repoid == etpConst['clientserverrepoid']: continue
mydir = etpConst['server_repositories'][repoid]['database_dir']
if not os.path.isdir(mydir): # empty ?
continue
migrated_filepath = os.path.join(mydir,migrated_filename)
if os.path.isfile(migrated_filepath):
continue
my_branched_dir = self.get_local_database_dir(repoid)
if os.path.isdir(my_branched_dir): # wtf? do not touch
continue
self.updateProgress(
"[%s:%s] %s: %s, %s: %s" % (
brown("repo"),
purple(repoid),
_("migrating database path from"),
brown(mydir),
_('to'),
brown(my_branched_dir),
),
importance = 1,
type = "info",
header = bold(" @@ ")
)
repo_files = [os.path.join(mydir,x) for x in os.listdir(mydir) if \
(os.path.isfile(os.path.join(mydir,x)) and \
os.access(os.path.join(mydir,x),os.W_OK))
]
os.makedirs(my_branched_dir)
const_setup_perms(my_branched_dir,etpConst['entropygid'])
for repo_file in repo_files:
repo_filename = os.path.basename(repo_file)
shutil.move(repo_file,os.path.join(my_branched_dir,repo_filename))
f = open(migrated_filepath,"w")
f.write("done\n")
f.flush()
f.close()
def add_client_database_to_repositories(self):
etpConst['server_repositories'][etpConst['clientserverrepoid']] = {}
mydata = {}
mydata['description'] = "Community Repositories System Database"
mydata['mirrors'] = []
mydata['community'] = False
etpConst['server_repositories'][etpConst['clientserverrepoid']].update(mydata)
def setup_services(self):
self.setup_entropy_settings()
if hasattr(self,'ClientService'):
self.ClientService.destroy()
self.ClientService = EquoInterface(
indexing = self.indexing,
xcache = self.xcache,
repo_validation = False,
noclientdb = 1
)
self.Cacher = self.ClientService.Cacher
self.ClientService.updateProgress = self.updateProgress
self.ClientService.FtpInterface = self.FtpInterface
self.SystemSettings = self.ClientService.SystemSettings
self.validRepositories = self.ClientService.validRepositories
self.entropyTools = self.ClientService.entropyTools
self.dumpTools = self.ClientService.dumpTools
self.QA = self.ClientService.QA
self.backup_entropy_settings()
self.SpmService = self.ClientService.Spm()
self.MirrorsService = ServerMirrorsInterface(self)
def setup_entropy_settings(self, repo = None):
backup_list = [
'etpdatabaseclientfilepath',
'clientdbid',
'officialserverrepositoryid'
]
for setting in backup_list:
if setting not in self.settings_to_backup:
self.settings_to_backup.append(setting)
# setup client database
if not self.community_repo:
etpConst['etpdatabaseclientfilepath'] = self.get_local_database_file(repo)
etpConst['clientdbid'] = etpConst['serverdbid']
const_createWorkingDirectories()
def close_server_databases(self):
if hasattr(self,'serverDbCache'):
for item in self.serverDbCache:
try:
self.serverDbCache[item].closeDB()
except self.dbapi2.ProgrammingError: # already closed?
pass
self.serverDbCache.clear()
def close_server_database(self, dbinstance):
found = None
for item in self.serverDbCache:
if dbinstance == self.serverDbCache[item]:
found = item
break
if found:
instance = self.serverDbCache.pop(found)
instance.closeDB()
def get_available_repositories(self):
return etpConst['server_repositories'].copy()
def switch_default_repository(self, repoid, save = None, handle_uninitialized = True):
# avoid setting __default__ as default server repo
if repoid == etpConst['clientserverrepoid']:
return
if save == None:
save = self.do_save_repository
if repoid not in etpConst['server_repositories']:
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (
repoid,
_("repository not configured"),
)
)
self.close_server_databases()
etpConst['officialserverrepositoryid'] = repoid
self.default_repository = repoid
self.setup_services()
if save:
self.save_default_repository(repoid)
self.setup_community_repositories_settings()
self.show_interface_status()
if handle_uninitialized:
self.handle_uninitialized_repository(repoid)
def setup_community_repositories_settings(self):
if self.community_repo:
for repoid in etpConst['server_repositories']:
etpConst['server_repositories'][repoid]['community'] = True
def handle_uninitialized_repository(self, repoid):
if not self.is_repository_initialized(repoid):
mytxt = blue("%s.") % (_("Your default repository is not initialized"),)
self.updateProgress(
"[%s:%s] %s" % (
brown("repo"),
purple(repoid),
mytxt,
),
importance = 1,
type = "warning",
header = darkred(" !!! ")
)
answer = self.askQuestion(_("Do you want to initialize your default repository ?"))
if answer == "No":
mytxt = red("%s.") % (_("You have taken the risk to continue with an uninitialized repository"),)
self.updateProgress(
"[%s:%s] %s" % (
brown("repo"),
purple(repoid),
mytxt,
),
importance = 1,
type = "warning",
header = darkred(" !!! ")
)
else:
# move empty database for security sake
dbfile = self.get_local_database_file(repoid)
if os.path.isfile(dbfile):
shutil.move(dbfile,dbfile+".backup")
self.initialize_server_database(empty = True, repo = repoid, warnings = False)
def show_interface_status(self):
type_txt = _("server-side repository")
if self.community_repo:
type_txt = _("community repository")
mytxt = _("Entropy Server Interface Instance on repository") # ..on repository: <repository_name>
self.updateProgress(
blue("%s: %s, %s: %s (%s: %s)" % (
mytxt,
red(self.default_repository),
_("current branch"),
darkgreen(etpConst['branch']),
purple(_("type")),
bold(type_txt),
)
),
importance = 2,
type = "info",
header = red(" @@ ")
)
repos = etpConst['server_repositories'].keys()
mytxt = blue("%s:") % (_("Currently configured repositories"),) # ...: <list>
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" @@ ")
)
for repo in repos:
self.updateProgress(
darkgreen(repo),
importance = 0,
type = "info",
header = brown(" # ")
)
def save_default_repository(self, repoid):
# avoid setting __default__ as default server repo
if repoid == etpConst['clientserverrepoid']:
return
if os.path.isfile(etpConst['serverconf']):
f = open(etpConst['serverconf'],"r")
content = f.readlines()
f.close()
content = [x.strip() for x in content]
found = False
new_content = []
for line in content:
if line.strip().startswith("officialserverrepositoryid|"):
line = "officialserverrepositoryid|%s" % (repoid,)
found = True
new_content.append(line)
if not found:
new_content.append("officialserverrepositoryid|%s" % (repoid,))
f = open(etpConst['serverconf']+".save_default_repo_tmp","w")
for line in new_content:
f.write(line+"\n")
f.flush()
f.close()
shutil.move(etpConst['serverconf']+".save_default_repo_tmp",etpConst['serverconf'])
else:
f = open(etpConst['serverconf'],"w")
f.write("officialserverrepositoryid|%s\n" % (repoid,))
f.flush()
f.close()
def toggle_repository(self, repoid, enable = True):
# avoid setting __default__ as default server repo
if repoid == etpConst['clientserverrepoid']:
return False
if not os.path.isfile(etpConst['serverconf']):
return None
f = open(etpConst['serverconf'])
tmpfile = etpConst['serverconf']+".switch"
mycontent = [x.strip() for x in f.readlines()]
f.close()
f = open(tmpfile,"w")
st = "repository|%s" % (repoid,)
status = False
for line in mycontent:
if enable:
if (line.find(st) != -1) and line.startswith("#") and (len(line.split("|")) == 5):
line = line[1:]
status = True
else:
if (line.find(st) != -1) and not line.startswith("#") and (len(line.split("|")) == 5):
line = "#"+line
status = True
f.write(line+"\n")
f.flush()
f.close()
shutil.move(tmpfile,etpConst['serverconf'])
if status:
self.close_server_databases()
const_readServerSettings()
self.setup_services()
self.show_interface_status()
return status
def backup_entropy_settings(self):
for setting in self.settings_to_backup:
self.ClientService.backup_setting(setting)
def is_repository_initialized(self, repo):
def do_validate(dbc):
try:
dbc.validateDatabase()
return True
except exceptionTools.SystemDatabaseError:
return False
dbc = self.openServerDatabase(just_reading = True, repo = repo)
valid = do_validate(dbc)
self.close_server_database(dbc)
if not valid: # check online?
dbc = self.openServerDatabase(read_only = False, no_upload = True, repo = repo, is_new = True)
valid = do_validate(dbc)
self.close_server_database(dbc)
return valid
def doServerDatabaseSyncLock(self, repo, no_upload):
if repo == None:
repo = self.default_repository
# check if the database is locked locally
lock_file = self.MirrorsService.get_database_lockfile(repo)
if os.path.isfile(lock_file):
self.updateProgress(
red(_("Entropy database is already locked by you :-)")),
importance = 1,
type = "info",
header = red(" * ")
)
else:
# check if the database is locked REMOTELY
mytxt = "%s ..." % (_("Locking and Syncing Entropy database"),)
self.updateProgress(
red(mytxt),
importance = 1,
type = "info",
header = red(" * "),
back = True
)
for uri in self.get_remote_mirrors(repo):
given_up = self.MirrorsService.mirror_lock_check(uri, repo = repo)
if given_up:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
mytxt = "%s:" % (_("Mirrors status table"),)
self.updateProgress(
darkgreen(mytxt),
importance = 1,
type = "info",
header = brown(" * ")
)
dbstatus = self.MirrorsService.get_mirrors_lock(repo = repo)
for db in dbstatus:
db[1] = green(_("Unlocked"))
if (db[1]):
db[1] = red(_("Locked"))
db[2] = green(_("Unlocked"))
if (db[2]):
db[2] = red(_("Locked"))
crippled_uri = self.entropyTools.extractFTPHostFromUri(db[0])
self.updateProgress(
"%s: [%s: %s] [%s: %s]" % (
bold(crippled_uri),
brown(_("database")),
db[1],
brown(_("download")),
db[2],
),
importance = 1,
type = "info",
header = "\t"
)
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: %s %s" % (
_("cannot lock mirror"),
crippled_uri,
)
)
# if we arrive here, it is because all the mirrors are unlocked
self.MirrorsService.lock_mirrors(True, repo = repo)
self.MirrorsService.sync_databases(no_upload, repo = repo)
def openServerDatabase(
self,
read_only = True,
no_upload = True,
just_reading = False,
repo = None,
indexing = True,
warnings = True,
do_cache = True,
use_branch = None,
lock_remote = True,
is_new = False
):
if repo == None:
repo = self.default_repository
if repo == etpConst['clientserverrepoid'] and self.community_repo:
return self.ClientService.clientDbconn
if just_reading:
read_only = True
no_upload = True
t_ident = 1 # thread.get_ident() disabled for now
local_dbfile = self.get_local_database_file(repo, use_branch)
if do_cache:
cached = self.serverDbCache.get(
( etpConst['systemroot'],
local_dbfile,
read_only,
no_upload,
just_reading,
repo,
t_ident,
use_branch,
lock_remote,
)
)
if cached != None:
return cached
if not os.path.isdir(os.path.dirname(local_dbfile)):
os.makedirs(os.path.dirname(local_dbfile))
if (not read_only) and (lock_remote):
self.doServerDatabaseSyncLock(repo, no_upload)
conn = EntropyDatabaseInterface(
readOnly = read_only,
dbFile = local_dbfile,
noUpload = no_upload,
OutputInterface = self,
ServiceInterface = self,
dbname = etpConst['serverdbid']+repo,
useBranch = use_branch,
lockRemote = lock_remote
)
valid = True
try:
conn.validateDatabase()
except exceptionTools.SystemDatabaseError:
valid = False
# verify if we need to update the database to sync
# with portage updates, we just ignore being readonly in the case
if (repo not in etpConst['server_treeupdatescalled']) and (not just_reading):
# sometimes, when filling a new server db, we need to avoid tree updates
if valid:
conn.serverUpdatePackagesData()
elif warnings and not is_new:
mytxt = _( "Entropy database is probably corrupted! I won't stop you here btw...")
self.updateProgress(
darkred(mytxt),
importance = 1,
type = "warning",
header = bold(" !!! ")
)
if not read_only and valid and indexing:
self.updateProgress(
"[repo:%s|%s] %s" % (
blue(repo),
red(_("database")),
blue(_("indexing database")),
),
importance = 1,
type = "info",
header = brown(" @@ "),
back = True
)
conn.createAllIndexes()
if do_cache:
# !!! also cache just_reading otherwise there will be
# real issues if the connection is opened several times
self.serverDbCache[(
etpConst['systemroot'],
local_dbfile,
read_only,
no_upload,
just_reading,
repo,
t_ident,
use_branch,
lock_remote
)] = conn
# auto-update package sets
if (not read_only) and (not is_new):
cur_sets = conn.retrievePackageSets()
sys_sets = self.get_configured_package_sets(repo)
if cur_sets != sys_sets:
self.update_database_package_sets(repo, dbconn = conn)
conn.commitChanges()
return conn
def deps_tester(self):
server_repos = etpConst['server_repositories'].keys()
installed_packages = set()
for repo in server_repos:
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
installed_packages |= set([(x,repo) for x in dbconn.listAllIdpackages()])
deps_not_satisfied = set()
length = str((len(installed_packages)))
count = 0
mytxt = _("Checking")
for pkgdata in installed_packages:
count += 1
idpackage = pkgdata[0]
repo = pkgdata[1]
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
atom = dbconn.retrieveAtom(idpackage)
if atom == None:
continue
self.updateProgress(
darkgreen(mytxt)+" "+bold(atom),
importance = 0,
type = "info",
back = True,
count = (count,length),
header = darkred(" @@ ")
)
xdeps = dbconn.retrieveDependencies(idpackage)
for xdep in xdeps:
xmatch = self.atomMatch(xdep)
if xmatch[0] == -1:
deps_not_satisfied.add(xdep)
return deps_not_satisfied
def dependencies_test(self):
mytxt = "%s %s" % (blue(_("Running dependencies test")),red("..."))
self.updateProgress(
mytxt,
importance = 2,
type = "info",
header = red(" @@ ")
)
server_repos = etpConst['server_repositories'].keys()
deps_not_matched = self.deps_tester()
if deps_not_matched:
crying_atoms = {}
for atom in deps_not_matched:
for repo in server_repos:
dbconn = self.openServerDatabase(just_reading = True, repo = repo)
riddep = dbconn.searchDependency(atom)
if riddep == -1:
continue
if riddep != -1:
ridpackages = dbconn.searchIdpackageFromIddependency(riddep)
for i in ridpackages:
iatom = dbconn.retrieveAtom(i)
if not crying_atoms.has_key(atom):
crying_atoms[atom] = set()
crying_atoms[atom].add((iatom,repo))
mytxt = blue("%s:") % (_("These are the dependencies not found"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" @@ ")
)
mytxt = "%s:" % (_("Needed by"),)
for atom in deps_not_matched:
self.updateProgress(
red(atom),
importance = 1,
type = "info",
header = blue(" # ")
)
if crying_atoms.has_key(atom):
self.updateProgress(
red(mytxt),
importance = 0,
type = "info",
header = blue(" # ")
)
for x , myrepo in crying_atoms[atom]:
self.updateProgress(
"[%s:%s] %s" % (blue(_("by repo")),darkred(myrepo),darkgreen(x),),
importance = 0,
type = "info",
header = blue(" # ")
)
else:
mytxt = blue(_("Every dependency is satisfied. It's all fine."))
self.updateProgress(
mytxt,
importance = 2,
type = "info",
header = red(" @@ ")
)
return deps_not_matched
def libraries_test(self, get_files = False, repo = None):
# load db
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
packagesMatched, brokenexecs, status = self.ClientService.libraries_test(dbconn = dbconn, broken_symbols = False)
if status != 0:
return 1,None
if get_files:
return 0,brokenexecs
if (not brokenexecs) and (not packagesMatched):
mytxt = "%s." % (_("System is healthy"),)
self.updateProgress(
blue(mytxt),
importance = 2,
type = "info",
header = red(" @@ ")
)
return 0,None
mytxt = "%s..." % (_("Matching libraries with Spm, please wait"),)
self.updateProgress(
blue(mytxt),
importance = 1,
type = "info",
header = red(" @@ ")
)
packages = self.SpmService.query_belongs_multiple(brokenexecs)
if packages:
mytxt = "%s:" % (_("These are the matched packages"),)
self.updateProgress(
red(mytxt),
importance = 1,
type = "info",
header = red(" @@ ")
)
for package_slot in packages:
self.updateProgress(
blue(unicode(package_slot)),
importance = 0,
type = "info",
header = red(" # ")
)
for filename in sorted(list(packages[package_slot])):
self.updateProgress(
blue(filename),
importance = 0,
type = "info",
header = brown(" => ")
)
# print string
pkgstring = ' '.join(["%s:%s" % (self.entropyTools.dep_getkey(x[0]),x[1],) for x in sorted(packages.keys())])
mytxt = "%s: %s" % (darkgreen(_("Packages string")),pkgstring,)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" @@ ")
)
else:
self.updateProgress(
red(_("No matched packages")),
importance = 1,
type = "info",
header = red(" @@ ")
)
return 0,packages
def orphaned_spm_packages_test(self):
mytxt = "%s %s" % (blue(_("Running orphaned SPM packages test")),red("..."),)
self.updateProgress(
mytxt,
importance = 2,
type = "info",
header = red(" @@ ")
)
installed_packages, length = self.SpmService.get_installed_packages()
not_found = {}
count = 0
for installed_package in installed_packages:
count += 1
self.updateProgress(
"%s: %s" % (darkgreen(_("Scanning package")),brown(installed_package),),
importance = 0,
type = "info",
back = True,
count = (count,length),
header = darkred(" @@ ")
)
key, slot = self.entropyTools.dep_getkey(installed_package),self.SpmService.get_installed_package_slot(installed_package)
pkg_atom = "%s:%s" % (key,slot,)
tree_atom = self.SpmService.get_best_atom(pkg_atom)
if not tree_atom:
not_found[installed_package] = pkg_atom
self.updateProgress(
"%s: %s" % (blue(pkg_atom),darkred(_("not found anymore")),),
importance = 0,
type = "warning",
count = (count,length),
header = darkred(" @@ ")
)
if not_found:
not_found_list = ' '.join([not_found[x] for x in sorted(not_found.keys())])
self.updateProgress(
"%s: %s" % (blue(_("Packages string")),not_found_list,),
importance = 0,
type = "warning",
count = (count,length),
header = darkred(" @@ ")
)
return not_found
def depends_table_initialize(self, repo = None):
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
dbconn.regenerateDependsTable()
dbconn.taintDatabase()
dbconn.commitChanges()
def create_empty_database(self, dbpath = None, repo = None):
if dbpath == None:
dbpath = self.get_local_database_file(repo)
dbdir = os.path.dirname(dbpath)
if not os.path.isdir(dbdir):
os.makedirs(dbdir)
mytxt = red("%s ...") % (_("Initializing an empty database file with Entropy structure"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = darkgreen(" * "),
back = True
)
dbconn = self.ClientService.openGenericDatabase(dbpath)
dbconn.initializeDatabase()
dbconn.commitChanges()
dbconn.closeDB()
mytxt = "%s %s %s." % (red(_("Entropy database file")),bold(dbpath),red(_("successfully initialized")),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = darkgreen(" * ")
)
def tag_packages(self, package_tag, idpackages, repo = None, ask = True):
# check package_tag "no spaces"
try:
package_tag = str(package_tag)
if " " in package_tag: raise ValueError
except (UnicodeDecodeError,UnicodeEncodeError,ValueError,):
self.updateProgress(
"%s: %s" % (
blue(_("Invalid tag specified")),
package_tag,
),
importance = 1, type = "error", header = darkred(" !! ")
)
return 1, package_tag
if repo == None: repo = self.default_repository
# sanity check
invalid_atoms = []
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
for idpackage in idpackages:
ver_tag = dbconn.retrieveVersionTag(idpackage)
if ver_tag:
invalid_atoms.append(dbconn.retrieveAtom(idpackage))
if invalid_atoms:
self.updateProgress(
"%s: %s" % (
blue(_("These are the packages already tagged, cannot re-tag, action aborted")),
', '.join([darkred(unicode(x)) for x in invalid_atoms]),
),
importance = 1, type = "error", header = darkred(" !! ")
)
return 2, invalid_atoms
matches = [(x,repo) for x in idpackages]
status = 0
data = self.move_packages(
matches, to_repo = repo, from_repo = repo, ask = ask,
do_copy = True, new_tag = package_tag
)
return status, data
def move_packages(self, matches, to_repo, from_repo = None, ask = True, do_copy = False, new_tag = None):
if from_repo == None: from_repo = self.default_repository
switched = set()
# avoid setting __default__ as default server repo
if etpConst['clientserverrepoid'] in (to_repo,from_repo):
self.updateProgress(
"%s: %s" % (
blue(_("You cannot switch packages from/to your system database")),
red(etpConst['clientserverrepoid']),
),
importance = 2, type = "warning", header = darkred(" @@ ")
)
return switched
if not matches and from_repo:
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = from_repo)
matches = set( \
[(x,from_repo) for x in \
dbconn.listAllIdpackages()]
)
mytxt = _("Preparing to move selected packages to")
if do_copy:
mytxt = _("Preparing to copy selected packages to")
self.updateProgress(
"%s %s:" % (
blue(mytxt),
red(to_repo),
),
importance = 2,
type = "info",
header = red(" @@ ")
)
self.updateProgress(
"%s: %s" % (
bold(_("Note")),
red(_("all the old packages with conflicting scope will be removed from the destination repo unless injected")),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
new_tag_string = ''
if new_tag != None: new_tag_string = "[%s: %s]" % (darkgreen(_("new tag")),brown(new_tag),)
for match in matches:
repo = match[1]
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
self.updateProgress(
"[%s=>%s|%s] %s " % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(dbconn.retrieveAtom(match[0])),
) + new_tag_string,
importance = 0,
type = "info",
header = brown(" # ")
)
if ask:
rc = self.askQuestion(_("Would you like to continue ?"))
if rc == "No":
return switched
for idpackage, repo in matches:
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
match_branch = dbconn.retrieveBranch(idpackage)
match_atom = dbconn.retrieveAtom(idpackage)
package_filename = os.path.basename(dbconn.retrieveDownloadURL(idpackage))
self.updateProgress(
"[%s=>%s|%s] %s: %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(_("switching")),
darkgreen(match_atom),
),
importance = 0,
type = "info",
header = red(" @@ "),
back = True
)
# move binary file
from_file = os.path.join(self.get_local_packages_directory(repo),match_branch,package_filename)
if not os.path.isfile(from_file):
from_file = os.path.join(self.get_local_upload_directory(repo),match_branch,package_filename)
if not os.path.isfile(from_file):
self.updateProgress(
"[%s=>%s|%s] %s: %s -> %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
bold(_("cannot switch, package not found, skipping")),
darkgreen(),
red(from_file),
),
importance = 1,
type = "warning",
header = darkred(" !!! ")
)
continue
if new_tag != None:
match_category = dbconn.retrieveCategory(idpackage)
match_name = dbconn.retrieveName(idpackage)
match_version = dbconn.retrieveVersion(idpackage)
tagged_package_filename = self.entropyTools.create_package_filename(match_category, match_name, match_version, new_tag)
to_file = os.path.join(self.get_local_upload_directory(to_repo),match_branch,tagged_package_filename)
else:
to_file = os.path.join(self.get_local_upload_directory(to_repo),match_branch,package_filename)
if not os.path.isdir(os.path.dirname(to_file)):
os.makedirs(os.path.dirname(to_file))
copy_data = [
(from_file,to_file,),
(from_file+etpConst['packageshashfileext'],to_file+etpConst['packageshashfileext'],),
(from_file+etpConst['packagesexpirationfileext'],to_file+etpConst['packagesexpirationfileext'],)
]
for from_item,to_item in copy_data:
self.updateProgress(
"[%s=>%s|%s] %s: %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(_("moving file")),
darkgreen(os.path.basename(from_item)),
),
importance = 0,
type = "info",
header = red(" @@ "),
back = True
)
if os.path.isfile(from_item):
shutil.copy2(from_item,to_item)
self.updateProgress(
"[%s=>%s|%s] %s: %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(_("loading data from source database")),
darkgreen(repo),
),
importance = 0,
type = "info",
header = red(" @@ "),
back = True
)
# install package into destination db
data = dbconn.getPackageData(idpackage)
if new_tag != None:
data['versiontag'] = new_tag
todbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = to_repo)
self.updateProgress(
"[%s=>%s|%s] %s: %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(_("injecting data to destination database")),
darkgreen(to_repo),
),
importance = 0,
type = "info",
header = red(" @@ "),
back = True
)
new_idpackage, new_revision, new_data = todbconn.handlePackage(data)
del data
todbconn.commitChanges()
if not do_copy:
self.updateProgress(
"[%s=>%s|%s] %s: %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(_("removing entry from source database")),
darkgreen(repo),
),
importance = 0,
type = "info",
header = red(" @@ "),
back = True
)
# remove package from old db
dbconn.removePackage(idpackage)
dbconn.commitChanges()
self.updateProgress(
"[%s=>%s|%s] %s: %s" % (
darkgreen(repo),
darkred(to_repo),
brown(etpConst['branch']),
blue(_("successfully handled atom")),
darkgreen(match_atom),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
switched.add(match)
return switched
def package_injector(self, package_file, inject = False, repo = None):
if repo == None:
repo = self.default_repository
upload_dir = os.path.join(self.get_local_upload_directory(repo),etpConst['branch'])
if not os.path.isdir(upload_dir):
os.makedirs(upload_dir)
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
self.updateProgress(
red("[repo: %s] %s: %s" % (
darkgreen(repo),
_("adding package"),
bold(os.path.basename(package_file)),
)
),
importance = 1,
type = "info",
header = brown(" * "),
back = True
)
mydata = self.ClientService.extract_pkg_metadata(package_file, etpBranch = etpConst['branch'], inject = inject)
idpackage, revision, mydata = dbconn.handlePackage(mydata)
# set trashed counters
trashing_counters = set()
myserver_repos = etpConst['server_repositories'].keys()
for myrepo in myserver_repos:
mydbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = myrepo)
mylist = mydbconn.retrieve_packages_to_remove(
mydata['name'],
mydata['category'],
mydata['slot'],
mydata['injected']
)
for myitem in mylist:
trashing_counters.add(mydbconn.retrieveCounter(myitem))
for mycounter in trashing_counters:
dbconn.setTrashedCounter(mycounter)
# add package info to our current server repository
dbconn.removePackageFromInstalledTable(idpackage)
dbconn.addPackageToInstalledTable(idpackage,repo)
atom = dbconn.retrieveAtom(idpackage)
self.updateProgress(
"[repo:%s] %s: %s %s: %s" % (
darkgreen(repo),
blue(_("added package")),
darkgreen(atom),
blue(_("rev")), # as in revision
bold(str(revision)),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
download_url = self._setup_repository_package_filename(idpackage, repo = repo)
downloadfile = os.path.basename(download_url)
destination_path = os.path.join(upload_dir,downloadfile)
shutil.move(package_file,destination_path)
dbconn.commitChanges()
return idpackage,destination_path
# this function changes the final repository package filename
def _setup_repository_package_filename(self, idpackage, repo = None):
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
downloadurl = dbconn.retrieveDownloadURL(idpackage)
packagerev = dbconn.retrieveRevision(idpackage)
downloaddir = os.path.dirname(downloadurl)
downloadfile = os.path.basename(downloadurl)
# add revision
downloadfile = downloadfile[:-5]+"~%s%s" % (packagerev,etpConst['packagesext'],)
downloadurl = os.path.join(downloaddir,downloadfile)
# update url
dbconn.setDownloadURL(idpackage,downloadurl)
return downloadurl
def add_packages_to_repository(self, packages_data, ask = True, repo = None):
if repo == None:
repo = self.default_repository
mycount = 0
maxcount = len(packages_data)
idpackages_added = set()
to_be_injected = set()
myQA = self.QA()
missing_deps_taint = False
for package_filepath, inject in packages_data:
mycount += 1
self.updateProgress(
"[repo:%s] %s: %s" % (
darkgreen(repo),
blue(_("adding package")),
darkgreen(os.path.basename(package_filepath)),
),
importance = 1,
type = "info",
header = blue(" @@ "),
count = (mycount,maxcount,)
)
try:
# add to database
idpackage, destination_path = self.package_injector(
package_filepath,
inject = inject
)
idpackages_added.add(idpackage)
to_be_injected.add((idpackage,destination_path))
except Exception, e:
self.updateProgress(
"[repo:%s] %s: %s" % (
darkgreen(repo),
darkred(_("Exception caught, running injection and RDEPEND check before raising")),
darkgreen(unicode(e)),
),
importance = 1,
type = "error",
header = bold(" !!! "),
count = (mycount,maxcount,)
)
# reinit depends table
self.depends_table_initialize(repo)
if idpackages_added:
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
missing_deps_taint = myQA.scan_missing_dependencies(
idpackages_added,
dbconn,
ask = ask,
repo = repo,
self_check = True,
black_list = self.get_missing_dependencies_blacklist(repo = repo),
black_list_adder = self.add_missing_dependencies_blacklist_items
)
myQA.test_depends_linking(idpackages_added, dbconn, repo = repo)
if to_be_injected:
self.inject_database_into_packages(to_be_injected, repo = repo)
# reinit depends table
if missing_deps_taint:
self.depends_table_initialize(repo)
self.close_server_databases()
raise
# reinit depends table
self.depends_table_initialize(repo)
if idpackages_added:
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
missing_deps_taint = myQA.scan_missing_dependencies(
idpackages_added,
dbconn,
ask = ask,
repo = repo,
self_check = True,
black_list = self.get_missing_dependencies_blacklist(repo = repo),
black_list_adder = self.add_missing_dependencies_blacklist_items
)
myQA.test_depends_linking(idpackages_added, dbconn, repo = repo)
# reinit depends table
if missing_deps_taint:
self.depends_table_initialize(repo)
# inject database into packages
self.inject_database_into_packages(to_be_injected, repo = repo)
return idpackages_added
def inject_database_into_packages(self, injection_data, repo = None):
if repo == None:
repo = self.default_repository
# now inject metadata into tbz2 packages
self.updateProgress(
"[repo:%s] %s:" % (
darkgreen(repo),
blue(_("Injecting entropy metadata into built packages")),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
for idpackage,package_path in injection_data:
self.updateProgress(
"[repo:%s|%s] %s: %s" % (
darkgreen(repo),
brown(str(idpackage)),
blue(_("injecting entropy metadata")),
darkgreen(os.path.basename(package_path)),
),
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
data = dbconn.getPackageData(idpackage)
treeupdates_actions = dbconn.listAllTreeUpdatesActions()
dbpath = self.ClientService.inject_entropy_database_into_package(package_path, data, treeupdates_actions)
digest = self.entropyTools.md5sum(package_path)
# update digest
dbconn.setDigest(idpackage,digest)
self.entropyTools.createHashFile(package_path)
# remove garbage
os.remove(dbpath)
self.updateProgress(
"[repo:%s|%s] %s: %s" % (
darkgreen(repo),
brown(str(idpackage)),
blue(_("injection complete")),
darkgreen(os.path.basename(package_path)),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
dbconn.commitChanges()
def check_config_file_updates(self):
self.updateProgress(
"[%s] %s" % (
red(_("config files")), # something short please
blue(_("checking system")),
),
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
# scanning for config files not updated
scandata = self.ClientService.FileUpdates.scanfs(dcache = False)
if scandata:
self.updateProgress(
"[%s] %s" % (
red(_("config files")), # something short please
blue(_("there are configuration files not updated yet")),
),
importance = 1,
type = "error",
header = darkred(" @@ ")
)
for x in scandata:
self.updateProgress(
"%s" % ( brown(etpConst['systemroot']+scandata[x]['destination']) ),
importance = 1,
type = "info",
header = "\t"
)
return True
return False
def quickpkg(self, atom, storedir):
return self.SpmService.quickpkg(atom,storedir)
def remove_packages(self, idpackages, repo = None):
if repo == None:
repo = self.default_repository
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
for idpackage in idpackages:
atom = dbconn.retrieveAtom(idpackage)
self.updateProgress(
"[repo:%s] %s: %s" % (
darkgreen(repo),
blue(_("removing package")),
darkgreen(atom),
),
importance = 1,
type = "info",
header = brown(" @@ ")
)
dbconn.removePackage(idpackage)
self.close_server_database(dbconn)
self.updateProgress(
"[repo:%s] %s" % (
darkgreen(repo),
blue(_("removal complete")),
),
importance = 1,
type = "info",
header = brown(" @@ ")
)
def bump_database(self, repo = None):
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
dbconn.taintDatabase()
self.close_server_database(dbconn)
def get_remote_mirrors(self, repo = None):
if repo == None:
repo = self.default_repository
return etpConst['server_repositories'][repo]['mirrors'][:]
def get_remote_packages_relative_path(self, repo = None):
if repo == None:
repo = self.default_repository
return etpConst['server_repositories'][repo]['packages_relative_path']
def get_remote_database_relative_path(self, repo = None):
if repo == None:
repo = self.default_repository
return etpConst['server_repositories'][repo]['database_relative_path']
def get_local_database_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabasefile'])
def get_local_store_directory(self, repo = None):
if repo == None:
repo = self.default_repository
return etpConst['server_repositories'][repo]['store_dir']
def get_local_upload_directory(self, repo = None):
if repo == None:
repo = self.default_repository
return etpConst['server_repositories'][repo]['upload_dir']
def get_local_packages_directory(self, repo = None):
if repo == None:
repo = self.default_repository
return etpConst['server_repositories'][repo]['packages_dir']
def get_local_database_taint_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabasetaintfile'])
def get_local_database_revision_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabaserevisionfile'])
def get_local_database_ca_cert_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabasecacertfile'])
def get_local_database_server_cert_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabaseservercertfile'])
def get_local_database_mask_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabasemaskfile'])
def get_local_database_system_mask_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabasesytemmaskfile'])
def get_local_database_confl_tagged_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabaseconflictingtaggedfile'])
def get_local_database_licensewhitelist_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabaselicwhitelistfile'])
def get_local_database_rss_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['rss-name'])
def get_local_database_rsslight_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['rss-light-name'])
def get_local_database_notice_board_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['rss-notice-board'])
def get_local_database_treeupdates_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['etpdatabaseupdatefile'])
def get_local_database_sets_dir(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
return os.path.join(self.get_local_database_dir(repo, branch),etpConst['confsetsdirname'])
def get_local_database_dir(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
if branch == None:
branch = etpConst['branch']
return os.path.join(etpConst['server_repositories'][repo]['database_dir'],branch)
def get_missing_dependencies_blacklist_file(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
if branch == None:
branch = etpConst['branch']
return os.path.join(etpConst['server_repositories'][repo]['database_dir'],branch,etpConst['etpdatabasemissingdepsblfile'])
def get_missing_dependencies_blacklist(self, repo = None, branch = None):
if repo == None:
repo = self.default_repository
if branch == None:
branch = etpConst['branch']
wl_file = self.get_missing_dependencies_blacklist_file(repo, branch)
wl_data = []
if os.path.isfile(wl_file) and os.access(wl_file,os.R_OK):
f = open(wl_file,"r")
wl_data = [x.strip() for x in f.readlines() if x.strip() and not x.strip().startswith("#")]
f.close()
return set(wl_data)
def add_missing_dependencies_blacklist_items(self, items, repo = None, branch = None):
if repo == None:
repo = self.default_repository
if branch == None:
branch = etpConst['branch']
wl_file = self.get_missing_dependencies_blacklist_file(repo, branch)
wl_dir = os.path.dirname(wl_file)
if not (os.path.isdir(wl_dir) and os.access(wl_dir,os.W_OK)):
return
if os.path.isfile(wl_file) and not os.access(wl_file,os.W_OK):
return
f = open(wl_file,"a+")
f.write('\n'.join(items)+'\n')
f.flush()
f.close()
def get_local_database_revision(self, repo = None):
if repo == None:
repo = self.default_repository
dbrev_file = self.get_local_database_revision_file(repo)
if os.path.isfile(dbrev_file):
f = open(dbrev_file)
rev = f.readline().strip()
f.close()
try:
rev = int(rev)
except ValueError:
self.updateProgress(
"[repo:%s] %s: %s - %s" % (
darkgreen(repo),
blue(_("invalid database revision")),
bold(rev),
blue(_("defaulting to 0")),
),
importance = 2,
type = "error",
header = darkred(" !!! ")
)
rev = 0
return rev
else:
return 0
def get_remote_database_revision(self, repo = None):
if repo == None:
repo = self.default_repository
remote_status = self.MirrorsService.get_remote_databases_status(repo)
if not [x for x in remote_status if x[1]]:
remote_revision = 0
else:
remote_revision = max([x[1] for x in remote_status])
return remote_revision
def get_branch_from_download_relative_uri(self, mypath):
return self.ClientService.get_branch_from_download_relative_uri(mypath)
def packageSetList(self, *args, **kwargs):
repos = etpConst['server_repositories'].keys()
kwargs['server_repos'] = repos
kwargs['serverInstance'] = self
return self.ClientService.packageSetList(*args,**kwargs)
def packageSetSearch(self, *args, **kwargs):
repos = etpConst['server_repositories'].keys()
kwargs['server_repos'] = repos
kwargs['serverInstance'] = self
return self.ClientService.packageSetSearch(*args,**kwargs)
def packageSetMatch(self, *args, **kwargs):
repos = etpConst['server_repositories'].keys()
kwargs['server_repos'] = repos
kwargs['serverInstance'] = self
return self.ClientService.packageSetMatch(*args,**kwargs)
def atomMatch(self, *args, **kwargs):
repos = etpConst['server_repositories'].keys()
kwargs['server_repos'] = repos
kwargs['serverInstance'] = self
return self.ClientService.atomMatch(*args,**kwargs)
def scan_package_changes(self):
installed_packages = self.SpmService.get_installed_packages_counter()
installed_counters = set()
toBeAdded = set()
toBeRemoved = set()
toBeInjected = set()
server_repos = etpConst['server_repositories'].keys()
# packages to be added
for spm_atom,spm_counter in installed_packages:
found = False
for server_repo in server_repos:
installed_counters.add(spm_counter)
server_dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = server_repo)
counter = server_dbconn.isCounterAvailable(spm_counter, branch = etpConst['branch'])
if counter:
found = True
break
if not found:
toBeAdded.add((spm_atom,spm_counter,))
# packages to be removed from the database
database_counters = {}
for server_repo in server_repos:
server_dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = server_repo)
database_counters[server_repo] = server_dbconn.listAllCounters(branch = etpConst['branch'])
ordered_counters = set()
for server_repo in database_counters:
for data in database_counters[server_repo]:
ordered_counters.add((data,server_repo))
database_counters = ordered_counters
for (counter,idpackage,),xrepo in database_counters:
if counter < 0:
continue # skip packages without valid counter
if counter in installed_counters:
continue
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = xrepo)
dorm = True
# check if the package is in toBeAdded
if toBeAdded:
dorm = False
atom = dbconn.retrieveAtom(idpackage)
atomkey = self.entropyTools.dep_getkey(atom)
atomtag = self.entropyTools.dep_gettag(atom)
atomslot = dbconn.retrieveSlot(idpackage)
add = True
for spm_atom, spm_counter in toBeAdded:
addslot = self.SpmService.get_installed_package_slot(spm_atom)
addkey = self.entropyTools.dep_getkey(spm_atom)
# workaround for ebuilds not having slot
if addslot == None:
addslot = '0' # handle tagged packages correctly
if (atomkey == addkey) and ((str(atomslot) == str(addslot)) or (atomtag != None)):
# do not add to toBeRemoved
add = False
break
if not add:
continue
dorm = True
if dorm:
trashed = self.is_counter_trashed(counter)
if trashed:
# search into portage then
try:
key, slot = dbconn.retrieveKeySlot(idpackage)
trashed = self.SpmService.get_installed_atom(key+":"+slot)
except TypeError: # referred to retrieveKeySlot
trashed = True
if not trashed:
dbtag = dbconn.retrieveVersionTag(idpackage)
if dbtag != '':
is_injected = dbconn.isInjected(idpackage)
if not is_injected:
toBeInjected.add((idpackage,xrepo))
else:
toBeRemoved.add((idpackage,xrepo))
return toBeAdded, toBeRemoved, toBeInjected
def is_counter_trashed(self, counter):
server_repos = etpConst['server_repositories'].keys()
for repo in server_repos:
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
if dbconn.isCounterTrashed(counter):
return True
return False
def transform_package_into_injected(self, idpackage, repo = None):
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
counter = dbconn.getNewNegativeCounter()
dbconn.setCounter(idpackage,counter)
dbconn.setInjected(idpackage)
def initialize_server_database(self, empty = True, repo = None, warnings = True):
if repo == None: repo = self.default_repository
self.close_server_databases()
revisions_match = {}
treeupdates_actions = []
injected_packages = set()
idpackages = set()
idpackages_added = set()
mytxt = red("%s ...") % (_("Initializing Entropy database"),)
self.updateProgress(
mytxt, importance = 1,
type = "info", header = darkgreen(" * "),
back = True
)
if os.path.isfile(self.get_local_database_file(repo)):
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo, warnings = warnings)
if dbconn.doesTableExist("baseinfo") and dbconn.doesTableExist("extrainfo"):
idpackages = dbconn.listAllIdpackages()
if dbconn.doesTableExist("treeupdatesactions"):
treeupdates_actions = dbconn.listAllTreeUpdatesActions()
# save list of injected packages
if dbconn.doesTableExist("injected") and dbconn.doesTableExist("extrainfo"):
injected_packages = dbconn.listAllInjectedPackages(justFiles = True)
injected_packages = set([os.path.basename(x) for x in injected_packages])
for idpackage in idpackages:
package = os.path.basename(dbconn.retrieveDownloadURL(idpackage))
branch = dbconn.retrieveBranch(idpackage)
revision = dbconn.retrieveRevision(idpackage)
revisions_match[package] = (branch,revision,)
self.close_server_database(dbconn)
mytxt = "%s: %s: %s" % (
bold(_("WARNING")),
red(_("database already exists")),
self.get_local_database_file(repo),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" !!! ")
)
rc = self.askQuestion(_("Do you want to continue ?"))
if rc == "No": return
try:
os.remove(self.get_local_database_file(repo))
except OSError:
pass
# initialize
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo, is_new = True)
dbconn.initializeDatabase()
if not empty:
revisions_file = "/entropy-revisions-dump.txt"
# dump revisions - as a backup
if revisions_match:
self.updateProgress(
"%s: %s" % (
red(_("Dumping current revisions to file")),
darkgreen(revisions_file),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
f = open(revisions_file,"w")
f.write(str(revisions_match))
f.flush()
f.close()
# dump treeupdates - as a backup
treeupdates_file = "/entropy-treeupdates-dump.txt"
if treeupdates_actions:
self.updateProgress(
"%s: %s" % (
red(_("Dumping current 'treeupdates' actions to file")), # do not translate treeupdates
bold(treeupdates_file),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
f = open(treeupdates_file,"w")
f.write(str(treeupdates_actions))
f.flush()
f.close()
rc = self.askQuestion(_("Would you like to sync packages first (important if you don't have them synced) ?"))
if rc == "Yes":
self.MirrorsService.sync_packages(repo = repo)
# fill tree updates actions
if treeupdates_actions:
dbconn.bumpTreeUpdatesActions(treeupdates_actions)
# now fill the database
pkg_branch_dir = os.path.join(self.get_local_packages_directory(repo),etpConst['branch'])
pkglist = os.listdir(pkg_branch_dir)
# filter .md5 and .expired packages
pkglist = [x for x in pkglist if x[-5:] == etpConst['packagesext'] and not \
os.path.isfile(os.path.join(pkg_branch_dir,x+etpConst['packagesexpirationfileext']))]
if pkglist:
self.updateProgress(
"%s '%s' %s %s" % (
red(_("Reinitializing Entropy database for branch")),
bold(etpConst['branch']),
red(_("using Packages in the repository")),
red("..."),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
counter = 0
maxcount = len(pkglist)
for pkg in pkglist:
counter += 1
self.updateProgress(
"[repo:%s|%s] %s: %s" % (
darkgreen(repo),
brown(etpConst['branch']),
blue(_("analyzing")),
bold(pkg),
),
importance = 1,
type = "info",
header = " ",
back = True,
count = (counter,maxcount,)
)
doinject = False
if pkg in injected_packages:
doinject = True
pkg_path = os.path.join(self.get_local_packages_directory(repo),etpConst['branch'],pkg)
mydata = self.ClientService.extract_pkg_metadata(pkg_path, etpConst['branch'], inject = doinject)
# get previous revision
revision_avail = revisions_match.get(pkg)
addRevision = 0
if (revision_avail != None):
if etpConst['branch'] == revision_avail[0]:
addRevision = revision_avail[1]
idpackage, revision, mydata_upd = dbconn.addPackage(mydata, revision = addRevision)
idpackages_added.add(idpackage)
self.updateProgress(
"[repo:%s] [%s:%s/%s] %s: %s, %s: %s" % (
repo,
brown(etpConst['branch']),
darkgreen(str(counter)),
blue(str(maxcount)),
red(_("added package")),
darkgreen(pkg),
red(_("revision")),
brown(str(revision)),
),
importance = 1,
type = "info",
header = " ",
back = True
)
self.depends_table_initialize(repo)
myQA = self.QA()
if idpackages_added:
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
myQA.scan_missing_dependencies(
idpackages_added, dbconn, ask = True,
repo = repo, self_check = True,
black_list = self.get_missing_dependencies_blacklist(repo = repo),
black_list_adder = self.add_missing_dependencies_blacklist_items
)
dbconn.commitChanges()
self.close_server_databases()
return 0
def match_packages(self, packages, repo = None):
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
if ("world" in packages) or not packages:
return dbconn.listAllIdpackages(),True
else:
idpackages = set()
for package in packages:
matches = dbconn.atomMatch(package, multiMatch = True)
if matches[1] == 0:
idpackages |= matches[0]
else:
mytxt = "%s: %s: %s" % (red(_("Attention")),blue(_("cannot match")),bold(package),)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" !!! ")
)
return idpackages,False
def get_remote_package_checksum(self, repo, filename, branch):
if not etpConst['server_repositories'][repo].has_key('handler'):
return None
url = etpConst['server_repositories'][repo]['handler']
# does the package has "#" (== tag) ? hackish thing that works
filename = filename.replace("#","%23")
# "+"
filename = filename.replace("+","%2b")
request = os.path.join(url,etpConst['handlers']['md5sum'])
request += filename+"&branch="+branch
# now pray the server
try:
mydict = {}
if etpConst['proxy']['ftp']:
mydict['ftp'] = etpConst['proxy']['ftp']
if etpConst['proxy']['http']:
mydict['http'] = etpConst['proxy']['http']
if mydict:
mydict['username'] = etpConst['proxy']['username']
mydict['password'] = etpConst['proxy']['password']
self.entropyTools.add_proxy_opener(urllib2, mydict)
else:
# unset
urllib2._opener = None
item = urllib2.urlopen(request)
result = item.readline().strip()
item.close()
del item
return result
except: # no HTTP support?
return None
def verify_remote_packages(self, packages, ask = True, repo = None):
if repo == None:
repo = self.default_repository
self.updateProgress(
"[%s] %s:" % (
red("remote"),
blue(_("Integrity verification of the selected packages")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
idpackages, world = self.match_packages(packages)
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
if world:
self.updateProgress(
blue(_("All the packages in the Entropy Packages repository will be checked.")),
importance = 1,
type = "info",
header = " "
)
else:
mytxt = red("%s:") % (_("This is the list of the packages that would be checked"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = " "
)
for idpackage in idpackages:
pkgatom = dbconn.retrieveAtom(idpackage)
pkgfile = os.path.basename(dbconn.retrieveDownloadURL(idpackage))
self.updateProgress(
red(pkgatom)+" -> "+bold(os.path.join(etpConst['branch'],pkgfile)),
importance = 1,
type = "info",
header = darkgreen(" - ")
)
if ask:
rc = self.askQuestion(_("Would you like to continue ?"))
if rc == "No":
return set(),set(),{}
match = set()
not_match = set()
broken_packages = {}
for uri in self.get_remote_mirrors(repo):
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
self.updateProgress(
"[repo:%s] %s: %s" % (
darkgreen(repo),
blue(_("Working on mirror")),
brown(crippled_uri),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
totalcounter = len(idpackages)
currentcounter = 0
for idpackage in idpackages:
currentcounter += 1
pkgfile = dbconn.retrieveDownloadURL(idpackage)
orig_branch = self.get_branch_from_download_relative_uri(pkgfile)
self.updateProgress(
"[%s] %s: %s" % (
brown(crippled_uri),
blue(_("checking hash")),
darkgreen(pkgfile),
),
importance = 1,
type = "info",
header = blue(" @@ "),
back = True,
count = (currentcounter,totalcounter,)
)
ckOk = False
ck = self.get_remote_package_checksum(repo, os.path.basename(pkgfile), orig_branch)
if ck == None:
self.updateProgress(
"[%s] %s: %s %s" % (
brown(crippled_uri),
blue(_("digest verification of")),
bold(pkgfile),
blue(_("not supported")),
),
importance = 1,
type = "info",
header = blue(" @@ "),
count = (currentcounter,totalcounter,)
)
elif len(ck) == 32:
pkghash = dbconn.retrieveDigest(idpackage)
if ck == pkghash: ckOk = True
else:
self.updateProgress(
"[%s] %s: %s %s" % (
brown(crippled_uri),
blue(_("digest verification of")),
bold(pkgfile),
blue(_("failed for unknown reasons")),
),
importance = 1,
type = "info",
header = blue(" @@ "),
count = (currentcounter,totalcounter,)
)
if ckOk:
match.add(idpackage)
else:
not_match.add(idpackage)
self.updateProgress(
"[%s] %s: %s %s" % (
brown(crippled_uri),
blue(_("package")),
bold(pkgfile),
red(_("NOT healthy")),
),
importance = 1,
type = "warning",
header = darkred(" !!! "),
count = (currentcounter,totalcounter,)
)
if not broken_packages.has_key(crippled_uri):
broken_packages[crippled_uri] = []
broken_packages[crippled_uri].append(pkgfile)
if broken_packages:
mytxt = blue("%s:") % (_("This is the list of broken packages"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" * ")
)
for mirror in broken_packages.keys():
mytxt = "%s: %s" % (brown(_("Mirror")),bold(mirror),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" <> ")
)
for bp in broken_packages[mirror]:
self.updateProgress(
blue(bp),
importance = 1,
type = "info",
header = red(" - ")
)
self.updateProgress(
"%s:" % (
blue(_("Statistics")),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
self.updateProgress(
"[%s] %s:\t%s" % (
red(crippled_uri),
brown(_("Number of checked packages")),
brown(str(len(match)+len(not_match))),
),
importance = 1,
type = "info",
header = brown(" # ")
)
self.updateProgress(
"[%s] %s:\t%s" % (
red(crippled_uri),
darkgreen(_("Number of healthy packages")),
darkgreen(str(len(match))),
),
importance = 1,
type = "info",
header = brown(" # ")
)
self.updateProgress(
"[%s] %s:\t%s" % (
red(crippled_uri),
darkred(_("Number of broken packages")),
darkred(str(len(not_match))),
),
importance = 1,
type = "info",
header = brown(" # ")
)
return match, not_match, broken_packages
def verify_local_packages(self, packages, ask = True, repo = None):
if repo == None:
repo = self.default_repository
self.updateProgress(
"[%s] %s:" % (
red(_("local")),
blue(_("Integrity verification of the selected packages")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
idpackages, world = self.match_packages(packages)
dbconn = self.openServerDatabase(read_only = True, no_upload = True, repo = repo)
if world:
self.updateProgress(
blue(_("All the packages in the Entropy Packages repository will be checked.")),
importance = 1,
type = "info",
header = " "
)
to_download = set()
available = set()
for idpackage in idpackages:
pkgatom = dbconn.retrieveAtom(idpackage)
pkg_path = dbconn.retrieveDownloadURL(idpackage)
pkg_rel_path = '/'.join(pkg_path.split("/")[2:])
bindir_path = os.path.join(self.get_local_packages_directory(repo),pkg_rel_path)
uploaddir_path = os.path.join(self.get_local_upload_directory(repo),pkg_rel_path)
if os.path.isfile(bindir_path):
if not world:
self.updateProgress(
"[%s] %s :: %s" % (
darkgreen(_("available")),
blue(pkgatom),
darkgreen(pkg_rel_path),
),
importance = 0,
type = "info",
header = darkgreen(" # ")
)
available.add(idpackage)
elif os.path.isfile(uploaddir_path):
if not world:
self.updateProgress(
"[%s] %s :: %s" % (
darkred(_("upload/ignored")),
blue(pkgatom),
darkgreen(pkg_rel_path),
),
importance = 0,
type = "info",
header = darkgreen(" # ")
)
else:
self.updateProgress(
"[%s] %s :: %s" % (
brown(_("download")),
blue(pkgatom),
darkgreen(pkg_rel_path),
),
importance = 0,
type = "info",
header = darkgreen(" # ")
)
to_download.add((idpackage,pkg_path,))
if ask:
rc = self.askQuestion(_("Would you like to continue ?"))
if rc == "No":
return set(),set(),set(),set()
fine = set()
failed = set()
downloaded_fine = set()
downloaded_errors = set()
if to_download:
not_downloaded = set()
mytxt = blue("%s ...") % (_("Starting to download missing files"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = " "
)
for uri in self.get_remote_mirrors(repo):
if not_downloaded:
mytxt = blue("%s ...") % (_("Trying to search missing or broken files on another mirror"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = " "
)
to_download = not_downloaded.copy()
not_downloaded = set()
for idpackage, pkg_path in to_download: # idpackage, pkgfile, branch
rc = self.MirrorsService.download_package(uri, pkg_path, repo = repo)
if rc:
downloaded_fine.add(idpackage)
available.add(idpackage)
else:
not_downloaded.add(pkg_path)
if not not_downloaded:
self.updateProgress(
red(_("All the binary packages have been downloaded successfully.")),
importance = 1,
type = "info",
header = " "
)
break
if not_downloaded:
mytxt = blue("%s:") % (_("These are the packages that cannot be found online"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = " "
)
for pkg_path in not_downloaded:
downloaded_errors.add(pkg_path)
self.updateProgress(
brown(pkg_path),
importance = 1,
type = "warning",
header = red(" * ")
)
downloaded_errors |= not_downloaded
mytxt = "%s." % (_("They won't be checked"),)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = " "
)
totalcounter = str(len(available))
currentcounter = 0
for idpackage in available:
currentcounter += 1
pkg_path = dbconn.retrieveDownloadURL(idpackage)
orig_branch = self.get_branch_from_download_relative_uri(pkg_path)
pkgfile = os.path.basename(pkg_path)
self.updateProgress(
"[branch:%s] %s %s" % (
brown(orig_branch),
blue(_("checking hash of")),
darkgreen(pkgfile),
),
importance = 1,
type = "info",
header = " ",
back = True,
count = (currentcounter,totalcounter,)
)
storedmd5 = dbconn.retrieveDigest(idpackage)
pkgpath = os.path.join(self.get_local_packages_directory(repo),orig_branch,pkgfile)
result = self.entropyTools.compareMd5(pkgpath,storedmd5)
if result:
fine.add(idpackage)
else:
failed.add(idpackage)
self.updateProgress(
"[branch:%s] %s %s %s: %s" % (
brown(orig_branch),
blue(_("package")),
darkgreen(pkg_path),
blue(_("is corrupted, stored checksum")), # package -blah- is corrupted...
brown(storedmd5),
),
importance = 1,
type = "info",
header = " ",
count = (currentcounter,totalcounter,)
)
if failed:
mytxt = blue("%s:") % (_("This is the list of broken packages"),)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" # ")
)
for idpackage in failed:
atom = dbconn.retrieveAtom(idpackage)
dp = dbconn.retrieveDownloadURL(idpackage)
self.updateProgress(
blue("[atom:%s] %s" % (atom,dp,)),
importance = 0,
type = "warning",
header = brown(" # ")
)
# print stats
self.updateProgress(
red("Statistics:"),
importance = 1,
type = "info",
header = blue(" * ")
)
self.updateProgress(
brown("%s:\t\t%s" % (
_("Number of checked packages"),
len(fine)+len(failed),
)
),
importance = 0,
type = "info",
header = brown(" # ")
)
self.updateProgress(
darkgreen("%s:\t\t%s" % (
_("Number of healthy packages"),
len(fine),
)
),
importance = 0,
type = "info",
header = brown(" # ")
)
self.updateProgress(
darkred("%s:\t\t%s" % (
_("Number of broken packages"),
len(failed),
)
),
importance = 0,
type = "info",
header = brown(" # ")
)
self.updateProgress(
blue("%s:\t\t%s" % (
_("Number of downloaded packages"),
len(downloaded_fine),
)
),
importance = 0,
type = "info",
header = brown(" # ")
)
self.updateProgress(
bold("%s:\t\t%s" % (
_("Number of failed downloads"),
len(downloaded_errors),
)
),
importance = 0,
type = "info",
header = brown(" # ")
)
self.close_server_database(dbconn)
return fine, failed, downloaded_fine, downloaded_errors
def switch_packages_branch(self, idpackages, from_branch, to_branch, repo = None):
if repo == None:
repo = self.default_repository
if to_branch != etpConst['branch']:
mytxt = "%s: %s %s" % (blue(_("Please setup your branch to")),bold(to_branch),blue(_("and retry")),)
self.updateProgress(
mytxt,
importance = 1,
type = "error",
header = darkred(" !! ")
)
return None
mytxt = red("%s ...") % (_("Moving database (if not exists)"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = darkgreen(" @@ ")
)
branch_dbdir = self.get_local_database_dir(repo)
old_branch_dbdir = self.get_local_database_dir(repo, from_branch)
if (not os.path.isdir(branch_dbdir)) and os.path.isdir(old_branch_dbdir):
shutil.copytree(old_branch_dbdir,branch_dbdir)
mytxt = red("%s ...") % (_("Switching packages"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = darkgreen(" @@ ")
)
dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo, lock_remote = False)
already_switched = set()
not_found = set()
switched = set()
ignored = set()
no_checksum = set()
maxcount = len(idpackages)
count = 0
for idpackage in idpackages:
count += 1
cur_branch = dbconn.retrieveBranch(idpackage)
atom = dbconn.retrieveAtom(idpackage)
if cur_branch == to_branch:
already_switched.add(idpackage)
self.updateProgress(
red("%s %s, %s %s" % (
_("Ignoring"),
bold(atom),
_("already in branch"),
cur_branch,
)
),
importance = 0,
type = "info",
header = darkgreen(" @@ "),
count = (count,maxcount,)
)
ignored.add(idpackage)
continue
mytxt = blue("%s ...") % (_("configuring package information"),)
self.updateProgress(
"[%s=>%s] %s, %s" % (
brown(cur_branch),
bold(to_branch),
darkgreen(atom),
mytxt,
),
importance = 0,
type = "info",
header = darkgreen(" @@ "),
back = True,
count = (count,maxcount,)
)
switch_status = dbconn.switchBranch(idpackage,to_branch)
if not switch_status:
# remove idpackage
dbconn.removePackage(idpackage)
dbconn.commitChanges()
switched.add(idpackage)
dbconn.commitChanges()
# now migrate counters
dbconn.moveCountersToBranch(to_branch)
self.close_server_database(dbconn)
mytxt = blue("%s.") % (_("migration loop completed"),)
self.updateProgress(
"[%s=>%s] %s" % (
brown(from_branch),
bold(to_branch),
mytxt,
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
return switched, already_switched, ignored, not_found, no_checksum
def get_entropy_sets(self, repo = None, branch = etpConst['branch']):
if repo == None: repo = self.default_repository
sets_dir = self.get_local_database_sets_dir(repo, branch)
if not (os.path.isdir(sets_dir) and os.access(sets_dir,os.R_OK)):
return {}
mydata = {}
items = os.listdir(sets_dir)
for item in items:
try:
item_clean = str(item)
except (UnicodeEncodeError,UnicodeDecodeError,):
continue
item_path = os.path.join(sets_dir,item)
if not (os.path.isfile(item_path) and os.access(item_path,os.R_OK)):
continue
item_elements = self.entropyTools.extract_packages_from_set_file(item_path)
if item_elements:
mydata[item_clean] = item_elements.copy()
return mydata
def get_configured_package_sets(self, repo = None, branch = etpConst['branch'], validate = True):
if repo == None: repo = self.default_repository
# portage sets
sets_data = self.SpmService.get_sets_expanded(builtin_sets = False)
sets_data.update(self.get_entropy_sets(repo, branch))
if validate:
invalid_sets = set()
# validate
for setname in sets_data:
good = True
for atom in sets_data[setname]:
dbconn = self.openServerDatabase(just_reading = True, repo = repo)
match = dbconn.atomMatch(atom)
if match[0] == -1:
good = False
break
if not good: invalid_sets.add(setname)
for invalid_set in invalid_sets:
del sets_data[invalid_set]
return sets_data
def update_database_package_sets(self, repo = None, dbconn = None):
if repo == None: repo = self.default_repository
package_sets = self.get_configured_package_sets(repo)
if dbconn == None: dbconn = self.openServerDatabase(read_only = False, no_upload = True, repo = repo)
dbconn.clearPackageSets()
if package_sets: dbconn.insertPackageSets(package_sets)
dbconn.commitChanges()
class RemoteDbSkelInterface:
def __init__(self):
self.dbconn = None
self.cursor = None
self.plain_cursor = None
self.connection_data = {}
try:
import MySQLdb, _mysql_exceptions
from MySQLdb.constants import FIELD_TYPE
from MySQLdb.converters import conversions
except ImportError:
raise exceptionTools.LibraryNotFound('LibraryNotFound: dev-python/mysql-python not found')
self.mysql = MySQLdb
self.mysql_exceptions = _mysql_exceptions
self.FIELD_TYPE = FIELD_TYPE
self.conversion_dict = conversions.copy()
self.conversion_dict[self.FIELD_TYPE.DECIMAL] = int
self.conversion_dict[self.FIELD_TYPE.LONG] = int
self.conversion_dict[self.FIELD_TYPE.FLOAT] = float
self.conversion_dict[self.FIELD_TYPE.NEWDECIMAL] = float
def check_connection(self):
if self.dbconn == None:
raise exceptionTools.ConnectionError('ConnectionError: %s' % (_("not connected to database"),))
self._check_needed_reconnect()
def _check_needed_reconnect(self):
if self.dbconn == None:
return
try:
self.dbconn.ping()
except self.mysql_exceptions.OperationalError, e:
if e[0] != 2006:
raise
else:
self.connect()
return True
return False
def _raise_not_implemented_error(self):
raise exceptionTools.NotImplementedError('NotImplementedError: %s' % (_('method not implemented'),))
def set_connection_data(self, data):
self.connection_data = data.copy()
if not self.connection_data.has_key('converters') and self.conversion_dict:
self.connection_data['converters'] = self.conversion_dict.copy()
def check_connection_data(self):
if not self.connection_data:
raise exceptionTools.PermissionDenied('ConnectionError: %s' % (_("no connection data"),))
def connect(self):
kwargs = {}
keys = [
('host',"hostname"),
('user',"username"),
('passwd',"password"),
('db',"dbname"),
('port',"port"),
('conv',"converters"), # mysql type converter dict
]
for ckey, dkey in keys:
if not self.connection_data.has_key(dkey):
continue
kwargs[ckey] = self.connection_data.get(dkey)
try:
self.dbconn = self.mysql.connect(**kwargs)
except self.mysql_exceptions.OperationalError, e:
raise exceptionTools.ConnectionError('ConnectionError: %s' % (e,))
self.plain_cursor = self.dbconn.cursor()
self.cursor = self.mysql.cursors.DictCursor(self.dbconn)
return True
def disconnect(self):
self.check_connection()
if hasattr(self.cursor,'close'):
self.cursor.close()
if hasattr(self.dbconn,'close'):
self.dbconn.close()
self.dbconn = None
self.cursor = None
self.plain_cursor = None
self.connection_data.clear()
return True
def commit(self):
self.check_connection()
return self.dbconn.commit()
def execute_script(self, myscript):
pty = None
for line in myscript.split(";"):
line = line.strip()
if not line:
continue
pty = self.cursor.execute(line)
return pty
def execute_query(self, *args):
return self.cursor.execute(*args)
def execute_many(self, query, myiter):
return self.cursor.executemany(query, myiter)
def fetchone(self):
return self.cursor.fetchone()
def fetchall(self):
return self.cursor.fetchall()
def fetchmany(self, *args, **kwargs):
return self.cursor.fetchmany(*args,**kwargs)
def lastrowid(self):
return self.cursor.lastrowid
def table_exists(self, table):
self.check_connection()
self.cursor.execute("show tables like %s", (table,))
rslt = self.cursor.fetchone()
if rslt:
return True
return False
def column_in_table_exists(self, table, column):
t_ex = self.table_exists(table)
if not t_ex:
return False
self.cursor.execute("show columns from "+table)
data = self.cursor.fetchall()
for row in data:
if row['Field'] == column:
return True
return False
def fetchall2set(self, item):
mycontent = set()
for x in item:
mycontent |= set(x)
return mycontent
def fetchall2list(self, item):
content = []
for x in item:
content += list(x)
return content
def fetchone2list(self, item):
return list(item)
def fetchone2set(self, item):
return set(item)
def _generate_sql(self, action, table, data, where = ''):
sql = u''
keys = sorted(data.keys())
if action == "update":
sql += 'UPDATE %s SET ' % (self.dbconn.escape_string(table),)
keys_data = []
for key in keys:
keys_data.append("%s = '%s'" % (
self.dbconn.escape_string(key),
self.dbconn.escape_string(unicode(data[key]).encode('utf-8')).decode('utf-8')
)
)
sql += ', '.join(keys_data)
sql += ' WHERE %s' % (where,)
elif action == "insert":
sql = u'INSERT INTO %s (%s) VALUES (%s)' % (
self.dbconn.escape_string(table),
u', '.join([self.dbconn.escape_string(x) for x in keys]),
u', '.join([u"'"+self.dbconn.escape_string(unicode(data[x]).encode('utf-8')).decode('utf-8')+"'" for x in keys])
)
return sql
class DistributionUGCInterface(RemoteDbSkelInterface):
SQL_TABLES = {
'entropy_base': """
CREATE TABLE `entropy_base` (
`idkey` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`key` VARCHAR( 255 ) collate utf8_bin NOT NULL,
KEY `key` (`key`)
);
""",
'entropy_votes': """
CREATE TABLE `entropy_votes` (
`idvote` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`idkey` INT UNSIGNED NOT NULL,
`userid` INT UNSIGNED NOT NULL,
`vdate` DATE NOT NULL,
`vote` TINYINT NOT NULL,
`ts` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`idkey`) REFERENCES `entropy_base` (`idkey`)
);
""",
'entropy_user_scores': """
CREATE TABLE `entropy_user_scores` (
`userid` INT UNSIGNED NOT NULL PRIMARY KEY,
`score` INT UNSIGNED NOT NULL DEFAULT 0
);
""",
'entropy_downloads': """
CREATE TABLE `entropy_downloads` (
`iddownload` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`idkey` INT UNSIGNED NOT NULL,
`ddate` DATE NOT NULL,
`count` INT UNSIGNED NULL DEFAULT '0',
KEY `idkey` (`idkey`,`ddate`),
KEY `idkey_2` (`idkey`),
FOREIGN KEY (`idkey`) REFERENCES `entropy_base` (`idkey`)
);
""",
'entropy_downloads_data': """
CREATE TABLE `entropy_downloads_data` (
`iddownload` INT UNSIGNED NOT NULL,
`ip_address` VARCHAR(40) NULL DEFAULT '',
`location` TINYINT NULL DEFAULT 0,
FOREIGN KEY (`iddownload`) REFERENCES `entropy_downloads` (`iddownload`)
);
""",
'entropy_docs': """
CREATE TABLE `entropy_docs` (
`iddoc` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`idkey` INT UNSIGNED NOT NULL,
`userid` INT UNSIGNED NOT NULL,
`username` VARCHAR( 255 ),
`iddoctype` TINYINT NOT NULL,
`ddata` TEXT NOT NULL,
`title` VARCHAR( 512 ),
`description` VARCHAR( 4000 ),
`ts` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY `idkey` (`idkey`),
KEY `userid` (`userid`),
KEY `idkey_2` (`idkey`,`userid`,`iddoctype`),
KEY `title` (`title`(333)),
KEY `description` (`description`(333)),
FOREIGN KEY (`idkey`) REFERENCES `entropy_base` (`idkey`)
);
""",
'entropy_doctypes': """
CREATE TABLE `entropy_doctypes` (
`iddoctype` TINYINT NOT NULL PRIMARY KEY,
`description` TEXT NOT NULL
);
""",
'entropy_docs_keywords': """
CREATE TABLE `entropy_docs_keywords` (
`iddoc` INT UNSIGNED NOT NULL,
`keyword` VARCHAR( 100 ),
KEY `keyword` (`keyword`),
FOREIGN KEY (`iddoc`) REFERENCES `entropy_docs` (`iddoc`)
);
""",
}
VOTE_RANGE = etpConst['ugc_voterange'] # [1, 2, 3, 4, 5]
VIRUS_CHECK_EXEC = '/usr/bin/clamscan'
VIRUS_CHECK_ARGS = []
gdata = None
YouTube = None
YouTubeService = None
entropy_docs_title_len = 512
entropy_docs_description_len = 4000
entropy_docs_keyword_len = 100
COMMENTS_SCORE_WEIGHT = 5
DOCS_SCORE_WEIGHT = 10
VOTES_SCORE_WEIGHT = 2
DOWNLOADS_STORE_LOCATION = 0
'''
dependencies:
dev-python/gdata
'''
def __init__(self, connection_data, store_path, store_url = ''):
import entropyTools, dumpTools
self.entropyTools, self.dumpTools = entropyTools, dumpTools
self.store_url = store_url
self.FLOOD_INTERVAL = 30
self.DOC_TYPES = etpConst['ugc_doctypes'].copy()
self.UPLOADED_DOC_TYPES = [
self.DOC_TYPES['image'],
self.DOC_TYPES['generic_file']
]
RemoteDbSkelInterface.__init__(self)
self.set_connection_data(connection_data)
self.connect()
self.initialize_tables()
self.initialize_doctypes()
self.setup_store_path(store_path)
self.system_name = etpConst['systemname']
from datetime import datetime
self.datetime = datetime
try:
import gdata
import gdata.youtube
import gdata.youtube.service
self.gdata = gdata
self.YouTube = gdata.youtube
self.YouTubeService = gdata.youtube.service
except ImportError:
pass
self.cached_results = {
#'get_ugc_allvotes': (self.get_ugc_allvotes, [], {}, 86400),
'get_ugc_alldownloads': (self.get_ugc_alldownloads, [], {}, 86400),
#'get_users_scored_count': (self.get_users_scored_count, [], {}, 86400),
'get_total_downloads_count': (self.get_total_downloads_count, [], {}, 7200),
}
def get_current_time(self):
return int(time.time())
def cache_results(self):
for cache_item in self.cached_results:
fdata = self.cached_results.get(cache_item)
if fdata == None: return
func, args, kwargs, exp_time = fdata
key = self.get_cache_item_key(cache_item)
r = func(*args,**kwargs)
self.dumpTools.dumpobj(key, r)
def get_cache_item_key(self, cache_item):
return os.path.join(etpCache['ugc_srv_cache'],cache_item)
def cache_result(self, cache_item, r):
if not self.cached_results.get(cache_item): return None
key = self.get_cache_item_key(cache_item)
self.dumpTools.dumpobj(key, r)
# expired get_ugc_alldownloads 0 86400 1228577077
def get_cached_result(self, cache_item):
fdata = self.cached_results.get(cache_item)
if fdata == None: return None
func, args, kwargs, exp_time = fdata
key = self.get_cache_item_key(cache_item)
cur_time = self.get_current_time()
cache_time = self.dumpTools.getobjmtime(key)
if (cache_time + exp_time) < cur_time:
# expired
return None
return self.dumpTools.loadobj(key)
def setup_store_path(self, path):
path = os.path.realpath(path)
if not os.path.isabs(path):
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_("not a valid directory path"),))
if not os.path.isdir(path):
try:
os.makedirs(path)
except OSError, e:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (e,))
if etpConst['entropygid'] != None:
const_setup_perms(path,etpConst['entropygid'])
self.STORE_PATH = path
def initialize_tables(self):
notable = False
for table in self.SQL_TABLES:
if self.table_exists(table):
continue
notable = True
self.execute_script(self.SQL_TABLES[table])
if notable:
self.commit()
def initialize_doctypes(self):
for mydoctype in self.DOC_TYPES:
if self.is_iddoctype_available(self.DOC_TYPES[mydoctype]):
continue
self.insert_iddoctype(self.DOC_TYPES[mydoctype],mydoctype)
def is_iddoctype_available(self, iddoctype):
self.check_connection()
rows = self.execute_query('SELECT `iddoctype` FROM entropy_doctypes WHERE `iddoctype` = %s', (iddoctype,))
if rows:
return True
return False
def is_pkgkey_available(self, key):
self.check_connection()
rows = self.execute_query('SELECT `idkey` FROM entropy_base WHERE `key` = %s', (key,))
if rows:
return True
return False
def is_iddoc_available(self, iddoc):
self.check_connection()
rows = self.execute_query('SELECT `iddoc` FROM entropy_docs WHERE `iddoc` = %s', (iddoc,))
if rows:
return True
return False
def insert_iddoctype(self, iddoctype, description, do_commit = False):
self.check_connection()
self.execute_query('INSERT INTO entropy_doctypes VALUES (%s,%s)', (iddoctype,description,))
if do_commit: self.commit()
def insert_pkgkey(self, key, do_commit = False):
self.check_connection()
self.execute_query('INSERT INTO entropy_base VALUES (%s,%s)', (None,key,))
myid = self.lastrowid()
if do_commit: self.commit()
return myid
def insert_download(self, key, ddate, count = 0, do_commit = False):
self.check_connection()
idkey = self.handle_pkgkey(key)
self.execute_query('INSERT INTO entropy_downloads VALUES (%s,%s,%s,%s)', (None,idkey,ddate,count))
myid = self.lastrowid()
if do_commit: self.commit()
return myid
def update_download(self, iddownload, key, ddate, incr, do_commit = False):
self.check_connection()
self.execute_query('SELECT `count` FROM entropy_downloads WHERE `iddownload` = %s', (iddownload,))
data = self.fetchone()
if not data: # !?!?
return self.insert_download(key, ddate, count = 1)
count = data['count']+incr
# now update
self.execute_query('UPDATE entropy_downloads SET `count` = %s WHERE `iddownload` = %s', (count,iddownload,))
if do_commit: self.commit()
return iddownload
def store_download_data(self, iddownload, ip_addr, do_commit = False):
self.execute_query('INSERT INTO entropy_downloads_data VALUES (%s,%s,%s)', (iddownload,ip_addr,self.DOWNLOADS_STORE_LOCATION))
if do_commit: self.commit()
def get_date(self):
mytime = time.time()
mydate = self.datetime.fromtimestamp(mytime)
mydate = self.datetime(mydate.year,mydate.month,mydate.day)
return mydate
def get_iddownload(self, key, ddate):
self.check_connection()
idkey = self.handle_pkgkey(key)
self.execute_query('SELECT `iddownload` FROM entropy_downloads WHERE `idkey` = %s AND `ddate` = %s', (idkey,ddate,))
data = self.fetchone()
if data:
return data['iddownload']
return -1
def get_idkey(self, key):
self.check_connection()
self.execute_query('SELECT `idkey` FROM entropy_base WHERE `key` = %s', (key,))
data = self.fetchone()
if data: return data['idkey']
return -1
def get_iddoctype(self, iddoc):
self.check_connection()
self.execute_query('SELECT `iddoctype` FROM entropy_docs WHERE `iddoc` = %s', (iddoc,))
data = self.fetchone()
if data: return data['iddoctype']
return -1
def get_pkgkey(self, idkey):
self.check_connection()
self.execute_query('SELECT `key` FROM entropy_base WHERE `idkey` = %s', (idkey,))
data = self.fetchone()
if data: return data['key']
def get_ugc_metadata(self, pkgkey):
self.check_connection()
metadata = {
'vote': 0.0,
'downloads': 0,
}
self.execute_query('SELECT * FROM entropy_docs,entropy_base WHERE entropy_base.`idkey` = entropy_docs.`idkey` AND entropy_base.`key` = %s', (pkgkey,))
metadata['docs'] = self.fetchall()
for mydict in metadata['docs']:
mydict = self._get_ugc_extra_metadata(mydict)
metadata['vote'] = self.get_ugc_vote(pkgkey)
metadata['downloads'] = self.get_ugc_downloads(pkgkey)
return metadata
def get_ugc_keywords(self, iddoc):
self.execute_query('SELECT `keyword` FROM entropy_docs_keywords WHERE `iddoc` = %s order by `keyword`', (iddoc,))
data = self.fetchall()
if not data: return []
mykeys = []
for mydict in data:
if not mydict.has_key('keyword'):
continue
mykeys.append(mydict['keyword'])
return mykeys
def get_ugc_metadata_doctypes(self, pkgkey, typeslist):
self.check_connection()
metadata = []
self.execute_query('SELECT * FROM entropy_docs,entropy_base WHERE entropy_docs.`idkey` = entropy_base.`idkey` AND entropy_base.`key` = %s AND entropy_docs.`iddoctype` IN %s ORDER BY entropy_docs.`ts` ASC', (pkgkey,typeslist,))
metadata = self.fetchall()
for mydict in metadata:
mydict = self._get_ugc_extra_metadata(mydict)
return metadata
def get_ugc_metadata_doctypes_by_identifiers(self, identifiers, typeslist):
self.check_connection()
identifiers = list(identifiers)
if len(identifiers) < 2:
identifiers += [0]
typeslist = list(typeslist)
if len(typeslist) < 2:
typeslist += [0]
self.execute_query('SELECT * FROM entropy_docs WHERE `iddoc` IN %s AND `iddoctype` IN %s', (identifiers,typeslist,))
metadata = self.fetchall()
for mydict in metadata:
mydict = self._get_ugc_extra_metadata(mydict)
return metadata
def get_ugc_metadata_by_identifiers(self, identifiers):
self.check_connection()
identifiers = list(identifiers)
if len(identifiers) < 2:
identifiers += [0]
self.execute_query('SELECT * FROM entropy_docs WHERE `iddoc` IN %s', (identifiers,))
metadata = self.fetchall()
for mydict in metadata:
mydict = self._get_ugc_extra_metadata(mydict)
return metadata
def _get_ugc_extra_metadata(self, mydict):
mydict['store_url'] = None
mydict['keywords'] = self.get_ugc_keywords(mydict['iddoc'])
if mydict.has_key("key"): mydict['pkgkey'] = mydict['key']
else: mydict['pkgkey'] = self.get_pkgkey(mydict['idkey'])
# for binary files, get size too
mydict['size'] = 0
if mydict['iddoctype'] in self.UPLOADED_DOC_TYPES:
myfilename = mydict['ddata']
if not isinstance(myfilename,basestring):
myfilename = myfilename.tostring()
mypath = os.path.join(self.STORE_PATH,myfilename)
if os.path.isfile(mypath) and os.access(mypath,os.R_OK):
try:
mydict['size'] = self.entropyTools.get_file_size(mypath)
except OSError:
pass
mydict['store_url'] = os.path.join(self.store_url,myfilename)
else:
mydata = mydict['ddata']
if not isinstance(mydata,basestring):
mydata = mydata.tostring()
try:
mydict['size'] = len(mydata)
except:
pass
return mydict
def get_ugc_vote(self, pkgkey):
self.check_connection()
vote = 0.0
self.execute_query('SELECT avg(entropy_votes.`vote`) as avg_vote FROM entropy_votes,entropy_base WHERE entropy_base.`key` = %s AND entropy_base.idkey = entropy_votes.idkey', (pkgkey,))
data = self.fetchone()
if isinstance(data,dict):
if data.get('avg_vote'):
return data['avg_vote']
return vote
def get_ugc_allvotes(self):
# cached?
cache_item = 'get_ugc_allvotes'
cached = self.get_cached_result(cache_item)
if cached != None: return cached
self.check_connection()
vote_data = {}
self.execute_query('SELECT entropy_base.`key` as `vkey`,avg(entropy_votes.vote) as `avg_vote` FROM entropy_votes,entropy_base WHERE entropy_votes.`idkey` = entropy_base.`idkey` GROUP BY entropy_base.`key`')
data = self.fetchall()
for d_dict in data:
vote_data[d_dict['vkey']] = d_dict['avg_vote']
# do cache
self.cache_result(cache_item, vote_data)
return vote_data
def get_ugc_downloads(self, pkgkey):
self.check_connection()
downloads = 0
self.execute_query('SELECT SQL_CACHE sum(entropy_downloads.`count`) as `tot_downloads` FROM entropy_downloads,entropy_base WHERE entropy_base.key = %s AND entropy_base.idkey = entropy_downloads.idkey', (pkgkey,))
data = self.fetchone()
if data['tot_downloads'] != None:
downloads = data['tot_downloads']
return downloads
def get_ugc_alldownloads(self):
# cached?
cache_item = 'get_ugc_alldownloads'
cached = self.get_cached_result(cache_item)
if cached != None: return cached
self.check_connection()
down_data = {}
self.execute_query('SELECT SQL_CACHE entropy_base.`key` as `vkey`,sum(entropy_downloads.`count`) as `tot_downloads` FROM entropy_downloads,entropy_base WHERE entropy_downloads.`idkey` = entropy_base.`idkey` GROUP BY entropy_base.`idkey`')
data = self.fetchall()
for d_dict in data:
down_data[d_dict['vkey']] = d_dict['tot_downloads']
# do cache
self.cache_result(cache_item, down_data)
return down_data
def get_iddoc_userid(self, iddoc):
self.check_connection()
self.execute_query('SELECT `userid` FROM entropy_docs WHERE `iddoc` = %s', (iddoc,))
data = self.fetchone()
if not data:
return None
elif not data.has_key('userid'):
return None
return data['userid']
def get_total_comments_count(self):
self.check_connection()
self.execute_query('SELECT count(`iddoc`) as comments FROM entropy_docs WHERE `iddoctype` = %s', (self.DOC_TYPES['comments'],))
data = self.fetchone()
if isinstance(data,dict):
if data['comments']: return data['comments']
return 0
def get_total_documents_count(self):
self.check_connection()
self.execute_query('SELECT count(`iddoc`) as comments FROM entropy_docs WHERE `iddoctype` != %s', (self.DOC_TYPES['comments'],))
data = self.fetchone()
if isinstance(data,dict):
if data['comments']: return data['comments']
return 0
def get_total_votes_count(self):
self.check_connection()
self.execute_query('SELECT count(`idvote`) as votes FROM entropy_votes')
data = self.fetchone()
if isinstance(data,dict):
if data['votes']: return data['votes']
return 0
def get_total_downloads_count(self):
# cached?
cache_item = 'get_total_downloads_count'
cached = self.get_cached_result(cache_item)
if cached != None: return cached
self.check_connection()
self.execute_query('SELECT SQL_CACHE sum(entropy_downloads.`count`) as downloads FROM entropy_downloads')
data = self.fetchone()
r = 0
if isinstance(data,dict):
if data['downloads']: r = int(data['downloads'])
# do cache
self.cache_result(cache_item, r)
return r
def get_user_score_ranking(self, userid):
self.check_connection()
self.execute_query('SET @row = 0')
self.execute_query('SELECT Row, col_a FROM (SELECT @row := @row + 1 AS Row, userid AS col_a FROM entropy_user_scores ORDER BY score DESC) As derived1 WHERE col_a = %s', (userid,))
data = self.fetchone()
if isinstance(data,dict):
if data.get('Row'): return data['Row']
return 0
def get_users_scored_count(self):
# cached?
cache_item = 'get_users_scored_count'
cached = self.get_cached_result(cache_item)
if cached != None: return cached
self.check_connection()
self.execute_query('SELECT SQL_CACHE count(`userid`) as mycount FROM entropy_user_scores')
data = self.fetchone()
r = 0
if isinstance(data,dict):
if data.get('mycount'): r = data['mycount']
# do cache
self.cache_result(cache_item, r)
return r
def is_user_score_available(self, userid):
self.check_connection()
rows = self.execute_query('SELECT `userid` FROM entropy_user_scores WHERE `userid` = %s', (userid,))
if rows:
return True
return False
def calculate_user_score(self, userid):
comments = self.get_user_comments_count(userid)
docs = self.get_user_docs_count(userid)
votes = self.get_user_votes_count(userid)
return (comments*self.COMMENTS_SCORE_WEIGHT)+(docs*self.DOCS_SCORE_WEIGHT)+(votes*self.VOTES_SCORE_WEIGHT)
def update_user_score(self, userid):
self.check_connection()
avail = self.is_user_score_available(userid)
myscore = self.calculate_user_score(userid)
if avail:
self.execute_query('UPDATE entropy_user_scores SET score = %s WHERE `userid` = %s', (myscore,userid,))
else:
self.execute_query('INSERT INTO entropy_user_scores VALUES (%s,%s)', (userid,myscore,))
return myscore
def get_user_score(self, userid):
self.check_connection()
myscore = None
self.execute_query('SELECT score FROM entropy_user_scores WHERE userid = %s',(userid,))
data = self.fetchone()
if isinstance(data,dict):
if data.has_key('score'): myscore = data.get('score')
if myscore == None: myscore = self.update_user_score(userid)
return myscore
def get_users_score_ranking(self, offset = 0, count = 0):
self.check_connection()
limit_string = ''
if count:
limit_string = ' LIMIT %s,%s' % (offset,count,)
self.execute_query('SELECT SQL_CALC_FOUND_ROWS *,userid,score FROM entropy_user_scores ORDER BY score DESC'+limit_string)
data = self.fetchall()
self.execute_query('SELECT FOUND_ROWS() as count')
rdata = self.fetchone()
found_rows = 0
if isinstance(rdata,dict):
if rdata.has_key('count'):
found_rows = rdata.get('count')
return found_rows, data
def get_user_votes_average(self, userid):
self.check_connection()
self.execute_query('SELECT avg(`vote`) as vote_avg FROM entropy_votes WHERE `userid` = %s', (userid,))
data = self.fetchone()
if isinstance(data,dict):
if data['vote_avg']: return round(float(data['vote_avg']),2)
return 0.0
def get_user_alldocs(self, userid):
self.check_connection()
self.execute_query('SELECT * FROM entropy_docs,entropy_base WHERE entropy_docs.`userid` = %s AND entropy_base.idkey = entropy_docs.idkey ORDER BY entropy_base.`key`', (userid,))
return self.fetchall()
def get_user_docs(self, userid):
return self.get_user_generic_doctype(userid, self.DOC_TYPES['comments'], doctype_sql_cmp = "!=")
def get_user_comments(self, userid):
return self.get_user_generic_doctype(userid, self.DOC_TYPES['comments'], doctype_sql_cmp = "=")
def get_user_votes(self, userid):
self.check_connection()
self.execute_query('SELECT * FROM entropy_votes WHERE `userid` = %s', (userid,))
return self.fetchall()
def get_user_generic_doctype(self, userid, doctype, doctype_sql_cmp = "="):
self.check_connection()
self.execute_query('SELECT * FROM entropy_docs WHERE `userid` = %s AND `iddoctype` '+doctype_sql_cmp+' %s', (userid,doctype,))
return self.fetchall()
def get_user_generic_doctype_count(self, userid, doctype, doctype_sql_cmp = "="):
self.check_connection()
self.execute_query('SELECT count(`iddoc`) as docs FROM entropy_docs WHERE `userid` = %s AND `iddoctype` '+doctype_sql_cmp+' %s', (userid,doctype,))
data = self.fetchone()
if isinstance(data,dict):
if data['docs']: return data['docs']
return 0
def get_user_comments_count(self, userid):
return self.get_user_generic_doctype_count(userid, self.DOC_TYPES['comments'])
def get_user_docs_count(self, userid):
return self.get_user_generic_doctype_count(userid, self.DOC_TYPES['comments'], doctype_sql_cmp = "!=")
def get_user_images_count(self, userid):
return self.get_user_generic_doctype_count(userid, self.DOC_TYPES['image'])
def get_user_files_count(self, userid):
return self.get_user_generic_doctype_count(userid, self.DOC_TYPES['generic_file'])
def get_user_yt_videos_count(self, userid):
return self.get_user_generic_doctype_count(userid, self.DOC_TYPES['youtube_video'])
def get_user_votes_count(self, userid):
self.check_connection()
self.execute_query('SELECT count(`idvote`) as votes FROM entropy_votes WHERE `userid` = %s', (userid,))
data = self.fetchone()
if isinstance(data,dict):
if data['votes']: return data['votes']
return 0
def get_user_stats(self, userid):
mydict = {}
mydict['comments'] = self.get_user_comments_count(userid)
mydict['docs'] = self.get_user_docs_count(userid)
mydict['images'] = self.get_user_images_count(userid)
mydict['files'] = self.get_user_files_count(userid)
mydict['yt_videos'] = self.get_user_yt_videos_count(userid)
mydict['votes'] = self.get_user_votes_count(userid)
mydict['votes_avg'] = self.get_user_votes_average(userid)
mydict['total_docs'] = mydict['comments'] + mydict['docs']
mydict['score'] = self.get_user_score(userid)
mydict['ranking'] = self.get_user_score_ranking(userid)
return mydict
def get_distribution_stats(self):
self.check_connection()
mydict = {}
mydict['comments'] = self.get_total_comments_count()
mydict['documents'] = self.get_total_documents_count()
mydict['votes'] = self.get_total_votes_count()
mydict['downloads'] = self.get_total_downloads_count()
return mydict
def search_pkgkey_items(self, pkgkey_string, iddoctypes = None, results_offset = 0, results_limit = 30, order_by = None):
self.check_connection()
if iddoctypes == None:
iddoctypes = [self.DOC_TYPES[x] for x in self.DOC_TYPES]
iddoctypes = "("+', '.join([str(self.DOC_TYPES[x]) for x in self.DOC_TYPES])+")"
myterm = "%"+pkgkey_string+"%"
search_params = [myterm,results_offset,results_limit]
order_by_string = ''
if order_by == "key":
order_by_string = 'ORDER BY entropy_base.`key`'
elif order_by == "username":
order_by_string = 'ORDER BY entropy_docs.`username`'
elif order_by == "vote":
order_by_string = 'ORDER BY avg_vote DESC'
elif order_by == "downloads":
order_by_string = 'ORDER BY tot_downloads DESC'
self.execute_query('SELECT SQL_CALC_FOUND_ROWS *, avg(entropy_votes.`vote`) as avg_vote, sum(entropy_downloads.`count`) as `tot_downloads`, entropy_user_scores.`score` as `score` FROM entropy_docs,entropy_base,entropy_votes,entropy_downloads,entropy_user_scores WHERE entropy_base.`key` LIKE %s AND entropy_docs.`iddoctype` IN '+iddoctypes+' AND entropy_docs.`idkey` = entropy_base.`idkey` AND entropy_votes.`idkey` = entropy_base.`idkey` AND entropy_downloads.`idkey` = entropy_base.`idkey` AND entropy_docs.`userid` = entropy_user_scores.`userid` GROUP BY entropy_docs.`iddoc` '+order_by_string+' LIMIT %s,%s', search_params)
results = self.fetchall()
self.execute_query('SELECT FOUND_ROWS() as count')
data = self.fetchone()
found_rows = 0
if isinstance(data,dict):
if data.has_key('count'):
found_rows = data.get('count')
return results, found_rows
def search_username_items(self, pkgkey_string, iddoctypes = None, results_offset = 0, results_limit = 30, order_by = None):
self.check_connection()
if iddoctypes == None:
iddoctypes = [self.DOC_TYPES[x] for x in self.DOC_TYPES]
iddoctypes = "("+', '.join([str(self.DOC_TYPES[x]) for x in self.DOC_TYPES])+")"
myterm = "%"+pkgkey_string+"%"
search_params = [myterm,results_offset,results_limit]
order_by_string = ''
if order_by == "key":
order_by_string = 'ORDER BY entropy_base.`key`'
elif order_by == "username":
order_by_string = 'ORDER BY entropy_docs.`username`'
elif order_by == "vote":
order_by_string = 'ORDER BY avg_vote DESC'
elif order_by == "downloads":
order_by_string = 'ORDER BY tot_downloads DESC'
self.execute_query('SELECT SQL_CALC_FOUND_ROWS *, avg(entropy_votes.`vote`) as avg_vote, sum(entropy_downloads.`count`) as `tot_downloads`, entropy_user_scores.`score` as `score` FROM entropy_docs,entropy_base,entropy_votes,entropy_downloads,entropy_user_scores WHERE entropy_docs.`username` LIKE %s AND entropy_docs.`iddoctype` IN '+iddoctypes+' AND entropy_docs.`idkey` = entropy_base.`idkey` AND entropy_votes.`idkey` = entropy_base.`idkey` AND entropy_downloads.`idkey` = entropy_base.`idkey` AND entropy_docs.`userid` = entropy_user_scores.`userid` GROUP BY entropy_docs.`iddoc` '+order_by_string+' LIMIT %s,%s', search_params)
results = self.fetchall()
self.execute_query('SELECT FOUND_ROWS() as count')
data = self.fetchone()
found_rows = 0
if isinstance(data,dict):
if data.has_key('count'):
found_rows = data.get('count')
return results, found_rows
def search_content_items(self, pkgkey_string, iddoctypes = None, results_offset = 0, results_limit = 30, order_by = None):
self.check_connection()
if iddoctypes == None:
iddoctypes = [self.DOC_TYPES[x] for x in self.DOC_TYPES]
iddoctypes = "("+', '.join([str(self.DOC_TYPES[x]) for x in self.DOC_TYPES])+")"
myterm = "%"+pkgkey_string+"%"
search_params = [myterm,myterm,myterm,results_offset,results_limit]
order_by_string = ''
if order_by == "key":
order_by_string = 'ORDER BY entropy_base.`key`'
elif order_by == "username":
order_by_string = 'ORDER BY entropy_docs.`username`'
elif order_by == "vote":
order_by_string = 'ORDER BY avg_vote DESC'
elif order_by == "downloads":
order_by_string = 'ORDER BY tot_downloads DESC'
self.execute_query('SELECT SQL_CALC_FOUND_ROWS *, avg(entropy_votes.`vote`) as avg_vote, sum(entropy_downloads.`count`) as `tot_downloads`, entropy_user_scores.`score` as `score` FROM entropy_docs,entropy_base,entropy_votes,entropy_downloads,entropy_user_scores WHERE (entropy_docs.`title` LIKE %s OR entropy_docs.`description` LIKE %s OR entropy_docs.`ddata` LIKE %s) AND entropy_docs.`iddoctype` IN '+iddoctypes+' AND entropy_docs.`idkey` = entropy_base.`idkey` AND entropy_votes.`idkey` = entropy_base.`idkey` AND entropy_downloads.`idkey` = entropy_base.`idkey` AND entropy_docs.`userid` = entropy_user_scores.`userid` GROUP BY entropy_docs.`iddoc` '+order_by_string+' LIMIT %s,%s', search_params)
results = self.fetchall()
self.execute_query('SELECT FOUND_ROWS() as count')
data = self.fetchone()
found_rows = 0
if isinstance(data,dict):
if data.has_key('count'):
found_rows = data.get('count')
return results, found_rows
def search_keyword_items(self, keyword_string, iddoctypes = None, results_offset = 0, results_limit = 30, order_by = None):
self.check_connection()
if iddoctypes == None:
iddoctypes = [self.DOC_TYPES[x] for x in self.DOC_TYPES]
iddoctypes = "("+', '.join([str(self.DOC_TYPES[x]) for x in self.DOC_TYPES])+")"
myterm = "%"+keyword_string+"%"
search_params = [myterm,results_offset,results_limit]
order_by_string = ''
if order_by == "key":
order_by_string = 'ORDER BY entropy_base.`key`'
elif order_by == "username":
order_by_string = 'ORDER BY entropy_docs.`username`'
elif order_by == "vote":
order_by_string = 'ORDER BY avg_vote DESC'
elif order_by == "downloads":
order_by_string = 'ORDER BY tot_downloads DESC'
self.execute_query('SELECT SQL_CALC_FOUND_ROWS *, avg(entropy_votes.`vote`) as avg_vote, sum(entropy_downloads.`count`) as `tot_downloads`, entropy_user_scores.`score` as `score` FROM entropy_docs,entropy_base,entropy_docs_keywords,entropy_votes,entropy_downloads,entropy_user_scores WHERE entropy_docs_keywords.`keyword` LIKE %s AND entropy_docs.`iddoctype` IN '+iddoctypes+' AND entropy_docs.`idkey` = entropy_base.`idkey` AND entropy_docs_keywords.`iddoc` = entropy_docs.`iddoc` AND entropy_votes.`idkey` = entropy_base.`idkey` AND entropy_downloads.`idkey` = entropy_base.`idkey` AND entropy_docs.`userid` = entropy_user_scores.`userid` GROUP BY entropy_docs.`iddoc` '+order_by_string+' LIMIT %s,%s', search_params)
results = self.fetchall()
self.execute_query('SELECT FOUND_ROWS() as count')
data = self.fetchone()
found_rows = 0
if isinstance(data,dict):
if data.has_key('count'):
found_rows = data.get('count')
return results, found_rows
def search_iddoc_item(self, iddoc_string, iddoctypes = None, results_offset = 0, results_limit = 30, order_by = None):
self.check_connection()
if iddoctypes == None:
iddoctypes = [self.DOC_TYPES[x] for x in self.DOC_TYPES]
iddoctypes = "("+', '.join([str(self.DOC_TYPES[x]) for x in self.DOC_TYPES])+")"
try:
myterm = int(iddoc_string)
except ValueError:
return [],0
search_params = [myterm,results_offset,results_limit]
order_by_string = ''
if order_by == "key":
order_by_string = 'ORDER BY entropy_base.`key`'
elif order_by == "username":
order_by_string = 'ORDER BY entropy_docs.`username`'
elif order_by == "vote":
order_by_string = 'ORDER BY avg_vote DESC'
elif order_by == "downloads":
order_by_string = 'ORDER BY tot_downloads DESC'
self.execute_query('SELECT SQL_CALC_FOUND_ROWS *, avg(entropy_votes.`vote`) as avg_vote, sum(entropy_downloads.`count`) as `tot_downloads`, entropy_user_scores.`score` as `score` FROM entropy_docs,entropy_base,entropy_votes,entropy_downloads,entropy_user_scores WHERE entropy_docs.`iddoc` = %s AND entropy_docs.`iddoctype` IN '+iddoctypes+' AND entropy_docs.`idkey` = entropy_base.`idkey` AND entropy_votes.`idkey` = entropy_base.`idkey` AND entropy_downloads.`idkey` = entropy_base.`idkey` AND entropy_docs.`userid` = entropy_user_scores.`userid` GROUP BY entropy_docs.`iddoc` '+order_by_string+' LIMIT %s,%s', search_params)
results = self.fetchall()
self.execute_query('SELECT FOUND_ROWS() as count')
data = self.fetchone()
found_rows = 0
if isinstance(data,dict):
if data.has_key('count'):
found_rows = data.get('count')
return results, found_rows
def handle_pkgkey(self, key):
idkey = self.get_idkey(key)
if idkey == -1: return self.insert_pkgkey(key)
return idkey
def insert_flood_control_check(self, userid):
self.check_connection()
self.execute_query('SELECT max(`ts`) as ts FROM entropy_docs WHERE `userid` = %s', (userid,))
data = self.fetchone()
if not data:
return False
elif not data.has_key('ts'):
return False
elif data['ts'] == None:
return False
delta = self.datetime.fromtimestamp(time.time()) - data['ts']
if (delta.days == 0) and (delta.seconds <= self.FLOOD_INTERVAL):
return True
return False
def insert_generic_doc(self, idkey, userid, username, doc_type, data, title, description, keywords, do_commit = False):
self.check_connection()
title = title[:self.entropy_docs_title_len]
description = description[:self.entropy_docs_description_len]
# flood control
flood_risk = self.insert_flood_control_check(userid)
if flood_risk: return 'flooding detected'
self.execute_query('INSERT INTO entropy_docs VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)',(
None,
idkey,
userid,
username,
doc_type,
data,
title,
description,
None,
)
)
if do_commit: self.commit()
iddoc = self.lastrowid()
self.insert_keywords(iddoc, keywords)
self.update_user_score(userid)
return iddoc
def insert_keywords(self, iddoc, keywords):
keywords = keywords.split(",")
clean_keys = []
for key in keywords:
try:
key = key.strip().split()[0]
except IndexError:
continue
if not key: continue
if not key.isalnum(): continue
key = key[:self.entropy_docs_keyword_len]
if key in clean_keys: continue
clean_keys.append(key)
if not clean_keys:
return
mydata = []
for key in clean_keys:
mydata.append((iddoc, key,))
self.execute_many('INSERT INTO entropy_docs_keywords VALUES (%s,%s)', mydata)
def remove_keywords(self, iddoc):
self.execute_query('DELETE FROM entropy_docs_keywords WHERE `iddoc` = %s',(iddoc,))
def insert_comment(self, pkgkey, userid, username, comment, title, keywords, do_commit = False):
self.check_connection()
idkey = self.handle_pkgkey(pkgkey)
iddoc = self.insert_generic_doc(idkey, userid, username, self.DOC_TYPES['comments'], comment, title, '', keywords)
if isinstance(iddoc,basestring):
return False, iddoc
if do_commit: self.commit()
return True, iddoc
def edit_comment(self, iddoc, new_comment, new_title, new_keywords, do_commit = False):
self.check_connection()
if not self.is_iddoc_available(iddoc):
return False
new_title = new_title[:self.entropy_docs_title_len]
self.execute_query('UPDATE entropy_docs SET `ddata` = %s, `title` = %s WHERE `iddoc` = %s AND `iddoctype` = %s',(
new_comment,
new_title,
iddoc,
self.DOC_TYPES['comments'],
)
)
self.remove_keywords(iddoc)
self.insert_keywords(iddoc, new_keywords)
if do_commit: self.commit()
return True, iddoc
def remove_comment(self, iddoc):
self.check_connection()
userid = self.get_iddoc_userid(iddoc)
self.remove_keywords(iddoc)
self.execute_query('DELETE FROM entropy_docs WHERE `iddoc` = %s AND `iddoctype` = %s',(
iddoc,
self.DOC_TYPES['comments'],
)
)
if userid: self.update_user_score(userid)
return True, iddoc
# give a vote to an app
def do_vote(self, pkgkey, userid, vote, do_commit = False):
self.check_connection()
idkey = self.handle_pkgkey(pkgkey)
vote = int(vote)
if vote not in self.VOTE_RANGE: # weird
vote = 3 # avg?
mydate = self.get_date()
if self.has_user_already_voted(pkgkey, userid):
return False
self.execute_query('INSERT INTO entropy_votes VALUES (%s,%s,%s,%s,%s,%s)',(
None,
idkey,
userid,
mydate,
vote,
None,
)
)
if do_commit: self.commit()
self.update_user_score(userid)
return True
def has_user_already_voted(self, pkgkey, userid):
self.check_connection()
idkey = self.handle_pkgkey(pkgkey)
self.execute_query('SELECT `idvote` FROM entropy_votes WHERE `idkey` = %s AND `userid` = %s', (idkey,userid,))
data = self.fetchone()
if data:
return True
return False
# increment +1 the number of downloads
def do_download(self, pkgkey, ip_addr = None, do_commit = False):
self.check_connection()
mydate = self.get_date()
iddownload = self.get_iddownload(pkgkey, mydate)
if iddownload == -1:
iddownload = self.insert_download(pkgkey, mydate, count = 1)
else:
self.update_download(iddownload, pkgkey, mydate, 1)
if do_commit: self.commit()
if (iddownload > 0) and isinstance(ip_addr,basestring):
self.store_download_data(iddownload, ip_addr)
return True
def do_downloads(self, pkgkeys, ip_addr = None, do_commit = False):
self.check_connection()
mydate = self.get_date()
for pkgkey in pkgkeys:
iddownload = self.get_iddownload(pkgkey, mydate)
if iddownload == -1:
iddownload = self.insert_download(pkgkey, mydate, count = 1)
else:
self.update_download(iddownload, pkgkey, mydate, 1)
if (iddownload > 0) and isinstance(ip_addr,basestring):
self.store_download_data(iddownload, ip_addr)
if do_commit: self.commit()
return True
def insert_document(self, pkgkey, userid, username, text, title, description, keywords, doc_type = None, do_commit = False):
self.check_connection()
idkey = self.handle_pkgkey(pkgkey)
if doc_type == None: doc_type = self.DOC_TYPES['bbcode_doc']
iddoc = self.insert_generic_doc(idkey, userid, username, doc_type, text, title, description, keywords)
if isinstance(iddoc,basestring):
return False, iddoc
if do_commit: self.commit()
return True, iddoc
def edit_document(self, iddoc, new_document, new_title, new_keywords, do_commit = False):
self.check_connection()
if not self.is_iddoc_available(iddoc):
return False
new_title = new_title[:self.entropy_docs_title_len]
self.execute_query('UPDATE entropy_docs SET `ddata` = %s, `title` = %s WHERE `iddoc` = %s AND `iddoctype` = %s',(
new_document,
new_title,
iddoc,
self.DOC_TYPES['bbcode_doc'],
)
)
self.remove_keywords(iddoc)
self.insert_keywords(iddoc, new_keywords)
if do_commit: self.commit()
return True, iddoc
def remove_document(self, iddoc):
self.check_connection()
userid = self.get_iddoc_userid(iddoc)
self.remove_keywords(iddoc)
self.execute_query('DELETE FROM entropy_docs WHERE `iddoc` = %s AND `iddoctype` = %s',(
iddoc,
self.DOC_TYPES['bbcode_doc'],
)
)
if userid: self.update_user_score(userid)
return True, iddoc
def scan_for_viruses(self, filepath):
if not os.access(filepath,os.R_OK):
return False,None
args = [self.VIRUS_CHECK_EXEC]
args += self.VIRUS_CHECK_ARGS
args += [filepath]
f = open("/dev/null","w")
p = subprocess.Popen(args, stdout = f, stderr = f)
rc = p.wait()
f.close()
if rc == 1:
return True,None
return False,None
def insert_generic_file(self, pkgkey, userid, username, file_path, file_name, doc_type, title, description, keywords):
self.check_connection()
file_path = os.path.realpath(file_path)
# do a virus check?
virus_found, virus_type = self.scan_for_viruses(file_path)
if virus_found:
os.remove(file_path)
return False, None
# flood control
flood_risk = self.insert_flood_control_check(userid)
if flood_risk: return False, 'flooding detected'
# validity check
if doc_type == self.DOC_TYPES['image']:
valid = False
if os.path.isfile(file_path) and os.access(file_path,os.R_OK):
valid = self.entropyTools.is_supported_image_file(file_path)
if not valid:
return False, 'not a valid image'
dest_path = os.path.join(self.STORE_PATH,file_name)
# create dir if not exists
dest_dir = os.path.dirname(dest_path)
if not os.path.isdir(dest_dir):
try:
os.makedirs(dest_dir)
except OSError, e:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (e,))
if etpConst['entropygid'] != None:
const_setup_perms(dest_dir,etpConst['entropygid'])
orig_dest_path = dest_path
dcount = 0
while os.path.isfile(dest_path):
dcount += 1
dest_path_name = "%s_%s" % (dcount,os.path.basename(orig_dest_path),)
dest_path = os.path.join(os.path.dirname(orig_dest_path),dest_path_name)
if os.path.dirname(file_path) != dest_dir:
shutil.move(file_path,dest_path)
if etpConst['entropygid'] != None:
const_setup_file(dest_path, etpConst['entropygid'], 0664)
title = title[:self.entropy_docs_title_len]
description = description[:self.entropy_docs_description_len]
# now store in db
idkey = self.handle_pkgkey(pkgkey)
self.execute_query('INSERT INTO entropy_docs VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)',(
None,
idkey,
userid,
username,
doc_type,
file_name,
title,
description,
None,
)
)
iddoc = self.lastrowid()
self.insert_keywords(iddoc, keywords)
store_url = os.path.basename(dest_path)
if self.store_url:
store_url = os.path.join(self.store_url,store_url)
self.update_user_score(userid)
return True, (iddoc, store_url)
def insert_image(self, pkgkey, userid, username, image_path, file_name, title, description, keywords):
return self.insert_generic_file(pkgkey, userid, username, image_path, file_name, self.DOC_TYPES['image'], title, description, keywords)
def insert_file(self, pkgkey, userid, username, file_path, file_name, title, description, keywords):
return self.insert_generic_file(pkgkey, userid, username, file_path, file_name, self.DOC_TYPES['generic_file'], title, description, keywords)
def delete_image(self, iddoc):
return self.delete_generic_file(iddoc, self.DOC_TYPES['image'])
def delete_file(self, iddoc):
return self.delete_generic_file(iddoc, self.DOC_TYPES['generic_file'])
def delete_generic_file(self, iddoc, doc_type):
self.check_connection()
userid = self.get_iddoc_userid(iddoc)
self.execute_query('SELECT `ddata` FROM entropy_docs WHERE `iddoc` = %s AND `iddoctype` = %s',(
iddoc,
doc_type,
)
)
data = self.fetchone()
if data != None:
mypath = data.get('ddata')
if not isinstance(mypath,basestring):
mypath = mypath.tostring()
if mypath != None:
mypath = os.path.join(self.STORE_PATH,mypath)
if os.path.isfile(mypath) and os.access(mypath,os.W_OK):
os.remove(mypath)
self.remove_keywords(iddoc)
self.execute_query('DELETE FROM entropy_docs WHERE `iddoc` = %s AND `iddoctype` = %s',(
iddoc,
doc_type,
)
)
if userid: self.update_user_score(userid)
return True, (iddoc, None)
def insert_youtube_video(self, pkgkey, userid, username, video_path, file_name, title, description, keywords):
self.check_connection()
if not self.gdata:
return False, None
idkey = self.handle_pkgkey(pkgkey)
video_path = os.path.realpath(video_path)
if not (os.access(video_path,os.R_OK) and os.path.isfile(video_path)):
return False
virus_found, virus_type = self.scan_for_viruses(video_path)
if virus_found:
os.remove(video_path)
return False, None
new_video_path = video_path
if isinstance(file_name,basestring):
# move file to the new filename
new_video_path = os.path.join(os.path.dirname(video_path),os.path.basename(file_name)) # force basename
scount = 0
while os.path.lexists(new_video_path):
scount += 1
bpath = "%s.%s" % (unicode(scount),os.path.basename(file_name),)
new_video_path = os.path.join(os.path.dirname(video_path),bpath)
shutil.move(video_path,new_video_path)
yt_service = self.get_youtube_service()
if yt_service == None:
return False, None
mykeywords = ', '.join([x.strip().strip(',') for x in keywords.split()+["sabayon"] if (x.strip() and x.strip(",") and (len(x.strip()) > 4))])
gd_keywords = self.gdata.media.Keywords(text = mykeywords)
mydescription = "%s: %s" % (pkgkey,description,)
mytitle = "%s: %s" % (self.system_name,title,)
my_media_group = self.gdata.media.Group(
title = self.gdata.media.Title(text = mytitle),
description = self.gdata.media.Description(
description_type = 'plain',
text = mydescription
),
keywords = gd_keywords,
category = self.gdata.media.Category(
text = 'Tech',
scheme = 'http://gdata.youtube.com/schemas/2007/categories.cat',
label = 'Tech'
),
player = None
)
video_entry = self.gdata.youtube.YouTubeVideoEntry(media = my_media_group)
new_entry = yt_service.InsertVideoEntry(video_entry, new_video_path)
if not isinstance(new_entry,self.gdata.youtube.YouTubeVideoEntry):
return False, None
video_url = new_entry.GetSwfUrl()
video_id = os.path.basename(video_url)
iddoc = self.insert_generic_doc(idkey, userid, username, self.DOC_TYPES['youtube_video'], video_id, title, description, keywords)
if isinstance(iddoc,basestring):
return False, (iddoc, None,)
return True, (iddoc, video_id,)
def remove_youtube_video(self, iddoc):
self.check_connection()
if not self.gdata:
return False, None
if not self.is_iddoc_available(iddoc):
return False, None
userid = self.get_iddoc_userid(iddoc)
yt_service = self.get_youtube_service()
if yt_service == None:
return False, None
def do_remove():
self.remove_keywords(iddoc)
self.execute_query('DELETE FROM entropy_docs WHERE `iddoc` = %s AND `iddoctype` = %s',(
iddoc,
self.DOC_TYPES['youtube_video'],
)
)
self.execute_query('SELECT `ddata` FROM entropy_docs WHERE `iddoc` = %s AND `iddoctype` = %s',(
iddoc,
self.DOC_TYPES['youtube_video'],
)
)
data = self.fetchone()
if data == None:
do_remove()
return False, None
elif not data.has_key('ddata'):
do_remove()
return False, None
video_id = data.get('ddata')
try:
video_entry = yt_service.GetYouTubeVideoEntry(video_id = video_id)
deleted = yt_service.DeleteVideoEntry(video_entry)
except:
deleted = True
if deleted:
do_remove()
if userid: self.update_user_score(userid)
return deleted, (iddoc, video_id,)
def get_youtube_service(self):
self.check_connection()
if not self.gdata:
return None
keywords = ['google_email', 'google_password']
for keyword in keywords:
if not self.connection_data.has_key(keyword):
return None
# note: your google account must be linked with the YouTube one
srv = self.YouTubeService.YouTubeService()
srv.email = self.connection_data['google_email']
srv.password = self.connection_data['google_password']
if self.connection_data.has_key('google_developer_key'):
srv.developer_key = self.connection_data['google_developer_key']
if self.connection_data.has_key('google_client_id'):
srv.client_id = self.connection_data['google_client_id']
srv.source = 'Entropy'
srv.ProgrammaticLogin()
return srv
class DistributionUGCCommands(SocketCommandsSkel):
import dumpTools, entropyTools
def __init__(self, HostInterface, connection_data, store_path, store_url):
SocketCommandsSkel.__init__(self, HostInterface, inst_name = "ugc-commands")
self.connection_data = connection_data.copy()
self.store_path = store_path
self.store_url = store_url
self.DOC_TYPES = etpConst['ugc_doctypes'].copy()
self.SUPPORTED_DOCFILE_TYPES = [
self.DOC_TYPES['image'],
self.DOC_TYPES['generic_file'],
self.DOC_TYPES['youtube_video'],
]
self.raw_commands = ['ugc:add_comment', 'ugc:edit_comment', 'ugc:register_stream']
self.valid_commands = {
'ugc:get_comments': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_comments,
'args': ["myargs"],
'as_user': False,
'desc': "get the comments of the provided package key",
'syntax': "<SESSION_ID> ugc:get_comments app-foo/foo",
'from': unicode(self), # from what class
},
'ugc:get_comments_by_identifiers': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_comments_by_identifiers,
'args': ["myargs"],
'as_user': False,
'desc': "get the comments belonging to the provided identifiers",
'syntax': "<SESSION_ID> ugc:get_comments_by_identifiers <identifier1> <identifier2> <identifier3>",
'from': unicode(self), # from what class
},
'ugc:get_documents_by_identifiers': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_documents_by_identifiers,
'args': ["myargs"],
'as_user': False,
'desc': "get the documents belonging to the provided identifiers",
'syntax': "<SESSION_ID> ugc:get_documents_by_identifiers <identifier1> <identifier2> <identifier3>",
'from': unicode(self), # from what class
},
'ugc:get_vote': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_vote,
'args': ["myargs"],
'as_user': False,
'desc': "get the vote of the provided package key",
'syntax': "<SESSION_ID> ugc:get_vote app-foo/foo",
'from': unicode(self), # from what class
},
'ugc:get_downloads': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_downloads,
'args': ["myargs"],
'as_user': False,
'desc': "get the number of downloads of the provided package key",
'syntax': "<SESSION_ID> ugc:get_downloads app-foo/foo",
'from': unicode(self), # from what class
},
'ugc:get_textdocs': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_textdocs,
'args': ["myargs"],
'as_user': False,
'desc': "get the text documents belonging to the provided package key",
'syntax': "<SESSION_ID> ugc:get_textdocs app-foo/foo",
'from': unicode(self), # from what class
},
'ugc:get_textdocs_by_identifiers': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_textdocs_by_identifiers,
'args': ["myargs"],
'as_user': False,
'desc': "get the text documents belonging to the provided identifiers",
'syntax': "<SESSION_ID> ugc:get_textdocs_by_identifiers <identifier1> <identifier2> <identifier3>",
'from': unicode(self), # from what class
},
'ugc:get_alldocs': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_alldocs,
'args': ["myargs"],
'as_user': False,
'desc': "get the all the documents belonging to the provided package key",
'syntax': "<SESSION_ID> ugc:get_alldocs app-foo/foo",
'from': unicode(self), # from what class
},
'ugc:get_allvotes': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_allvotes,
'args': [],
'as_user': False,
'desc': "get vote information for every available package key",
'syntax': "<SESSION_ID> ugc:get_allvotes",
'from': unicode(self), # from what class
},
'ugc:get_alldownloads': {
'auth': False,
'built_in': False,
'cb': self.docmd_get_alldownloads,
'args': [],
'as_user': False,
'desc': "get download information for every available package key",
'syntax': "<SESSION_ID> ugc:get_alldownloads",
'from': unicode(self), # from what class
},
'ugc:do_vote': {
'auth': True,
'built_in': False,
'cb': self.docmd_do_vote,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "vote the specified application (from 0 to 5)",
'syntax': "<SESSION_ID> ugc:do_vote app-foo/foo <0..5>",
'from': unicode(self), # from what class
},
'ugc:do_download': {
'auth': False,
'built_in': False,
'cb': self.docmd_do_download,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "inform the system of a downloaded application",
'syntax': "<SESSION_ID> ugc:do_download app-foo/foo",
'from': unicode(self), # from what class
},
'ugc:do_downloads': {
'auth': False,
'built_in': False,
'cb': self.docmd_do_downloads,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "inform the system of downloaded applications",
'syntax': "<SESSION_ID> ugc:do_downloads app-foo/foo1 app-foo/foo2 <...>",
'from': unicode(self), # from what class
},
'ugc:add_comment': {
'auth': True,
'built_in': False,
'cb': self.docmd_add_comment,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "insert a comment related to a package key",
'syntax': "<SESSION_ID> ugc:add_comment app-foo/foo <valid xml formatted data>",
'from': unicode(self), # from what class
},
'ugc:remove_comment': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_comment,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "remove a comment (you need its iddoc and mod/admin privs)",
'syntax': "<SESSION_ID> ugc:remove_comment <iddoc>",
'from': unicode(self), # from what class
},
'ugc:edit_comment': {
'auth': True,
'built_in': False,
'cb': self.docmd_edit_comment,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "edit a comment related to a package key (you need its iddoc, mod/admin privs or being the author)",
'syntax': "<SESSION_ID> ugc:edit_comment <iddoc> <valid xml formatted data>",
'from': unicode(self), # from what class
},
'ugc:register_stream': {
'auth': True,
'built_in': False,
'cb': self.docmd_register_stream,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "register an uploaded file (through stream cmd) to the relative place (image, file, videos)",
'syntax': "<SESSION_ID> ugc:register_stream app-foo/foo <valid xml formatted data>",
'from': unicode(self), # from what class
},
'ugc:remove_image': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_image,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "remove an image (you need its iddoc and mod/admin privs)",
'syntax': "<SESSION_ID> ugc:remove_image <iddoc>",
'from': unicode(self), # from what class
},
'ugc:remove_file': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_file,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "remove a file (you need its iddoc and mod/admin privs)",
'syntax': "<SESSION_ID> ugc:remove_file <iddoc>",
'from': unicode(self), # from what class
},
'ugc:remove_youtube_video': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_youtube_video,
'args': ["authenticator","myargs"],
'as_user': False,
'desc': "remove a youtube video (you need its iddoc and mod/admin privs)",
'syntax': "<SESSION_ID> ugc:remove_youtube_video <iddoc>",
'from': unicode(self), # from what class
},
}
def _load_ugc_interface(self):
return DistributionUGCInterface(self.connection_data, self.store_path, self.store_url)
def _get_userid(self, authenticator):
session_data = self.HostInterface.sessions.get(authenticator.session)
if not session_data:
return False
elif not session_data.has_key('auth_uid'):
return False
return session_data['auth_uid']
def _get_username(self, authenticator):
return authenticator.get_username()
def _get_session_file(self, authenticator):
session_data = self.HostInterface.sessions.get(authenticator.session)
if not session_data:
return False
elif not session_data.has_key('stream_path'):
return False
elif not session_data['stream_path']:
return False
mypath = session_data['stream_path']
if not (os.path.isfile(mypath) and os.access(mypath,os.R_OK)):
return False
return mypath
def _get_session_ip_address(self, authenticator):
session_data = self.HostInterface.sessions.get(authenticator.session)
if not session_data:
return None
elif not session_data.has_key('ip_address'):
return None
return session_data['ip_address']
def docmd_register_stream(self, authenticator, myargs):
if len(myargs) < 3:
return None,'wrong arguments'
pkgkey = myargs[0]
xml_string = ' '.join(myargs[1:])
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('doc_type') \
and mydict.has_key('title') \
and mydict.has_key('description') \
and mydict.has_key('keywords') \
and mydict.has_key('file_name') ):
return None,'wrong dict arguments, xml must have 5 items with attr value -> doc_type, title, description, keywords, file_name'
doc_type = mydict.get('doc_type')
title = mydict.get('title')
description = mydict.get('description')
keywords = mydict.get('keywords')
file_name = mydict.get('file_name')
real_filename = mydict.get('real_filename')
try:
doc_type = int(doc_type)
except (ValueError,):
return None,'wrong arguments (doc_type)'
if doc_type not in self.SUPPORTED_DOCFILE_TYPES:
return None,'unsupported doc type (SUPPORTED_DOCFILE_TYPES)'
if not title: title = 'No title'
if not description: description = 'No description'
if not keywords: keywords = ''
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
username = self._get_username(authenticator)
# get file path
stream_path = self._get_session_file(authenticator)
if not stream_path:
return False,'no stream path available'
orig_stream_path = os.path.dirname(stream_path)
new_stream_path = orig_stream_path
scount = -1
while os.path.lexists(new_stream_path):
scount += 1
b_name = os.path.basename(stream_path)
b_name = "%s.%s" % (scount,b_name,)
new_stream_path = os.path.join(os.path.dirname(orig_stream_path),b_name)
if scount > 1000000:
return False,'while loop interrupted while looking for new_stream_path'
shutil.move(stream_path,new_stream_path)
stream_path = new_stream_path
ugc = self._load_ugc_interface()
rslt = None, 'invalid doc type'
if doc_type == self.DOC_TYPES['image']:
rslt = ugc.insert_image(pkgkey, userid, username, stream_path, file_name, title, description, keywords)
elif doc_type == self.DOC_TYPES['generic_file']:
rslt = ugc.insert_file(pkgkey, userid, username, stream_path, file_name, title, description, keywords)
elif doc_type == self.DOC_TYPES['youtube_video']:
rslt = ugc.insert_youtube_video(pkgkey, userid, username, stream_path, real_filename, title, description, keywords)
return rslt
def docmd_add_comment(self, authenticator, myargs):
if len(myargs) < 2:
return None,'wrong arguments'
pkgkey = myargs[0]
xml_string = ' '.join(myargs[1:])
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('comment') and mydict.has_key('title') and mydict.has_key('keywords')):
return None,'wrong dict arguments, xml must have 3 items with attr value -> comment, title, keywords'
comment = mydict.get('comment')
title = mydict.get('title')
keywords = mydict.get('keywords')
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
username = self._get_username(authenticator)
ugc = self._load_ugc_interface()
status, iddoc = ugc.insert_comment(pkgkey, userid, username, comment, title, keywords)
if not status:
t = 'unable to add comment'
if isinstance(iddoc,basestring):
t = iddoc
return False,t
return iddoc,'ok'
def docmd_remove_comment(self, authenticator, myargs):
if not myargs:
return None,'wrong arguments'
try:
iddoc = int(myargs[0])
except (ValueError,):
return False,'not a valid iddoc'
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
ugc = self._load_ugc_interface()
iddoc_userid = ugc.get_iddoc_userid(iddoc)
if iddoc_userid == None:
return False,'document not available'
# check if admin/mod or author
if authenticator.is_user() and (userid != iddoc_userid):
return False,'permission denied'
ugc = self._load_ugc_interface()
status, iddoc = ugc.remove_comment(iddoc)
if not status:
return False,'document not removed or not available'
return iddoc,'ok'
def docmd_edit_comment(self, authenticator, myargs):
if len(myargs) < 2:
return None,'wrong arguments'
try:
iddoc = int(myargs[0])
except (ValueError,):
return False,'not a valid iddoc'
xml_string = ' '.join(myargs[1:])
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('comment') and mydict.has_key('title') and mydict.has_key('keywords')):
return None,'wrong dict arguments, xml must have two item with attr value -> comment, title'
new_comment = mydict.get('comment')
new_title = mydict.get('title')
new_keywords = mydict.get('keywords')
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
ugc = self._load_ugc_interface()
iddoc_userid = ugc.get_iddoc_userid(iddoc)
if iddoc_userid == None:
return False,'document not available'
# check if admin/mod or author
if authenticator.is_user() and (userid != iddoc_userid):
return False,'permission denied'
status, iddoc = ugc.edit_comment(iddoc, new_comment, new_title, new_keywords)
if not status:
return False,'document not removed or not available'
return iddoc,'ok'
def docmd_remove_image(self, authenticator, myargs):
if not myargs:
return None,'wrong arguments'
try:
iddoc = int(myargs[0])
except (ValueError,):
return False,'not a valid iddoc'
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
ugc = self._load_ugc_interface()
iddoc_userid = ugc.get_iddoc_userid(iddoc)
if iddoc_userid == None:
return False,'document not available'
# check if admin/mod or author
if authenticator.is_user() and (userid != iddoc_userid):
return False,'permission denied'
ugc = self._load_ugc_interface()
status, iddoc = ugc.delete_image(iddoc)
if not status:
return False,'document not removed or not available'
return iddoc,'ok'
def docmd_remove_file(self, authenticator, myargs):
if not myargs:
return None,'wrong arguments'
try:
iddoc = int(myargs[0])
except (ValueError,):
return False,'not a valid iddoc'
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
ugc = self._load_ugc_interface()
iddoc_userid = ugc.get_iddoc_userid(iddoc)
if iddoc_userid == None:
return False,'document not available'
# check if admin/mod or author
if authenticator.is_user() and (userid != iddoc_userid):
return False,'permission denied'
ugc = self._load_ugc_interface()
status, iddoc = ugc.delete_file(iddoc)
if not status:
return False,'document not removed or not available'
return iddoc,'ok'
def docmd_remove_youtube_video(self, authenticator, myargs):
if not myargs:
return None,'wrong arguments'
try:
iddoc = int(myargs[0])
except (ValueError,):
return False,'not a valid iddoc'
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return False,'no session data available'
ugc = self._load_ugc_interface()
iddoc_userid = ugc.get_iddoc_userid(iddoc)
if iddoc_userid == None:
return False,'document not available'
# check if admin/mod or author
if authenticator.is_user() and (userid != iddoc_userid):
return False,'permission denied'
ugc = self._load_ugc_interface()
status, iddoc = ugc.remove_youtube_video(iddoc)
if not status:
return False,'document not removed or not available'
return iddoc,'ok'
def docmd_do_vote(self, authenticator, myargs):
if len(myargs) < 2:
return None,'wrong arguments'
pkgkey = myargs[0]
vote = myargs[1]
userid = self._get_userid(authenticator)
if userid == None:
return False,'no session userid available'
elif isinstance(userid,bool) and not userid:
return userid,'no session data available'
ugc = self._load_ugc_interface()
voted = ugc.do_vote(pkgkey, userid, vote)
if not voted:
return voted,'already voted'
return voted,'ok'
def docmd_do_download(self, authenticator, myargs):
if not myargs:
return None,'wrong arguments'
pkgkey = myargs[0]
ip_addr = self._get_session_ip_address(authenticator)
ugc = self._load_ugc_interface()
done = ugc.do_download(pkgkey, ip_addr = ip_addr)
if not done:
return done,'download not stored'
return done,'ok'
def docmd_do_downloads(self, authenticator, myargs):
if not myargs:
return None,'wrong arguments'
ip_addr = self._get_session_ip_address(authenticator)
ugc = self._load_ugc_interface()
done = ugc.do_downloads(myargs, ip_addr = ip_addr)
if not done:
return done,'download not stored'
return done,'ok'
def _get_generic_doctypes(self, pkgkey, doctypes):
ugc = self._load_ugc_interface()
metadata = ugc.get_ugc_metadata_doctypes(pkgkey, doctypes)
if not metadata:
return None
return metadata
def _get_generic_doctypes_by_identifiers(self, identifiers, doctypes):
ugc = self._load_ugc_interface()
metadata = ugc.get_ugc_metadata_doctypes_by_identifiers(identifiers, doctypes)
if not metadata:
return None
return metadata
def _get_generic_documents_by_identifiers(self, identifiers):
ugc = self._load_ugc_interface()
metadata = ugc.get_ugc_metadata_by_identifiers(identifiers)
if not metadata:
return None
return metadata
def docmd_get_comments(self, myargs):
if not myargs:
return None,'wrong arguments'
pkgkey = myargs[0]
metadata = self._get_generic_doctypes(pkgkey, [self.DOC_TYPES['comments']])
if metadata == None:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_comments_by_identifiers(self, myargs):
if not myargs:
return None,'wrong arguments'
identifiers = []
for myarg in myargs:
try:
identifiers.append(int(myarg))
except ValueError:
pass
if not identifiers:
return None,'wrong arguments'
metadata = self._get_generic_doctypes_by_identifiers(identifiers, [self.DOC_TYPES['comments']])
if metadata == None:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_documents_by_identifiers(self, myargs):
if not myargs:
return None,'wrong arguments'
identifiers = []
for myarg in myargs:
try:
identifiers.append(int(myarg))
except ValueError:
pass
if not identifiers:
return None,'wrong arguments'
metadata = self._get_generic_documents_by_identifiers(identifiers)
if metadata == None:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_allvotes(self):
ugc = self._load_ugc_interface()
metadata = ugc.get_ugc_allvotes()
if not metadata:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_alldownloads(self):
ugc = self._load_ugc_interface()
metadata = ugc.get_ugc_alldownloads()
if not metadata:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_vote(self, myargs):
if not myargs:
return None,'wrong arguments'
pkgkey = myargs[0]
ugc = self._load_ugc_interface()
vote = ugc.get_ugc_vote(pkgkey)
return vote,'ok'
def docmd_get_downloads(self, myargs):
if not myargs:
return None,'wrong arguments'
pkgkey = myargs[0]
ugc = self._load_ugc_interface()
downloads = ugc.get_ugc_downloads(pkgkey)
return downloads,'ok'
def docmd_get_textdocs(self, myargs):
if not myargs:
return None,'wrong arguments'
pkgkey = myargs[0]
metadata = self._get_generic_doctypes(pkgkey, [self.DOC_TYPES['comments'],self.DOC_TYPES['bbcode_doc']])
if metadata == None:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_textdocs_by_identifiers(self, myargs):
if not myargs:
return None,'wrong arguments'
identifiers = []
for myarg in myargs:
try:
identifiers.append(int(myarg))
except ValueError:
pass
if not identifiers:
return None,'wrong arguments'
metadata = self._get_generic_doctypes_by_identifiers(identifiers, [self.DOC_TYPES['comments'],self.DOC_TYPES['bbcode_doc']])
if metadata == None:
return None,'no metadata available'
return metadata,'ok'
def docmd_get_alldocs(self, myargs):
if not myargs:
return None,'wrong arguments'
pkgkey = myargs[0]
metadata = self._get_generic_doctypes(pkgkey, [self.DOC_TYPES[x] for x in self.DOC_TYPES])
if metadata == None:
return None,'no metadata available'
return metadata,'ok'
class DistributionAuthInterface:
"""just a reference class, methods must be reimplemented"""
def __init__(self):
self.login_data = {}
self.logged_in = False
def check_login(self):
if not self.logged_in:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_("not logged in"),))
def set_login_data(self, data):
self.login_data = data.copy()
def check_login_data(self):
if not self.login_data:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_("no login data"),))
def check_logged_in(self):
if not self.is_logged_in():
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_("not logged in"),))
def _raise_not_implemented_error(self):
raise exceptionTools.NotImplementedError('NotImplementedError: %s' % (_('method not implemented'),))
def check_connection(self):
pass
def login(self):
self.check_connection()
self.check_login_data()
self._raise_not_implemented_error()
self.logged_in = True
return True
def logout(self):
self.check_connection()
self.check_login_data()
self._raise_not_implemented_error()
return True
def is_developer(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return True
def is_administrator(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return True
def is_moderator(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return True
def is_user(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return True
def is_user_banned(self, user):
self.check_connection()
self._raise_not_implemented_error()
return False
def is_in_group(self, group):
mygroup = group
del mygroup
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return True
def get_user_groups(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return {}
def get_user_group(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return -1
def get_user_id(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return -1
def is_logged_in(self):
return self.logged_in
def get_permission_data(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return {}
def get_user_data(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return {}
def get_username(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self._raise_not_implemented_error()
return {}
class phpBB3AuthInterface(DistributionAuthInterface,RemoteDbSkelInterface):
import entropyTools
def __init__(self):
DistributionAuthInterface.__init__(self)
RemoteDbSkelInterface.__init__(self)
self.itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
self.USER_NORMAL = 0
self.USER_INACTIVE = 1
self.USER_IGNORE = 2
self.USER_FOUNDER = 3
self.REGISTERED_USERS_GROUP = 7895
self.ADMIN_GROUPS = [7893, 7898]
self.MODERATOR_GROUPS = [484]
self.DEVELOPER_GROUPS = [7900]
self.USERNAME_LENGTH_RANGE = range(3,21)
self.PASSWORD_LENGTH_RANGE = range(6,31)
self.PRIVMSGS_NO_BOX = -3
self.NOTIFY_EMAIL = 0
self.FAKE_USERNAME = 'already_authed'
self.USER_AGENT = "Entropy/%s (compatible; %s; %s: %s %s %s)" % (
etpConst['entropyversion'],
"Entropy",
"UGC",
os.uname()[0],
os.uname()[4],
os.uname()[2],
)
self.TABLE_PREFIX = 'phpbb_'
self.do_update_session_table = True
def validate_username_regex(self, username):
allow_name_chars = self._get_config_value("allow_name_chars")
if allow_name_chars == "USERNAME_CHARS_ANY":
regex = '.+'
elif allow_name_chars == "USERNAME_ALPHA_ONLY":
regex = '[A-Za-z0-9]+'
elif allow_name_chars == "USERNAME_ALPHA_SPACERS":
regex = '[A-Za-z0-9-[\]_+ ]+'
elif allow_name_chars == "USERNAME_LETTER_NUM":
regex = '[a-zA-Z0-9]+'
elif allow_name_chars == "USERNAME_LETTER_NUM_SPACERS":
regex = '[-\]_+ [a-zA-Z0-9]+'
else: # USERNAME_ASCII
regex = '[\x01-\x7F]+'
regex = "^%s$" % (regex,)
import re
myreg = re.compile(regex)
if myreg.match(username):
del myreg
return True
return False
def does_username_exist(self, username, username_clean):
self.check_connection()
self.cursor.execute('SELECT user_id FROM '+self.TABLE_PREFIX+'users WHERE `username_clean` = %s OR LOWER(`username`) = %s', (username_clean,username.lower(),))
data = self.cursor.fetchone()
if not data: return False
if not isinstance(data,dict): return False
if not data.has_key('user_id'): return False
return True
def does_email_exist(self, email):
self.check_connection()
self.cursor.execute('SELECT user_id FROM '+self.TABLE_PREFIX+'users WHERE `user_email` = %s', (email,))
data = self.cursor.fetchone()
if not data: return False
if not isinstance(data,dict): return False
if not data.has_key('user_id'): return False
return True
def is_username_allowed(self, username):
self.check_connection()
self.cursor.execute('SELECT disallow_id FROM '+self.TABLE_PREFIX+'disallow WHERE `disallow_username` = %s', (username,))
data = self.cursor.fetchone()
if not data: return True
if not isinstance(data,dict): return True
if not data.has_key('disallow_id'): return True
return False
def validate_username_string(self, username, username_clean):
try:
x = unicode(username.encode('utf-8'),'raw_unicode_escape')
del x
except (UnicodeDecodeError,UnicodeEncodeError,):
return False,'Invalid username'
if ("&quot;" in username) or ("'" in username) or ('"' in username):
return False,'Invalid username'
try:
valid = self.validate_username_regex(username)
except:
return False,'Username contains bad characters'
if not valid:
return False,'Invalid username'
exists = self.does_username_exist(username, username_clean)
if exists: return False,'Username already taken'
allowed = self.is_username_allowed(username)
if not allowed: return False,'Username not allowed'
return True,'All fine'
def _generate_email_hash(self, email):
import binascii
return str(binascii.crc32(email.lower())) + str(len(email))
def activate_user(self, user_id):
self.check_connection()
self.cursor.execute('UPDATE '+self.TABLE_PREFIX+'users SET user_type = %s WHERE `user_id` = %s', (self.USER_NORMAL,user_id,))
return True, user_id
def generate_username_clean(self, username):
import re
username_clean = username.lower()
username_clean = re.sub(r'(?:[\x00-\x1F\x7F]+|(?:\xC2[\x80-\x9F])+)', '', username_clean)
username_clean = re.sub(r' {2,}',' ',username_clean)
username_clean = username_clean.strip()
return username_clean
def register_user(self, username, password, email, activate = False):
if len(username) not in self.USERNAME_LENGTH_RANGE:
return False,'Username not in range'
if len(password) not in self.PASSWORD_LENGTH_RANGE:
return False,'Password not in range'
valid = self.entropyTools.is_valid_email(email)
if not valid:
return False,'Invalid email'
# create the clean one
username_clean = self.generate_username_clean(username)
# check username validity
status, err_msg = self.validate_username_string(username, username_clean)
if not status: return False,err_msg
# check email
exists = self.does_email_exist(email)
if exists: return False,'Email already in use'
# now cross fingers
user_id = self.__register(username, username_clean, password, email, activate)
return True, user_id
def __register(self, username, username_clean, password, email, activate):
email_hash = self._generate_email_hash(email)
password_hash = self._get_password_hash(password.encode('utf-8'))
time_now = int(time.time())
user_type = self.USER_INACTIVE
if activate: user_type = self.USER_NORMAL
registration_data = {
'username': username,
'username_clean': username_clean,
'user_password': password_hash,
'user_pass_convert': 0,
'user_email': email.lower(),
'user_email_hash': email_hash,
'group_id': self.REGISTERED_USERS_GROUP,
'user_type': user_type,
'user_permissions': '',
'user_timezone': self._get_config_value('board_timezone'),
'user_dateformat': self._get_config_value('default_dateformat'),
'user_lang': self._get_config_value('default_lang'),
'user_style': self._get_config_value('default_style'),
'user_actkey': '',
'user_ip': '',
'user_regdate': time_now,
'user_passchg': time_now,
'user_options': 895, # ? don't ask me
'user_inactive_reason': 0,
'user_inactive_time': 0,
'user_lastmark': time_now,
'user_lastvisit': 0,
'user_lastpost_time': 0,
'user_lastpage': '',
'user_posts': 0,
'user_dst': self._get_config_value('board_dst'),
'user_colour': '',
'user_occ': '',
'user_interests': '',
'user_avatar': '',
'user_avatar_type': 0,
'user_avatar_width': 0,
'user_avatar_height': 0,
'user_new_privmsg': 0,
'user_unread_privmsg': 0,
'user_last_privmsg': 0,
'user_message_rules': 0,
'user_full_folder': self.PRIVMSGS_NO_BOX,
'user_emailtime': 0,
'user_notify': 0,
'user_notify_pm': 1,
'user_notify_type': self.NOTIFY_EMAIL,
'user_allow_pm': 1,
'user_allow_viewonline': 1,
'user_allow_viewemail': 1,
'user_allow_massemail': 1,
'user_sig': '',
'user_sig_bbcode_uid': '',
'user_sig_bbcode_bitfield': '',
'user_form_salt': self._get_unique_id(),
}
sql = self._generate_sql('insert', self.TABLE_PREFIX+'users', registration_data)
self.cursor.execute(sql)
user_id = self.cursor.lastrowid
# now insert into the default group
group_data = {
'user_id': user_id,
'group_id': self.REGISTERED_USERS_GROUP,
'user_pending': 0,
}
sql = self._generate_sql('insert', self.TABLE_PREFIX+'user_group', group_data)
self.cursor.execute(sql)
# set some misc config shit
self._set_config_value('newest_user_id',user_id)
self._set_config_value('newest_username',username)
self._set_config_value('num_users',int(self._get_config_value('num_users'))+1)
self.cursor.execute('SELECT group_colour FROM '+self.TABLE_PREFIX+'groups WHERE group_id = %s', (group_data['group_id'],))
data = self.cursor.fetchone()
gcolor = None
if isinstance(data,dict):
if data.has_key('group_colour'):
gcolor = data['group_colour']
if gcolor: self._set_config_value('newest_user_colour',gcolor)
return user_id
def login(self):
self.check_connection()
self.check_login_data()
if not self.login_data.has_key('username'):
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('no username specified'),))
elif not self.login_data.has_key('password'):
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('no password specified'),))
if not self.login_data['password']:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('empty password'),))
elif not self.login_data['username']:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('empty username'),))
self.cursor.execute('SELECT * FROM '+self.TABLE_PREFIX+'users WHERE username = %s', (self.login_data['username'],))
data = self.cursor.fetchone()
if not data:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('user not found'),))
if data['user_pass_convert']:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (
_('you need to login on the website to update your password format'),
)
)
valid = self._phpbb3_check_hash(self.login_data['password'], data['user_password'])
if not valid:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('wrong password'),))
user_type = data['user_type']
if (user_type == self.USER_INACTIVE) or (user_type == self.USER_IGNORE):
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('user inactive'),))
banned = self.is_user_banned(data['user_id'])
if banned:
raise exceptionTools.PermissionDenied('PermissionDenied: %s' % (_('user banned'),))
self.login_data.update(data)
self.logged_in = True
return self.logged_in
def disconnect(self):
if self.is_logged_in():
self.logout()
RemoteDbSkelInterface.disconnect(self)
def logout(self):
self.check_connection()
self.check_login_data()
self.logged_in = False
self.login_data.clear()
return True
def get_user_data(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT * FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
return self.cursor.fetchone()
def get_user_birthday(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT user_birthday FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
bday = self.cursor.fetchone()
if not bday:
return None
elif not bday.has_key('user_birthday'):
return None
return bday['user_birthday']
def get_username(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT username_clean FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
data = self.cursor.fetchone()
if not data:
return ''
elif not data.has_key('username_clean'):
return ''
return data['username_clean']
def is_developer(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
# search into phpbb_groups
groups = self.get_user_groups()
for group in groups:
if group in self.DEVELOPER_GROUPS:
return True
return False
def is_administrator(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT user_type FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
data = self.cursor.fetchone()
if data:
if data['user_type'] == self.USER_FOUNDER:
return True
# search into phpbb_groups
groups = self.get_user_groups()
for group in groups:
if group in self.ADMIN_GROUPS:
return True
return False
def is_moderator(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
# search into phpbb_groups
groups = self.get_user_groups()
for group in groups:
if group in self.MODERATOR_GROUPS:
return True
return False
def is_user(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
if self.is_moderator():
return False
elif self.is_administrator():
return False
elif self.is_developer():
return False
self.cursor.execute('SELECT user_type,user_id FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
data = self.cursor.fetchone()
if not data:
return False
if self.is_user_banned(data['user_id']):
return False
elif data['user_type'] in [self.USER_NORMAL]:
return True
return False
# user == user_id
def is_user_banned(self, user):
self.check_connection()
self.cursor.execute('SELECT ban_userid FROM '+self.TABLE_PREFIX+'banlist WHERE ban_userid = %s', (user,))
data = self.cursor.fetchone()
if data:
return True
return False
def is_in_group(self, group):
self.check_connection()
self.check_login_data()
self.check_logged_in()
groups = self.get_user_groups()
if isinstance(group,int):
if group in groups:
return True
elif isinstance(group,basestring):
self.cursor.execute('SELECT group_id FROM '+self.TABLE_PREFIX+'groups WHERE group_name = %s', (group,))
data = self.cursor.fetchone()
if not data:
return False
elif data['group_id'] in groups:
return True
return False
def get_user_groups(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT '+self.TABLE_PREFIX+'user_group.group_id,'+self.TABLE_PREFIX+'groups.group_name FROM '+self.TABLE_PREFIX+'user_group,'+self.TABLE_PREFIX+'users,'+self.TABLE_PREFIX+'groups WHERE '+self.TABLE_PREFIX+'users.user_id = %s and '+self.TABLE_PREFIX+'users.user_id = '+self.TABLE_PREFIX+'user_group.user_id and '+self.TABLE_PREFIX+'user_group.group_id = '+self.TABLE_PREFIX+'groups.group_id', (self.login_data['user_id'],))
data = self.cursor.fetchall()
mydata = {}
for mydict in data:
mydata[mydict['group_id']] = mydict['group_name']
return mydata
def get_user_group(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT group_id FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
data = self.cursor.fetchone()
if data:
if data.has_key('group_id'):
return data['group_id']
return -1
def get_user_id(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
return self.login_data['user_id']
def get_permission_data(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT user_permissions FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
return self.cursor.fetchone()
def update_email(self, email):
self.check_connection()
self.check_login_data()
self.check_logged_in()
email_hash = self._generate_email_hash(email)
mydata = {
'user_email_hash': email_hash,
'user_email': email.lower(),
}
try:
sql = self._generate_sql("update",self.TABLE_PREFIX+'users', mydata, 'user_id = %s' % (self.login_data['user_id'],))
self.cursor.execute(sql)
return True
except Exception:
return False
def update_password_hash(self, password_hash):
self.check_connection()
self.check_login_data()
self.check_logged_in()
mydata = {
'user_password': password_hash,
}
try:
sql = self._generate_sql("update",self.TABLE_PREFIX+'users', mydata, 'user_id = %s' % (self.login_data['user_id'],))
self.cursor.execute(sql)
return True
except Exception:
return False
def get_email(self):
self.check_connection()
self.check_login_data()
self.check_logged_in()
self.cursor.execute('SELECT user_email FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
data = self.cursor.fetchone()
if not data:
return ''
elif not data.has_key('user_email'):
return ''
return data['user_email']
def update_user_id_profile(self, profile_data):
self.check_connection()
self.check_login_data()
self.check_logged_in()
# filter valid params
valid_params = [
"user_icq","user_yim","user_msnm",
"user_jabber","user_website","user_from",
"user_interests","user_occ","user_birthday",
"user_sig"
]
my_params = {}
for param in valid_params:
d = profile_data.get(param)
if d == None: continue
my_params[param] = d
if not my_params:
return False,'no parameters'
# validate parameters
b_day = my_params.get('user_birthday')
if isinstance(b_day,basestring):
import re
myre = re.compile("(0[1-9]|[12][0-9]|3[01])[-](0[1-9]|1[012])[-](19|20)\d\d")
if not myre.match(b_day):
del my_params['user_birthday']
try:
sql = self._generate_sql("update",self.TABLE_PREFIX+'users', my_params, 'user_id = %s' % (self.login_data['user_id'],))
self.cursor.execute(sql)
return True, None
except Exception, e:
return False, unicode(e)
def _set_config_value(self, config_name, data):
self.cursor.execute('UPDATE '+self.TABLE_PREFIX+'config SET config_value = %s WHERE config_name = %s',(data,config_name,))
def _get_config_value(self, config_name):
self.check_connection()
self.cursor.execute('SELECT config_value FROM '+self.TABLE_PREFIX+'config WHERE config_name = %s',(config_name,))
myconfig = self.cursor.fetchone()
if isinstance(myconfig,dict):
if myconfig.has_key('config_value'):
return myconfig['config_value']
return None
def _update_session_table(self, user_id, ip_address):
self.check_connection()
time_now = int(time.time())
autologin = self._get_config_value("allow_autologin")
self.cursor.execute('SELECT user_allow_viewonline FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (user_id,))
myuserprefs = self.cursor.fetchone()
session_admin = 0
session_data = {
'session_id': None,
'session_user_id': user_id,
'session_last_visit': time_now,
'session_start': time_now,
'session_time': time_now,
'session_ip': ip_address,
'session_browser': self.USER_AGENT,
'session_forwarded_for': '',
'session_page': 'index.php',
'session_viewonline': myuserprefs['user_allow_viewonline'],
'session_autologin': autologin,
'session_admin': session_admin,
'session_forum_id': 0,
}
import hashlib
m = hashlib.md5()
m.update(str(user_id)+str(time_now)+str(self.USER_AGENT)+str(ip_address)+str(autologin)+str(myuserprefs['user_allow_viewonline']))
session_data['session_id'] = m.hexdigest()
self.cursor.execute('SELECT * FROM '+self.TABLE_PREFIX+'sessions WHERE session_user_id = %s', (user_id,))
mydata = self.cursor.fetchone()
do_update = False
if mydata:
do_update = True
# update
session_data['session_id'] = mydata['session_id']
session_data['session_viewonline'] = mydata['session_viewonline']
session_data['session_autologin'] = mydata['session_autologin']
session_data['session_forwarded_for'] = mydata['session_forwarded_for']
session_data['session_forum_id'] = mydata['session_forum_id']
session_data['session_page'] = mydata['session_page']
session_data['session_browser'] = mydata['session_browser']
session_data['session_admin'] = mydata['session_admin']
if do_update:
where = "session_id = '%s'" % (session_data['session_id'],)
del session_data['session_id']
sql = self._generate_sql('update', self.TABLE_PREFIX+'sessions', session_data, where)
else:
sql = self._generate_sql('insert', self.TABLE_PREFIX+'sessions', session_data)
if sql:
self.cursor.execute(sql)
self.dbconn.commit()
def _is_ip_banned(self, ip):
self.check_connection()
self.cursor.execute('SELECT ban_ip FROM '+self.TABLE_PREFIX+'banlist WHERE ban_ip = %s', (ip,))
data = self.cursor.fetchone()
if data:
return True
return False
def _get_unique_id(self):
import hashlib
m = hashlib.md5()
rnd = str(abs(hash(os.urandom(20))))
m.update(rnd)
x = m.hexdigest()[:-16]
del m
return x
def _get_random_number(self):
myrand = 0
while (myrand < 100000) or (myrand > 999999):
myrand = hash(os.urandom(50))%999999
return myrand
def _get_password_hash(self, password):
#random_state = self._get_unique_id()
myrandom = str(self._get_random_number())
myhash = self._hash_crypt_private(password, self._hash_gensalt_private(myrandom))
if len(myhash) == 34:
return myhash
import hashlib
m = hashlib.md5()
m.update(myhash)
return m.hexdigest()
def _hash_gensalt_private(self, myinput, iteration_count_log2 = 6):
if (iteration_count_log2 < 4) or (iteration_count_log2 > 31):
iteration_count_log2 = 8
myoutput = '$H$'
myoutput += self.itoa64[min(iteration_count_log2 + 5,30)]
myoutput += self._hash_encode64(myinput, 6)
return myoutput
def _hash_crypt_private(self, password, setting):
myoutput = '*'
# Check for correct hash
if setting[:3] != '$H$':
return myoutput
count_log2 = self.itoa64.find(setting[3])
if count_log2 == -1: count_log2 = 0
if (count_log2 < 7) or (count_log2 > 30):
return myoutput
count = 1 << count_log2
salt = setting[4:12]
if len(salt) != 8:
return myoutput
import hashlib
m = hashlib.md5()
m.update(salt+password)
myhash = m.digest()
while count:
m = hashlib.md5()
m.update(myhash+password)
myhash = m.digest()
count -= 1
myoutput = setting[:12]
myoutput += self._hash_encode64(myhash, 16)
return myoutput
def _hash_encode64(self, myinput, count):
output = ''
i = 0
while i < count:
value = ord(myinput[i])
i += 1
output += self.itoa64[value & 0x3f]
if i < count:
value |= ord(myinput[i]) << 8
output += self.itoa64[(value >> 6) & 0x3f]
if i >= count:
break
i += 1
if i < count:
value |= ord(myinput[i]) << 16
output += self.itoa64[(value >> 12) & 0x3f]
if (i >= count):
break
i += 1
output += self.itoa64[(value >> 18) & 0x3f]
return output
def _phpbb3_check_hash(self, password, myhash):
if len(myhash) == 34:
return self._hash_crypt_private(password, myhash) == myhash
import hashlib
m = hashlib.md5()
m.update(password)
rhash = m.hexdigest()
return rhash == myhash
# Authenticator that can be used by SocketHostInterface based instances
class phpbb3Authenticator(phpBB3AuthInterface,SocketAuthenticatorSkel):
import entropyTools
def __init__(self, HostInterface, *args, **kwargs):
SocketAuthenticatorSkel.__init__(self, HostInterface)
phpBB3AuthInterface.__init__(self)
self.set_connection_data(kwargs)
self.connect()
def set_session(self, session):
self.session = session
session_data = self.HostInterface.sessions.get(self.session)
if not session_data:
return
auth_id = session_data['auth_uid']
if auth_id:
self.logged_in = True
# fill login_data with fake information
self.login_data = {'username': self.FAKE_USERNAME, 'password': 'look elsewhere, this is not a password', 'user_id': auth_id}
ip_address = session_data.get('ip_address')
if ip_address and self.do_update_session_table:
self._update_session_table(auth_id, ip_address)
def docmd_login(self, arguments):
# filter n00bs
if not arguments or (len(arguments) != 2):
return False,None,None,'wrong arguments'
ip_address = None
session_data = self.HostInterface.sessions.get(self.session)
if session_data:
ip_address = session_data.get('ip_address')
user = arguments[0]
password = arguments[1]
if ip_address:
if self._is_ip_banned(ip_address):
return False,user,None,"banned IP"
login_data = {'username': user, 'password': password}
self.set_login_data(login_data)
rc = False
try:
rc = self.login()
except exceptionTools.PermissionDenied, e:
return rc,user,None,e.value
if rc:
uid = self.get_user_id()
is_admin = self.is_administrator()
is_dev = self.is_developer()
is_mod = self.is_moderator()
is_user = self.is_user()
self.HostInterface.sessions[self.session]['admin'] = is_admin
self.HostInterface.sessions[self.session]['developer'] = is_dev
self.HostInterface.sessions[self.session]['moderator'] = is_mod
self.HostInterface.sessions[self.session]['user'] = is_user
if ip_address and uid and self.do_update_session_table:
self._update_session_table(uid, ip_address)
return True,user,uid,"ok"
return rc,user,None,"login failed"
# if we get here it means we are logged in
def docmd_userdata(self):
data = self.get_user_data()
return True, data, 'ok'
def docmd_logout(self, myargs):
# filter n00bs
if (len(myargs) < 1) or (len(myargs) > 1):
return False,None,'wrong arguments'
user = myargs[0]
# filter n00bs
if not user or not isinstance(user,basestring):
return False,None,"wrong user"
if not self.is_logged_in():
return False,user,"already logged out"
return True,user,"ok"
def set_exc_permissions(self, *args, **kwargs):
pass
def hide_login_data(self, args):
myargs = args[:]
myargs[-1] = 'hidden'
return myargs
def terminate_instance(self):
self.disconnect()
# commands that can be used by SocketHostInterface based instances, with phpbb3Authenticator
class phpbb3Commands(SocketCommandsSkel):
import dumpTools
import entropyTools
def __init__(self, HostInterface):
SocketCommandsSkel.__init__(self, HostInterface, inst_name = "phpbb3-commands")
self.valid_commands = {
'is_user': {
'auth': True,
'built_in': False,
'cb': self.docmd_is_user,
'args': ["authenticator"],
'as_user': False,
'desc': "returns whether the username linked with the session belongs to a simple user",
'syntax': "<SESSION_ID> is_user",
'from': unicode(self), # from what class
},
'is_developer': {
'auth': True,
'built_in': False,
'cb': self.docmd_is_developer,
'args': ["authenticator"],
'as_user': False,
'desc': "returns whether the username linked with the session belongs to a developer",
'syntax': "<SESSION_ID> is_developer",
'from': unicode(self), # from what class
},
'is_moderator': {
'auth': True,
'built_in': False,
'cb': self.docmd_is_moderator,
'args': ["authenticator"],
'as_user': False,
'desc': "returns whether the username linked with the session belongs to a moderator",
'syntax': "<SESSION_ID> is_moderator",
'from': unicode(self), # from what class
},
'is_administrator': {
'auth': True,
'built_in': False,
'cb': self.docmd_is_administrator,
'args': ["authenticator"],
'as_user': False,
'desc': "returns whether the username linked with the session belongs to an administrator",
'syntax': "<SESSION_ID> is_administrator",
'from': unicode(self), # from what class
},
}
def docmd_is_user(self, authenticator):
return authenticator.is_user(),'ok'
def docmd_is_developer(self, authenticator):
return authenticator.is_developer(),'ok'
def docmd_is_administrator(self, authenticator):
return authenticator.is_administrator(),'ok'
def docmd_is_moderator(self, authenticator):
return authenticator.is_moderator(),'ok'
class RepositorySocketServerInterface(SocketHostInterface):
class RepositoryCommands(SocketCommandsSkel):
import dumpTools
import entropyTools
def __init__(self, HostInterface):
SocketCommandsSkel.__init__(self, HostInterface, inst_name = "repository_server")
self.valid_commands = {
'repository_server:dbdiff': {
'auth': False,
'built_in': False,
'cb': self.docmd_dbdiff,
'args': ["myargs"],
'as_user': False,
'desc': "returns idpackage differences against the latest available repository",
'syntax': "<SESSION_ID> repository_server:dbdiff <repository> <arch> <product> <branch> [idpackages]",
'from': unicode(self), # from what class
},
'repository_server:pkginfo': {
'auth': False,
'built_in': False,
'cb': self.docmd_pkginfo,
'args': ["myargs"],
'as_user': False,
'desc': "returns idpackage differences against the latest available repository",
'syntax': "<SESSION_ID> repository_server:pkginfo <content fmt True/False> <repository> <arch> <product> <branch> <idpackage>",
'from': unicode(self), # from what class
},
'repository_server:treeupdates': {
'auth': False,
'built_in': False,
'cb': self.docmd_treeupdates,
'args': ["myargs"],
'as_user': False,
'desc': "returns repository treeupdates metadata",
'syntax': "<SESSION_ID> repository_server:treeupdates <repository> <arch> <product> <branch>",
'from': unicode(self), # from what class
},
'repository_server:get_package_sets': {
'auth': False,
'built_in': False,
'cb': self.docmd_package_sets,
'args': ["myargs"],
'as_user': False,
'desc': "returns repository package sets metadata",
'syntax': "<SESSION_ID> repository_server:get_package_sets <repository> <arch> <product> <branch>",
'from': unicode(self), # from what class
}
}
def trash_old_databases(self):
for db in self.HostInterface.syscache['db_trashed']:
db.closeDB()
self.HostInterface.syscache['db_trashed'].clear()
def docmd_dbdiff(self, myargs):
self.trash_old_databases()
if len(myargs) < 5:
return None
repository = myargs[0]
arch = myargs[1]
product = myargs[2]
try:
branch = str(myargs[3])
except (UnicodeEncodeError,UnicodeDecodeError,):
return None
foreign_idpackages = myargs[4:]
x = (repository,arch,product,branch,)
valid = self.HostInterface.is_repository_available(x)
if not valid:
return valid
dbpath = self.get_database_path(repository, arch, product, branch)
dbconn = self.HostInterface.open_db(dbpath, docache = False)
mychecksum = dbconn.database_checksum(do_order = True, strict = False, strings = True)
myids = dbconn.listAllIdpackages()
dbconn.closeDB()
foreign_idpackages = set(foreign_idpackages)
removed_ids = foreign_idpackages - myids
added_ids = myids - foreign_idpackages
return {'removed': removed_ids, 'added': added_ids, 'checksum': mychecksum}
def docmd_package_sets(self, myargs):
self.trash_old_databases()
if len(myargs) < 4:
return None
repository = myargs[0]
arch = myargs[1]
product = myargs[2]
try:
branch = str(myargs[3])
except (UnicodeEncodeError,UnicodeDecodeError,):
return None
x = (repository,arch,product,branch,)
valid = self.HostInterface.is_repository_available(x)
if not valid:
return valid
cached = self.HostInterface.get_dcache((repository, arch, product, branch, 'docmd_package_sets'), repository)
if cached != None:
return cached
dbpath = self.get_database_path(repository, arch, product, branch)
dbconn = self.HostInterface.open_db(dbpath, docache = False)
# get data
data = dbconn.retrievePackageSets()
self.HostInterface.set_dcache((repository, arch, product, branch, 'docmd_package_sets'), data, repository)
dbconn.closeDB()
return data
def docmd_treeupdates(self, myargs):
self.trash_old_databases()
if len(myargs) < 4:
return None
repository = myargs[0]
arch = myargs[1]
product = myargs[2]
try:
branch = str(myargs[3])
except (UnicodeEncodeError,UnicodeDecodeError,):
return None
x = (repository,arch,product,branch,)
valid = self.HostInterface.is_repository_available(x)
if not valid:
return valid
cached = self.HostInterface.get_dcache((repository, arch, product, branch, 'docmd_treeupdates'), repository)
if cached != None:
return cached
dbpath = self.get_database_path(repository, arch, product, branch)
dbconn = self.HostInterface.open_db(dbpath, docache = False)
# get data
data = {}
data['actions'] = dbconn.listAllTreeUpdatesActions()
data['digest'] = dbconn.retrieveRepositoryUpdatesDigest(repository)
self.HostInterface.set_dcache((repository, arch, product, branch, 'docmd_treeupdates'), data, repository)
dbconn.closeDB()
return data
def docmd_pkginfo(self, myargs):
self.trash_old_databases()
if len(myargs) < 6:
return None
format_content_for_insert = myargs[0]
if type(format_content_for_insert) is not bool:
format_content_for_insert = False
repository = myargs[1]
arch = myargs[2]
product = myargs[3]
try:
branch = str(myargs[4])
except (UnicodeEncodeError,UnicodeDecodeError,):
return None
zidpackages = myargs[5:]
idpackages = []
for idpackage in zidpackages:
if type(idpackage) is int:
idpackages.append(idpackage)
if not idpackages:
return None
idpackages = tuple(sorted(idpackages))
x = (repository,arch,product,branch,)
valid = self.HostInterface.is_repository_available(x)
if not valid:
return valid
cached = self.HostInterface.get_dcache(
(repository, arch, product, branch, idpackages, 'docmd_pkginfo'),
repository
)
if cached != None:
return cached
dbpath = self.get_database_path(repository, arch, product, branch)
dbconn = self.HostInterface.open_db(dbpath, docache = False)
result = {}
for idpackage in idpackages:
try:
mydata = dbconn.getPackageData(
idpackage,
content_insert_formatted = format_content_for_insert,
trigger_unicode = True
)
except:
self.entropyTools.printTraceback()
self.entropyTools.printTraceback(f = self.HostInterface.socketLog)
dbconn.closeDB()
return None
result[idpackage] = mydata.copy()
self.HostInterface.set_dcache(
(repository, arch, product, branch, idpackages, 'docmd_pkginfo'),
result,
repository
)
dbconn.closeDB()
return result
def get_database_path(self, repository, arch, product, branch):
repoitems = (repository,arch,product,branch,)
mydbroot = self.HostInterface.repositories[repoitems]['dbpath']
dbpath = os.path.join(mydbroot,etpConst['etpdatabasefile'])
return dbpath
class ServiceInterface(TextInterface):
def __init__(self, *args, **kwargs):
pass
import entropyTools, dumpTools
def __init__(self, repositories, do_ssl = False, stdout_logging = True, **kwargs):
self.Entropy = EquoInterface(noclientdb = 2)
self.do_ssl = do_ssl
self.LockScanner = None
self.syscache = {
'db': {},
'db_trashed': set(),
'dbs_not_available': set(),
}
etpConst['socket_service']['max_connections'] = 5000
etpConst['socketloglevel'] = 1
if not kwargs.has_key('external_cmd_classes'):
kwargs['external_cmd_classes'] = []
kwargs['external_cmd_classes'].insert(0,self.RepositoryCommands)
SocketHostInterface.__init__(
self,
self.ServiceInterface,
noclientdb = 2,
sock_output = self.Entropy,
ssl = do_ssl,
**kwargs
)
self.stdout_logging = stdout_logging
self.repositories = repositories
self.expand_repositories()
# start timed lock file scanning
self.start_repository_lock_scanner()
def killall(self):
SocketHostInterface.killall(self)
if self.LockScanner != None:
self.LockScanner.kill()
def start_repository_lock_scanner(self):
self.LockScanner = self.entropyTools.TimeScheduled( self.lock_scan, 0.5 )
self.LockScanner.setName("Lock_Scanner::"+str(abs(hash(os.urandom(20)))))
self.LockScanner.start()
def set_repository_db_availability(self, repo_tuple):
self.repositories[repo_tuple]['enabled'] = False
mydbpath = os.path.join(self.repositories[repo_tuple]['dbpath'],etpConst['etpdatabasefile'])
if os.path.isfile(mydbpath) and os.access(mydbpath, os.W_OK):
self.syscache['dbs_not_available'].discard(repo_tuple)
self.repositories[repo_tuple]['enabled'] = True
def is_repository_available(self, repo_tuple):
if repo_tuple not in self.repositories:
return None
# is repository being updated
if self.repositories[repo_tuple]['locked']:
return False
# repository database does not exist
if not self.repositories[repo_tuple]['enabled']:
return False
return True
def lock_scan(self):
do_clear = set()
for repository,arch,product,branch in self.repositories:
x = (repository,arch,product,branch,)
self.set_repository_db_availability(x)
if not self.repositories[x]['enabled']:
if x in self.syscache['dbs_not_available']:
continue
self.syscache['dbs_not_available'].add(x)
mytxt = blue("%s.") % (_("database does not exist. Locking services for it"),)
self.updateProgress(
"[%s] %s" % (
brown(str(x)),
mytxt,
),
importance = 1,
type = "info"
)
do_clear.add(repository)
continue
if os.path.isfile(self.repositories[x]['download_lock']) and \
not self.repositories[x]['locked']:
self.repositories[x]['locked'] = True
mydbpath = os.path.join(self.repositories[x]['dbpath'],etpConst['etpdatabasefile'])
self.close_db(mydbpath)
self.eapi3_lock_repo(*x)
do_clear.add(repository)
mytxt = blue("%s.") % (_("database got locked. Locking services for it"),)
self.updateProgress(
"[%s] %s" % (
brown(str(x)),
mytxt,
),
importance = 1,
type = "info"
)
elif not os.path.isfile(self.repositories[x]['download_lock']) and \
self.repositories[x]['locked']:
mydbpath = os.path.join(self.repositories[x]['dbpath'],etpConst['etpdatabasefile'])
self.close_db(mydbpath)
mytxt = blue("%s. %s:") % (
_("unlocking and indexing database"),
_("hash"),
)
self.updateProgress(
"[%s] %s" % (
brown(str(x)),
mytxt,
),
importance = 1,
type = "info"
)
# woohoo, got unlocked eventually
mydb = self.open_db(mydbpath, docache = False)
mydb.createAllIndexes()
self.updateProgress(
str(mydb.database_checksum(do_order = True, strict = False, strings = True)),
importance = 1,
type = "info"
)
mydb.closeDB()
self.Entropy.clear_dump_cache(etpCache['repository_server']+"/"+repository+"/")
self.repositories[x]['locked'] = False
self.eapi3_unlock_repo(*x)
for repo in do_clear:
self.Entropy.clear_dump_cache(etpCache['repository_server']+"/"+repo+"/")
def eapi3_lock_repo(self, repository, arch, product, branch):
lock_file = os.path.join(self.repositories[(repository, arch, product, branch,)]['dbpath'],etpConst['etpdatabaseeapi3lockfile'])
if not os.path.lexists(lock_file):
f = open(lock_file,"w")
f.write("this repository is EAPI3 locked")
f.flush()
f.close()
def eapi3_unlock_repo(self, repository, arch, product, branch):
lock_file = os.path.join(self.repositories[(repository, arch, product, branch,)]['dbpath'],etpConst['etpdatabaseeapi3lockfile'])
if os.path.isfile(lock_file):
os.remove(lock_file)
def get_dcache(self, item, repo = '_norepo_'):
return self.dumpTools.loadobj(etpCache['repository_server']+"/"+repo+"/"+str(hash(item)))
def set_dcache(self, item, data, repo = '_norepo_'):
self.dumpTools.dumpobj(etpCache['repository_server']+"/"+repo+"/"+str(hash(item)),data)
def close_db(self, dbpath):
try:
dbc = self.syscache['db'].pop(dbpath)
dbc.closeDB()
except KeyError:
pass
except dbapi2.ProgrammingError:
# they've been opened by the Commands Processor
self.syscache['db_trashed'].add(dbc)
def open_db(self, dbpath, docache = True):
if docache:
cached = self.syscache['db'].get(dbpath)
if cached != None:
return cached
dbc = self.Entropy.openGenericDatabase(
dbpath,
xcache = False,
readOnly = True,
skipChecks = True
)
if docache:
self.syscache['db'][dbpath] = dbc
return dbc
def expand_repositories(self):
for repository,arch,product, branch in self.repositories:
x = (repository,arch,product,branch,)
self.repositories[x]['locked'] = True # loading locked
self.set_repository_db_availability(x)
mydbpath = self.repositories[x]['dbpath']
myrevfile = os.path.join(mydbpath,etpConst['etpdatabaserevisionfile'])
myrev = '0'
if os.path.isfile(myrevfile):
while 1:
try:
f = open(myrevfile)
myrev = f.readline().strip()
f.close()
except IOError: # should never happen but who knows
continue
break
self.repositories[x]['dbrevision'] = myrev
self.repositories[x]['download_lock'] = os.path.join(
mydbpath,
etpConst['etpdatabasedownloadlockfile']
)
class EntropySocketClientCommands:
def __init__(self, Entropy, Service):
if not isinstance(Entropy, (EquoInterface, ServerInterface)) and \
not issubclass(Entropy, (EquoInterface, ServerInterface)):
mytxt = _("A valid EquoInterface/ServerInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (Entropy,mytxt,))
if not isinstance(Service, (SystemSocketClientInterface,)) and \
not issubclass(Service, (SystemSocketClientInterface,)):
mytxt = _("A valid SystemSocketClientInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (Service,mytxt,))
import entropyTools, socket, zlib, struct, dumpTools
self.entropyTools, self.socket, self.zlib, self.struct, self.dumpTools = entropyTools, socket, zlib, struct, dumpTools
self.Entropy = Entropy
self.Service = Service
self.output_header = ''
def handle_standard_answer(self, data, repository = None, arch = None, product = None):
do_skip = False
# elaborate answer
if data == None:
mytxt = _("feature not supported remotely")
self.Entropy.updateProgress(
"[%s:%s|%s:%s|%s:%s] %s" % (
darkblue(_("repo")),
bold(str(repository)),
darkred(_("arch")),
bold(str(arch)),
darkgreen(_("product")),
bold(str(product)),
blue(mytxt),
),
importance = 1,
type = "error",
header = self.output_header
)
do_skip = True
elif not data:
mytxt = _("service temporarily not available")
self.Entropy.updateProgress(
"[%s:%s|%s:%s|%s:%s] %s" % (
darkblue(_("repo")),
bold(str(repository)),
darkred(_("arch")),
bold(str(arch)),
darkgreen(_("product")),
bold(str(product)),
blue(mytxt),
),
importance = 1,
type = "error",
header = self.output_header
)
do_skip = True
elif data == self.Service.answers['no']:
# command failed
mytxt = _("command failed")
self.Entropy.updateProgress(
"[%s:%s|%s:%s|%s:%s] %s" % (
darkblue(_("repo")),
bold(str(repository)),
darkred(_("arch")),
bold(str(arch)),
darkgreen(_("product")),
bold(str(product)),
blue(mytxt),
),
importance = 1,
type = "error",
header = self.output_header
)
elif data != self.Service.answers['ok']:
mytxt = _("received wrong answer")
self.Entropy.updateProgress(
"[%s:%s|%s:%s|%s:%s] %s: %s" % (
darkblue(_("repo")),
bold(str(repository)),
darkred(_("arch")),
bold(str(arch)),
darkgreen(_("product")),
bold(str(product)),
blue(mytxt),
repr(data),
),
importance = 1,
type = "error",
header = self.output_header
)
do_skip = True
return do_skip
def get_result(self, session):
# get the information
cmd = "%s rc" % (session,)
self.Service.transmit(cmd)
try:
data = self.Service.receive()
return data
except:
self.entropyTools.printTraceback()
return None
def convert_stream_to_object(self, data, gzipped, repository = None, arch = None, product = None):
# unstream object
error = False
try:
data = self.Service.stream_to_object(data, gzipped)
except (EOFError,IOError,self.zlib.error,self.dumpTools.pickle.UnpicklingError,):
mytxt = _("cannot convert stream into object")
self.Entropy.updateProgress(
"[%s:%s|%s:%s|%s:%s] %s" % (
darkblue(_("repo")),
bold(unicode(repository)),
darkred(_("arch")),
bold(unicode(arch)),
darkgreen(_("product")),
bold(unicode(product)),
blue(mytxt),
),
importance = 1,
type = "error",
header = self.output_header
)
data = None
error = True
return data, error
def retrieve_command_answer(self, cmd, session_id, repository = None, arch = None, product = None, compression = False):
tries = 3
lasterr = None
while 1:
if tries <= 0:
return lasterr
tries -= 1
try:
# send command
self.Service.transmit(cmd)
except (exceptionTools.SSLError,):
return None
# receive answer
data = self.Service.receive()
skip = self.handle_standard_answer(data, repository, arch, product)
if skip:
continue
data = self.get_result(session_id)
if data == None:
lasterr = None
continue
elif not data:
lasterr = False
continue
objdata, error = self.convert_stream_to_object(data, compression, repository, arch, product)
if not error:
return objdata
def do_generic_handler(self, cmd, session_id, tries = 10, compression = False):
self.Service.check_socket_connection()
while 1:
try:
result = self.retrieve_command_answer(cmd, session_id, compression = compression)
if result == None:
return False,'command not supported' # untranslated on purpose
return result
except (self.socket.error,self.struct.error,):
self.Service.reconnect_socket()
tries -= 1
if tries < 1:
raise
def set_gzip_compression(self, session, do):
self.Service.check_socket_connection()
cmd = "%s %s %s %s zlib" % (session, 'session_config', 'compression', do,)
self.Service.transmit(cmd)
data = self.Service.receive()
if data == self.Service.answers['ok']:
return True
return False
def service_login(self, username, password, session_id):
cmd = "%s %s %s %s" % (
session_id,
'login',
username,
password,
)
return self.do_generic_handler(cmd, session_id)
def service_logout(self, username, session_id):
cmd = "%s %s %s" % (
session_id,
'logout',
username,
)
return self.do_generic_handler(cmd, session_id)
def get_logged_user_data(self, session_id):
cmd = "%s %s" % (
session_id,
'user_data',
)
return self.do_generic_handler(cmd, session_id)
def is_user(self, session_id):
cmd = "%s %s" % (
session_id,
'is_user',
)
return self.do_generic_handler(cmd, session_id)
def is_developer(self, session_id):
cmd = "%s %s" % (
session_id,
'is_developer',
)
return self.do_generic_handler(cmd, session_id)
def is_moderator(self, session_id):
cmd = "%s %s" % (
session_id,
'is_moderator',
)
return self.do_generic_handler(cmd, session_id)
def is_administrator(self, session_id):
cmd = "%s %s" % (
session_id,
'is_administrator',
)
return self.do_generic_handler(cmd, session_id)
def available_commands(self, session_id):
cmd = "%s %s" % (
session_id,
'available_commands',
)
return self.do_generic_handler(cmd, session_id)
class RepositorySocketClientCommands(EntropySocketClientCommands):
def __init__(self, EntropyInterface, ServiceInterface):
EntropySocketClientCommands.__init__(self, EntropyInterface, ServiceInterface)
def differential_packages_comparison(self, session_id, idpackages, repository, arch, product):
myidlist = ' '.join([str(x) for x in idpackages])
cmd = "%s %s %s %s %s %s %s" % (
session_id,
'repository_server:dbdiff',
repository,
arch,
product,
etpConst['branch'],
myidlist,
)
# enable zlib compression
compression = self.set_gzip_compression(session_id, True)
data = self.do_generic_handler(cmd, session_id, tries = 5, compression = compression)
# disable compression
self.set_gzip_compression(session_id, False)
return data
def get_repository_treeupdates(self, session_id, repository, arch, product):
cmd = "%s %s %s %s %s %s" % (
session_id,
'repository_server:treeupdates',
repository,
arch,
product,
etpConst['branch'],
)
return self.do_generic_handler(cmd, session_id, tries = 5)
def get_package_sets(self, session_id, repository, arch, product):
cmd = "%s %s %s %s %s %s" % (
session_id,
'repository_server:get_package_sets',
repository,
arch,
product,
etpConst['branch'],
)
return self.do_generic_handler(cmd, session_id, tries = 5)
def get_package_information(self, session_id, idpackages, repository, arch, product):
cmd = "%s %s %s %s %s %s %s %s" % (
session_id,
'repository_server:pkginfo',
True,
repository,
arch,
product,
etpConst['branch'],
' '.join([str(x) for x in idpackages]),
)
# enable zlib compression
compression = self.set_gzip_compression(session_id, True)
data = self.do_generic_handler(cmd, session_id, compression = compression)
# disable compression
self.set_gzip_compression(session_id, False)
return data
def ugc_do_download(self, session_id, pkgkey):
cmd = "%s %s %s" % (
session_id,
'ugc:do_download',
pkgkey,
)
return self.do_generic_handler(cmd, session_id)
def ugc_do_downloads(self, session_id, pkgkeys):
cmd = "%s %s %s" % (
session_id,
'ugc:do_downloads',
' '.join(pkgkeys),
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_downloads(self, session_id, pkgkey):
cmd = "%s %s %s" % (
session_id,
'ugc:get_downloads',
pkgkey,
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_alldownloads(self, session_id):
cmd = "%s %s" % (
session_id,
'ugc:get_alldownloads',
)
# enable zlib compression
compression = self.set_gzip_compression(session_id, True)
rc = self.do_generic_handler(cmd, session_id, compression = compression)
# disable compression
self.set_gzip_compression(session_id, False)
return rc
def ugc_do_vote(self, session_id, pkgkey, vote):
cmd = "%s %s %s %s" % (
session_id,
'ugc:do_vote',
pkgkey,
vote,
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_vote(self, session_id, pkgkey):
cmd = "%s %s %s" % (
session_id,
'ugc:get_vote',
pkgkey,
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_allvotes(self, session_id):
cmd = "%s %s" % (
session_id,
'ugc:get_allvotes',
)
# enable zlib compression
compression = self.set_gzip_compression(session_id, True)
rc = self.do_generic_handler(cmd, session_id, compression = compression)
# disable compression
self.set_gzip_compression(session_id, False)
return rc
def ugc_add_comment(self, session_id, pkgkey, comment, title, keywords):
mydict = {
'comment': comment,
'title': title,
'keywords': keywords,
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s %s" % (
session_id,
'ugc:add_comment',
pkgkey,
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def ugc_edit_comment(self, session_id, iddoc, new_comment, new_title, new_keywords):
mydict = {
'comment': new_comment,
'title': new_title,
'keywords': new_keywords,
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s %s" % (
session_id,
'ugc:edit_comment',
iddoc,
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def ugc_remove_comment(self, session_id, iddoc):
cmd = "%s %s %s" % (
session_id,
'ugc:remove_comment',
iddoc,
)
return self.do_generic_handler(cmd, session_id)
def ugc_remove_image(self, session_id, iddoc):
cmd = "%s %s %s" % (
session_id,
'ugc:remove_image',
iddoc,
)
return self.do_generic_handler(cmd, session_id)
def ugc_remove_file(self, session_id, iddoc):
cmd = "%s %s %s" % (
session_id,
'ugc:remove_file',
iddoc,
)
return self.do_generic_handler(cmd, session_id)
def ugc_remove_youtube_video(self, session_id, iddoc):
cmd = "%s %s %s" % (
session_id,
'ugc:remove_youtube_video',
iddoc,
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_docs(self, session_id, pkgkey):
cmd = "%s %s %s" % (
session_id,
'ugc:get_alldocs',
pkgkey,
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_textdocs(self, session_id, pkgkey):
cmd = "%s %s %s" % (
session_id,
'ugc:get_textdocs',
pkgkey,
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_textdocs_by_identifiers(self, session_id, identifiers):
cmd = "%s %s %s" % (
session_id,
'ugc:get_textdocs_by_identifiers',
' '.join([str(x) for x in identifiers]),
)
return self.do_generic_handler(cmd, session_id)
def ugc_get_documents_by_identifiers(self, session_id, identifiers):
cmd = "%s %s %s" % (
session_id,
'ugc:get_documents_by_identifiers',
' '.join([str(x) for x in identifiers]),
)
return self.do_generic_handler(cmd, session_id)
def ugc_send_file_stream(self, session_id, file_path):
if not (os.path.isfile(file_path) and os.access(file_path,os.R_OK)):
return False,False,'cannot read file_path'
import zlib
# enable stream
cmd = "%s %s %s on" % (
session_id,
'session_config',
'stream',
)
status, msg = self.do_generic_handler(cmd, session_id)
if not status:
return False,status,msg
# enable zlib compression
compression = self.set_gzip_compression(session_id, True)
# start streamer
stream_status = True
stream_msg = 'ok'
f = open(file_path,"rb")
chunk = f.read(8192)
base_path = os.path.basename(file_path)
transferred = len(chunk)
max_size = self.entropyTools.get_file_size(file_path)
while chunk:
if (not self.Service.quiet) or self.Service.show_progress:
self.Entropy.updateProgress(
"%s, %s: %s" % (
blue(_("User Generated Content")),
darkgreen(_("sending file")),
darkred(base_path),
),
importance = 1,
type = "info",
header = brown(" @@ "),
back = True,
count = (transferred,max_size,),
percent = True
)
chunk = zlib.compress(chunk, 7) # compression level 1-9
cmd = "%s %s %s" % (
session_id,
'stream',
chunk,
)
status, msg = self.do_generic_handler(cmd, session_id, compression = compression)
if not status:
stream_status = status
stream_msg = msg
break
chunk = f.read(8192)
transferred += len(chunk)
f.close()
# disable compression
self.set_gzip_compression(session_id, False)
# disable config
cmd = "%s %s %s off" % (
session_id,
'session_config',
'stream',
)
status, msg = self.do_generic_handler(cmd, session_id)
if not status:
return False,status,msg
return True,stream_status,stream_msg
def ugc_send_file(self, session_id, pkgkey, file_path, doc_type, title, description, keywords):
status, rem_status, err_msg = self.ugc_send_file_stream(session_id, file_path)
if not (status and rem_status):
return False,err_msg
mydict = {
'doc_type': str(doc_type),
'title': title,
'description': description,
'keywords': keywords,
'file_name': os.path.join(pkgkey,os.path.basename(file_path)),
'real_filename': os.path.basename(file_path),
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s %s" % (
session_id,
'ugc:register_stream',
pkgkey,
xml_string,
)
return self.do_generic_handler(cmd, session_id)
class SystemSocketClientInterface:
import socket
import dumpTools
import entropyTools
import zlib
import select
def __init__(self, EntropyInterface, ClientCommandsClass, quiet = False, show_progress = True, output_header = '', ssl = False, socket_timeout = 25): #, server_ca_cert = None, server_cert = None):
if not isinstance(EntropyInterface, (EquoInterface, ServerInterface)) and \
not issubclass(EntropyInterface, (EquoInterface, ServerInterface)):
mytxt = _("A valid EquoInterface/ServerInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (EntropyInterface,mytxt,))
if not issubclass(ClientCommandsClass, (EntropySocketClientCommands,)):
mytxt = _("A valid EntropySocketClientCommands based class is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (ClientCommandsClass,mytxt,))
self.setup_ssl(ssl) #, server_ca_cert, server_cert)
self.answers = etpConst['socket_service']['answers']
self.Entropy = EntropyInterface
self.sock_conn = None
self.real_sock_conn = None
self.hostname = None
self.hostport = None
self.buffered_data = ''
self.buffer_length = None
self.quiet = quiet
self.show_progress = show_progress
self.output_header = output_header
self.CmdInterface = ClientCommandsClass(self.Entropy, self)
self.CmdInterface.output_header = self.output_header
self.socket_timeout = socket_timeout
self.socket.setdefaulttimeout(self.socket_timeout)
def setup_ssl(self, ssl): # , server_ca_cert, server_cert):
# SSL Support
self.SSL = {}
self.SSL_exceptions = {
'WantReadError': None,
'WantWriteError': None,
'WantX509LookupError': None,
'ZeroReturnError': None,
'Error': None,
'SysCallError': None
}
self.ssl = ssl
self.pyopenssl = True
self.context = None
'''
self.server_cert = server_cert
self.server_ca_cert = server_ca_cert
self.ssl_pkey = None
self.ssl_cert = None
self.ssl_CN = 'Entropy Repository Service Client'
self.ssl_digest = 'md5'
self.ssl_serial = 1
self.ssl_not_before = 0
self.ssl_not_after = 60*60*24*1 # 1 day
'''
if self.ssl:
try:
from OpenSSL import SSL, crypto
except ImportError:
self.pyopenssl = False
'''
if not (self.server_cert and self.server_ca_cert):
raise exceptionTools.SSLError('SSLError: %s: %s' % (_("Specified SSL server certificate not available"),)
if not (os.path.isfile(self.server_cert) and \
os.access(self.server_cert,os.R_OK) and \
os.path.isfile(self.server_ca_cert) and \
os.access(self.server_ca_cert,os.R_OK)) and self.pyopenssl:
raise exceptionTools.SSLError('SSLError: %s: %s' % (_("Specified SSL server certificate not available"),self.server_cert,))
'''
if self.pyopenssl:
self.SSL_exceptions['WantReadError'] = SSL.WantReadError
self.SSL_exceptions['WantWriteError'] = SSL.WantWriteError
self.SSL_exceptions['WantX509LookupError'] = SSL.WantX509LookupError
self.SSL_exceptions['ZeroReturnError'] = SSL.ZeroReturnError
self.SSL_exceptions['Error'] = SSL.Error
self.SSL_exceptions['SysCallError'] = SSL.SysCallError
self.SSL['m'] = SSL
self.SSL['crypto'] = crypto
# setup an SSL context.
self.context = self.SSL['m'].Context(self.SSL['m'].SSLv23_METHOD)
#self.context.set_verify(self.SSL['m'].VERIFY_PEER, self.verify_ssl_cb)
# load up certificate stuff.
'''
self.ssl_pkey = self.create_ssl_key_pair(self.SSL['crypto'].TYPE_RSA, 1024)
self.context.use_privatekey(self.ssl_pkey)
self.context.use_certificate_file(self.server_cert)
self.context.load_verify_locations(self.server_ca_cert)
self.context.load_client_ca(self.server_cert)
self.ssl_pkey = self.create_ssl_key_pair(self.SSL['crypto'].TYPE_RSA, 1024)
self.ssl_cert = self.create_ssl_certificate(self.ssl_pkey)
self.context.use_privatekey(self.ssl_pkey)
self.context.use_certificate(self.ssl_cert)
self.context.load_client_ca(self.server_cert)
'''
else:
self.ssl = False
self.pyopenssl = False
def check_pyopenssl(self):
if not self.pyopenssl:
raise exceptionTools.SSLError('SSLError: %s' % (_("OpenSSL Python module not available, you need dev-python/pyopenssl"),))
'''
# this function should do the authentication checking to see that
# the client is who they say they are.
def verify_ssl_cb(self, conn, cert, errnum, depth, ok):
self.check_pyopenssl()
#print 'Got certificate: %s' % cert.get_subject()
#print repr(ok),repr(cert),repr(errnum),repr(depth)
return ok
def create_ssl_key_pair(self, keytype, bits):
if not self.pyopenssl:
raise exceptionTools.SSLError('SSLError: %s' % (_("OpenSSL Python module not available, you need dev-python/pyopenssl"),))
pkey = self.SSL['crypto'].PKey()
pkey.generate_key(keytype, bits)
return pkey
def create_ssl_certificate(self, pkey):
self.check_pyopenssl()
myreq = self.create_ssl_certificate_request(pkey, CN = self.ssl_CN)
cert = self.SSL['crypto'].X509()
cert.set_serial_number(self.ssl_serial)
cert.gmtime_adj_notBefore(self.ssl_not_before)
cert.gmtime_adj_notAfter(self.ssl_not_after)
cert.set_issuer(myreq.get_subject())
cert.set_subject(myreq.get_subject())
cert.set_pubkey(myreq.get_pubkey())
cert.sign(pkey, self.ssl_digest)
return cert
def create_ssl_certificate_request(self, pkey, **name):
self.check_pyopenssl()
req = self.SSL['crypto'].X509Req()
subj = req.get_subject()
for (key,value) in name.items():
setattr(subj, key, value)
req.set_pubkey(pkey)
req.sign(pkey, self.ssl_digest)
return req
'''
def stream_to_object(self, data, gzipped):
if gzipped:
data = self.zlib.decompress(data)
obj = self.dumpTools.unserialize_string(data)
return obj
def append_eos(self, data):
return str(len(data))+self.answers['eos']+data
def transmit(self, data):
self.check_socket_connection()
if hasattr(self.sock_conn,'settimeout'):
self.sock_conn.settimeout(self.socket_timeout)
data = self.append_eos(data)
try:
if self.ssl and not self.pyopenssl:
try:
self.sock_conn.write(data)
except UnicodeEncodeError:
self.sock_conn.write(data.encode('utf-8'))
else:
encode_done = False
mydata = data[:]
while 1:
try:
sent = self.sock_conn.send(mydata)
if sent == len(mydata):
break
mydata = mydata[sent:]
except (self.SSL_exceptions['WantWriteError'],self.SSL_exceptions['WantReadError'],):
time.sleep(0.2)
continue
except UnicodeEncodeError, e:
if encode_done:
raise
mydata = mydata.encode('utf-8')
encode_done = True
continue
except self.SSL_exceptions['Error'], e:
self.disconnect()
raise exceptionTools.SSLError('SSLError: %s' % (e,))
except self.socket.sslerror, e:
self.disconnect()
raise exceptionTools.SSLError('SSL Socket error: %s' % (e,))
except:
self.disconnect()
raise
def close_session(self, session_id):
self.check_socket_connection()
try:
self.transmit("%s end" % (session_id,))
# since we don't know if it's expired, we need to wrap it
data = self.receive()
except self.socket.error, e:
if etpUi['debug']:
self.entropyTools.printTraceback()
import pdb
pdb.set_trace()
if e[0] == 32: # broken pipe
return None
raise
except exceptionTools.SSLError:
raise
return data
def open_session(self):
self.check_socket_connection()
self.socket.setdefaulttimeout(self.socket_timeout)
self.transmit('begin')
data = self.receive()
return data
def receive(self):
self.check_socket_connection()
if hasattr(self.sock_conn,'settimeout'):
self.sock_conn.settimeout(self.socket_timeout)
self.ssl_prepending = True
def do_receive():
data = ''
if self.ssl and not self.pyopenssl:
data = self.sock_conn.read(1024)
elif self.ssl:
if self.ssl_prepending:
data = self.sock_conn.recv(1024)
self.ssl_prepending = False
while self.sock_conn.pending():
data += self.sock_conn.recv(1024)
else:
data = self.sock_conn.recv(1024)
return data
myeos = self.answers['eos']
ssl_error_loop_count = 0
while 1:
try:
data = do_receive()
if self.buffer_length == None:
self.buffered_data = ''
if (data == '') or (data == self.answers['cl']):
# nein! no support, KAPUTT!
# RAUSS!
if not self.quiet:
mytxt = _("command not supported. receive aborted")
self.Entropy.updateProgress(
"[%s:%s] %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
),
importance = 1,
type = "warning",
header = self.output_header
)
return None
elif len(data) < len(myeos):
if not self.quiet:
mytxt = _("malformed EOS. receive aborted")
self.Entropy.updateProgress(
"[%s:%s] %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
),
importance = 1,
type = "warning",
header = self.output_header
)
if etpUi['debug']:
self.entropyTools.printTraceback()
import pdb
pdb.set_trace()
return None
mystrlen = data.split(myeos)[0]
self.buffer_length = int(mystrlen)
data = data[len(mystrlen)+1:]
self.buffer_length -= len(data)
self.buffered_data = data
else:
self.buffer_length -= len(data)
self.buffered_data += data
while self.buffer_length > 0:
x = do_receive()
if self.ssl and self.pyopenssl and not x:
self.ssl_prepending = True
self.buffer_length -= len(x)
self.buffered_data += x
self.buffer_length = None
break
except ValueError, e:
if not self.quiet:
mytxt = _("malformed data. receive aborted")
self.Entropy.updateProgress(
"[%s:%s] %s: %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
e,
),
importance = 1,
type = "warning",
header = self.output_header
)
return None
except self.socket.timeout, e:
if not self.quiet:
mytxt = _("connection timed out while receiving data")
self.Entropy.updateProgress(
"[%s:%s] %s: %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
e,
),
importance = 1,
type = "warning",
header = self.output_header
)
return None
except self.socket.error, e:
if not self.quiet:
mytxt = _("connection error while receiving data")
self.Entropy.updateProgress(
"[%s:%s] %s: %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
e,
),
importance = 1,
type = "warning",
header = self.output_header
)
return None
except (self.SSL_exceptions['WantReadError'],self.SSL_exceptions['WantX509LookupError'],), e:
ssl_error_loop_count += 1
if ssl_error_loop_count > 3000000:
if not self.quiet:
mytxt = _("too many WantReadError error while receiving data")
self.Entropy.updateProgress(
"[%s:%s] %s: %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
e,
),
importance = 1,
type = "warning",
header = self.output_header
)
return None
continue
except self.SSL_exceptions['ZeroReturnError']:
break
except self.SSL_exceptions['SysCallError'], e:
if not self.quiet:
mytxt = _("syscall error while receiving data")
self.Entropy.updateProgress(
"[%s:%s] %s: %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
e,
),
importance = 1,
type = "warning",
header = self.output_header
)
return None
return self.buffered_data
def reconnect_socket(self):
if not self.quiet:
mytxt = _("Reconnecting to socket")
self.Entropy.updateProgress(
"[%s:%s] %s" % (
brown(unicode(self.hostname)),
bold(unicode(self.hostport)),
blue(mytxt),
),
importance = 1,
type = "info",
header = self.output_header
)
self.connect(self.hostname,self.hostport)
def check_socket_connection(self):
if not self.sock_conn:
raise exceptionTools.ConnectionError("ConnectionError: %s" % (_("Not connected to host"),))
def connect(self, host, port):
if self.ssl:
self.real_sock_conn = self.socket.socket(self.socket.AF_INET, self.socket.SOCK_STREAM)
if hasattr(self.real_sock_conn,'settimeout'):
self.real_sock_conn.settimeout(self.socket_timeout)
if self.pyopenssl:
self.sock_conn = self.SSL['m'].Connection(self.context, self.real_sock_conn)
else:
self.sock_conn = self.real_sock_conn
else:
self.sock_conn = self.socket.socket(self.socket.AF_INET, self.socket.SOCK_STREAM)
if hasattr(self.sock_conn,'settimeout'):
self.sock_conn.settimeout(self.socket_timeout)
self.real_sock_conn = self.sock_conn
self.hostname = host
self.hostport = port
try:
self.sock_conn.connect((self.hostname, self.hostport))
if self.ssl and not self.pyopenssl:
self.sock_conn = self.socket.ssl(self.real_sock_conn)
# inform about certificate verification
if not self.quiet:
mytxt = _("Warning: you are using an emergency SSL interface, SSL certificate can't be verified. Please install dev-python/pyopenssl")
self.Entropy.updateProgress(
"[%s:%s] %s" % (
brown(str(self.hostname)),
bold(str(self.hostport)),
blue(mytxt),
),
importance = 1,
type = "warning",
header = self.output_header
)
mytxt = _("Service issuer")
self.Entropy.updateProgress(
"[%s:%s] %s: %s" % (
brown(str(self.hostname)),
bold(str(self.hostport)),
blue(mytxt),
self.sock_conn.issuer()
),
importance = 1,
type = "warning",
header = self.output_header
)
except self.socket.error, e:
if e[0] == 111:
mytxt = "%s: %s, %s: %s" % (_("Cannot connect to"),host,_("on port"),port,)
raise exceptionTools.ConnectionError("ConnectionError: %s" % (mytxt,))
else:
raise
if not self.quiet:
mytxt = _("Successfully connected to host")
self.Entropy.updateProgress(
"[%s:%s] %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
),
importance = 1,
type = "info",
header = self.output_header
)
def disconnect(self):
if not self.real_sock_conn:
return True
if self.ssl and self.pyopenssl:
self.sock_conn.shutdown()
self.sock_conn.close()
elif self.ssl and not self.pyopenssl:
try:
self.real_sock_conn.shutdown(self.socket.SHUT_RDWR)
except self.socket.error:
pass
del self.sock_conn
self.sock_conn = None
try:
self.real_sock_conn.close()
except self.socket.error:
pass
if not self.quiet:
mytxt = _("Successfully disconnected from host")
self.Entropy.updateProgress(
"[%s:%s] %s" % (
brown(self.hostname),
bold(str(self.hostport)),
blue(mytxt),
),
importance = 1,
type = "info",
header = self.output_header
)
self.real_sock_conn = None
# otherwise reconnect_socket won't work
#self.hostname = None
#self.hostport = None
class SystemManagerExecutorInterface:
def __init__(self, SystemInterface, Entropy):
import entropyTools
self.entropyTools = entropyTools
self.Entropy = Entropy
self.SystemInterface = SystemInterface
self.available_commands = {}
self.task_result = None
def register(self, available_commands):
self.available_commands.update(available_commands)
def execute_task(self, command_data):
import signal
queue_id = command_data['queue_id']
args = command_data['args']
kwargs = command_data['kwargs']
data = self.available_commands.get(command_data['call'])
if data == None:
return False, 'no command'
elif len(args)+1 < data['args']:
return False, 'not enough args'
args.insert(0,queue_id)
self.task_result = None
t = self.entropyTools.parallelTask(data['func'], *args, **kwargs)
t.start()
killed = False
while 1:
if not t.isAlive():
break
time.sleep(1)
live_item, key = self.SystemInterface.get_item_by_queue_id(queue_id)
if isinstance(live_item,dict) and (key == "processing") and (not killed):
if live_item['kill'] and (live_item['processing_pid'] != None):
os.kill(live_item['processing_pid'],signal.SIGKILL)
killed = True
if killed:
return False, 'killed by user'
return True, t.result
class SystemManagerExecutorServerRepositoryInterface:
import entropyTools
def __init__(self, SystemManagerExecutorInstance, *args, **kwargs):
try:
import cPickle as pickle
except ImportError:
import pickle
self.pickle = pickle
import entropyTools
self.entropyTools = entropyTools
self.SystemManagerExecutor = SystemManagerExecutorInstance
self.args = args
self.kwargs = kwargs
self.available_commands = {
'sync_spm': {
'func': self.sync_portage,
'args': 1,
},
'compile_atoms': {
'func': self.compile_atoms,
'args': 2,
},
'spm_remove_atoms': {
'func': self.spm_remove_atoms,
'args': 2,
},
'get_spm_categories_updates': {
'func': self.get_spm_categories_updates,
'args': 2,
},
'get_spm_categories_installed': {
'func': self.get_spm_categories_installed,
'args': 2,
},
'enable_uses_for_atoms': {
'func': self.enable_uses_for_atoms,
'args': 3,
},
'disable_uses_for_atoms': {
'func': self.disable_uses_for_atoms,
'args': 3,
},
'get_spm_atoms_info': {
'func': self.get_spm_atoms_info,
'args': 2,
},
'run_spm_info': {
'func': self.run_spm_info,
'args': 1,
},
'run_custom_shell_command': {
'func': self.run_custom_shell_command,
'args': 1,
},
'get_spm_glsa_data': {
'func': self.get_spm_glsa_data,
'args': 1,
},
'move_entropy_packages_to_repository': {
'func': self.move_entropy_packages_to_repository,
'args': 5,
},
'scan_entropy_packages_database_changes': {
'func': self.scan_entropy_packages_database_changes,
'args': 1,
},
'run_entropy_database_updates': {
'func': self.run_entropy_database_updates,
'args': 4,
},
'run_entropy_dependency_test': {
'func': self.run_entropy_dependency_test,
'args': 1,
},
'run_entropy_library_test': {
'func': self.run_entropy_library_test,
'args': 1,
},
'run_entropy_treeupdates': {
'func': self.run_entropy_treeupdates,
'args': 2,
},
'scan_entropy_mirror_updates': {
'func': self.scan_entropy_mirror_updates,
'args': 2,
},
'run_entropy_mirror_updates': {
'func': self.run_entropy_mirror_updates,
'args': 2,
},
'run_entropy_checksum_test': {
'func': self.run_entropy_checksum_test,
'args': 3,
},
'get_notice_board': {
'func': self.get_notice_board,
'args': 2,
},
'remove_notice_board_entries': {
'func': self.remove_notice_board_entries,
'args': 3,
},
'add_notice_board_entry': {
'func': self.add_notice_board_entry,
'args': 5,
},
}
def _set_processing_pid(self, queue_id, process_pid):
self.SystemManagerExecutor.SystemInterface.queue_lock_acquire()
try:
live_item, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if isinstance(live_item,dict):
live_item['processing_pid'] = process_pid
self.SystemManagerExecutor.SystemInterface.store_queue()
finally:
self.SystemManagerExecutor.SystemInterface.queue_lock_release()
def sync_portage(self, queue_id):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
cmd = ["emerge", "--sync"]
try:
p = subprocess.Popen(cmd, stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id))
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
finally:
stdout_err.write('\n\n Stdin: %s' % (self._get_stdin(queue_id),))
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True,rc
def compile_atoms( self,
queue_id, atoms,
pretend = False, oneshot = False,
verbose = True, nocolor = True,
fetchonly = False, buildonly = False,
nodeps = False, custom_use = '', ldflags = '', cflags = ''
):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
cmd = [etpConst['spm']['env_update_cmd'],"&&"]
cmd += etpConst['spm']['source_profile']+["&&"]
if custom_use:
cmd += ['export USE="']+custom_use.strip().split()+['"','&&']
if ldflags:
cmd += ['export LDFLAGS="']+custom_use.strip().split()+['"','&&']
if cflags:
cmd += ['export CFLAGS="']+custom_use.strip().split()+['"','&&']
cmd += [etpConst['spm']['exec']]+atoms
if pretend:
cmd.append(etpConst['spm']['pretend_cmd'])
if verbose:
cmd.append(etpConst['spm']['verbose_cmd'])
if oneshot:
cmd.append(etpConst['spm']['oneshot_cmd'])
if nocolor:
cmd.append(etpConst['spm']['nocolor_cmd'])
if fetchonly:
cmd.append(etpConst['spm']['fetchonly_cmd'])
if buildonly:
cmd.append(etpConst['spm']['buildonly_cmd'])
if nodeps:
cmd.append(etpConst['spm']['nodeps_cmd'])
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (' '.join(cmd),))
stdout_err.flush()
try:
p = subprocess.Popen(' '.join(cmd), stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id), shell = True)
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
finally:
stdout_err.write('\n\n Stdin: %s' % (self._get_stdin(queue_id),))
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True,rc
def spm_remove_atoms(self, queue_id, atoms, pretend = True, verbose = True, nocolor = True):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
cmd = [etpConst['spm']['env_update_cmd'],"&&"]
cmd += etpConst['spm']['source_profile']+["&&"]
cmd += [etpConst['spm']['exec'],etpConst['spm']['remove_cmd']]+atoms
if pretend:
cmd.append(etpConst['spm']['pretend_cmd'])
if verbose:
cmd.append(etpConst['spm']['verbose_cmd'])
if nocolor:
cmd.append(etpConst['spm']['nocolor_cmd'])
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (' '.join(cmd),))
stdout_err.flush()
try:
p = subprocess.Popen(' '.join(cmd), stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id), shell = True)
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
finally:
stdout_err.write('\n\n Stdin: %s' % (self._get_stdin(queue_id),))
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True,rc
def enable_uses_for_atoms(self, queue_id, atoms, useflags):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
use_data = {}
for atom in atoms:
try:
status = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.enable_package_useflags(atom, useflags)
except:
continue
if status:
use_data[atom] = {}
matched_atom = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_best_atom(atom)
use_data[atom] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_package_useflags(matched_atom)
return True, use_data
def disable_uses_for_atoms(self, queue_id, atoms, useflags):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
use_data = {}
for atom in atoms:
try:
status = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.disable_package_useflags(atom, useflags)
except:
continue
if status:
use_data[atom] = {}
matched_atom = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_best_atom(atom)
use_data[atom] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_package_useflags(matched_atom)
return True, use_data
def get_spm_atoms_info(self, queue_id, atoms):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
atoms_data = {}
for atom in atoms:
try:
key = self.entropyTools.dep_getkey(atom)
category = key.split("/")[0]
except:
continue
matched_atom = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_best_atom(atom)
if not matched_atom: continue
if not atoms_data.has_key(category):
atoms_data[category] = {}
atoms_data[category][matched_atom] = self._get_spm_pkginfo(matched_atom)
return True, atoms_data
def get_spm_categories_updates(self, queue_id, categories):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
packages = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_available_packages(categories)
package_data = {}
for package in packages:
try:
key = self.entropyTools.dep_getkey(package)
category = key.split("/")[0]
except:
continue
if not package_data.has_key(category):
package_data[category] = {}
package_data[category][package] = self._get_spm_pkginfo(package)
return True, package_data
def get_spm_categories_installed(self, queue_id, categories):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
packages, pkg_len = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_installed_packages(categories = categories)
package_data = {}
for package in packages:
try:
key = self.entropyTools.dep_getkey(package)
category = key.split("/")[0]
except:
continue
if not package_data.has_key(category):
package_data[category] = {}
package_data[category][package] = self._get_spm_pkginfo(package, from_installed = True)
return True, package_data
def run_spm_info(self, queue_id):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
cmd = [etpConst['spm']['exec'],etpConst['spm']['info_cmd']]
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (' '.join(cmd),))
stdout_err.flush()
try:
p = subprocess.Popen(cmd, stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id))
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
finally:
stdout_err.write('\n\n Stdin: %s' % (self._get_stdin(queue_id),))
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True,rc
def run_custom_shell_command(self, queue_id, command):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
cmd = [etpConst['spm']['env_update_cmd'],"&&"]
cmd += etpConst['spm']['source_profile']+[";"]
cmd += command.split()
cmd = ' '.join(cmd)
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (cmd,))
stdout_err.flush()
try:
p = subprocess.Popen(cmd, stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id), shell = True)
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
finally:
stdout_err.write('Stdin: %s\n' % (self._get_stdin(queue_id),))
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True,rc
def move_entropy_packages_to_repository(self, queue_id, from_repo, to_repo, idpackages, do_copy):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
# run
matches = []
for idpackage in idpackages:
matches.append((idpackage,from_repo,))
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
switched = self.SystemManagerExecutor.SystemInterface.Entropy.move_packages(
matches, to_repo,
from_repo = from_repo,
ask = False,
do_copy = do_copy
)
return switched
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
switched = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
rc = 1
if len(switched) == len(idpackages):
rc = 0
return True,rc
def scan_entropy_packages_database_changes(self, queue_id):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
Entropy = self.SystemManagerExecutor.SystemInterface.Entropy
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
for repoid in Entropy.get_available_repositories():
self.run_entropy_treeupdates(queue_id, repoid)
stdout_err.write("\n"+_("Calculating updates...").encode('utf-8')+"\n")
stdout_err.flush()
to_add, to_remove, to_inject = Entropy.scan_package_changes()
mydict = { 'add': to_add, 'remove': to_remove, 'inject': to_inject }
# setup add data
mydict['add_data'] = {}
for portage_atom, portage_counter in to_add:
mydict['add_data'][(portage_atom, portage_counter,)] = self._get_spm_pkginfo(portage_atom,from_installed = True)
mydict['remove_data'] = {}
for idpackage, repoid in to_remove:
dbconn = Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
mydict['remove_data'][(idpackage, repoid,)] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
dbconn.closeDB()
mydict['inject_data'] = {}
for idpackage, repoid in to_inject:
dbconn = Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
mydict['inject_data'][(idpackage, repoid,)] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
dbconn.closeDB()
return True,mydict
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
data = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return data
def run_entropy_database_updates(self, queue_id, to_add, to_remove, to_inject):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
Entropy = self.SystemManagerExecutor.SystemInterface.Entropy
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
atoms_removed = []
matches_injected = set()
if to_inject: Entropy.updateProgress(_("Running package injection"))
# run inject
for idpackage, repoid in to_inject:
matches_injected.add((idpackage,repoid,))
Entropy.transform_package_into_injected(idpackage, repo = repoid)
if to_remove: Entropy.updateProgress(_("Running package removal"))
# run remove
remdata = {}
for idpackage,repoid in to_remove:
dbconn = Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
atoms_removed.append(dbconn.retrieveAtom(idpackage))
dbconn.closeDB()
if not remdata.has_key(repoid):
remdata[repoid] = set()
remdata[repoid].add(idpackage)
for repoid in remdata:
Entropy.remove_packages(remdata[repoid], repo = repoid)
mydict = {
'added_data': {},
'remove_data': atoms_removed,
'inject_data': {}
}
if to_add:
problems = Entropy.check_config_file_updates()
if problems:
return False,mydict
Entropy.updateProgress(_("Running package quickpkg"))
# run quickpkg
for repoid in to_add:
store_dir = Entropy.get_local_store_directory(repo = repoid)
for atom in to_add[repoid]:
Entropy.quickpkg(atom,store_dir)
# inject new into db
avail_repos = Entropy.get_available_repositories()
if etpConst['clientserverrepoid'] in avail_repos:
avail_repos.pop(etpConst['clientserverrepoid'])
matches_added = set()
for repoid in avail_repos:
store_dir = Entropy.get_local_store_directory(repo = repoid)
package_files = os.listdir(store_dir)
if not package_files: continue
package_files = [(os.path.join(store_dir,x),False) for x in package_files]
Entropy.updateProgress( "[%s|%s] %s" % (
repoid,
etpConst['branch'],
_("Adding packages"),
)
)
for package_file, inject in package_files:
Entropy.updateProgress(" %s" % (package_file,))
idpackages = Entropy.add_packages_to_repository(package_files, ask = False, repo = repoid)
matches_added |= set([(x,repoid,) for x in idpackages])
Entropy.dependencies_test()
for idpackage, repoid in matches_added:
dbconn = Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
mydict['added_data'][(idpackage, repoid,)] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
dbconn.closeDB()
for idpackage, repoid in matches_injected:
dbconn = Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
mydict['inject_data'][(idpackage, repoid,)] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
dbconn.closeDB()
return True, mydict
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
data = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return data
def run_entropy_dependency_test(self, queue_id):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
deps_not_matched = self.SystemManagerExecutor.SystemInterface.Entropy.dependencies_test()
return deps_not_matched
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
data = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return data
def run_entropy_library_test(self, queue_id):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
return self.SystemManagerExecutor.SystemInterface.Entropy.libraries_test()
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
status, result = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
mystatus = False
if status == 0: mystatus = True
if not result: result = set()
return mystatus,result
def run_entropy_checksum_test(self, queue_id, repoid, mode):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
if mode == "local":
data = self.SystemManagerExecutor.SystemInterface.Entropy.verify_local_packages([], ask = False, repo = repoid)
else:
data = self.SystemManagerExecutor.SystemInterface.Entropy.verify_remote_packages([], ask = False, repo = repoid)
return True, data
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
mydata = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return mydata
def run_entropy_treeupdates(self, queue_id, repoid):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
sys.stdout.write(_("Opening database to let it run treeupdates. If you won't see anything below, it's just fine.").encode('utf-8')+"\n")
dbconn = self.SystemManagerExecutor.SystemInterface.Entropy.openServerDatabase(
repo = repoid, do_cache = False,
read_only = True
)
dbconn.closeDB()
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return True, 0
def scan_entropy_mirror_updates(self, queue_id, repositories):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
import socket
Entropy = self.SystemManagerExecutor.SystemInterface.Entropy
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
sys.stdout.write(_("Scanning").encode('utf-8')+"\n")
repo_data = {}
for repoid in repositories:
repo_data[repoid] = {}
for uri in Entropy.get_remote_mirrors(repoid):
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
repo_data[repoid][crippled_uri] = {}
repo_data[repoid][crippled_uri]['packages'] = {}
try:
upload_queue, download_queue, removal_queue, \
fine_queue, remote_packages_data = Entropy.MirrorsService.calculate_packages_to_sync(uri, etpConst['branch'], repoid)
except socket.error:
self.entropyTools.printTraceback(f = stdout_err)
stdout_err.write("\n"+_("Socket error, continuing...").encode('utf-8')+"\n")
continue
if (upload_queue or download_queue or removal_queue):
upload, download, removal, copy, metainfo = Entropy.MirrorsService.expand_queues(
upload_queue,
download_queue,
removal_queue,
remote_packages_data,
etpConst['branch'],
repoid
)
if len(upload)+len(download)+len(removal)+len(copy):
repo_data[repoid][crippled_uri]['packages'] = {
'upload': upload,
'download': download,
'removal': removal,
'copy': copy,
}
# now the db
current_revision = Entropy.get_local_database_revision(repoid)
remote_revision = Entropy.get_remote_database_revision(repoid)
download_latest, upload_queue = Entropy.MirrorsService.calculate_database_sync_queues(repoid)
repo_data[repoid][crippled_uri]['database'] = {
'current_revision': current_revision,
'remote_revision': remote_revision,
'download_latest': download_latest,
'upload_queue': [(self.entropyTools.extractFTPHostFromUri(x[0]),x[1],) for x in upload_queue]
}
return True, repo_data
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
data = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return data
def run_entropy_mirror_updates(self, queue_id, repository_data):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
Entropy = self.SystemManagerExecutor.SystemInterface.Entropy
def sync_remote_databases(repoid, pretend):
rdb_status = Entropy.MirrorsService.get_remote_databases_status()
Entropy.updateProgress(
"%s:" % (_("Remote Entropy Database Repository Status"),),
header = " * "
)
for myuri, myrev in rdb_status:
Entropy.updateProgress("\t %s:\t %s" % (_("Host"),self.entropyTools.extractFTPHostFromUri(myuri),))
Entropy.updateProgress("\t * %s: %s" % (_("Database revision"),myrev,))
local_revision = Entropy.get_local_database_revision(repoid)
Entropy.updateProgress("\t * %s: %s" % (_("Database local revision currently at"),local_revision,))
if pretend:
return 0,set(),set()
errors, fine_uris, broken_uris = Entropy.MirrorsService.sync_databases(no_upload = False)
remote_status = Entropy.MirrorsService.get_remote_databases_status(repoid)
Entropy.updateProgress(" * %s: " % (_("Remote Entropy Database Repository Status"),))
for myuri, myrev in remote_status:
Entropy.updateProgress("\t %s:\t%s" % (_("Host"),Entropy.entropyTools.extractFTPHostFromUri(myuri),))
Entropy.updateProgress("\t * %s: %s" % (_("Database revision"),myrev,) )
return errors, fine_uris, broken_uris
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
repo_data = {}
for repoid in repository_data:
# avoid __default__
if repoid == etpConst['clientserverrepoid']: continue
successfull_mirrors = set()
mirrors_errors = False
mirrors_tainted = False
broken_mirrors = set()
check_data = []
repo_data[repoid] = {
'mirrors_tainted': mirrors_tainted,
'mirrors_errors': mirrors_errors,
'successfull_mirrors': successfull_mirrors.copy(),
'broken_mirrors': broken_mirrors.copy(),
'check_data': check_data,
'db_errors': 0,
'db_fine': set(),
'db_broken': set(),
}
if repository_data[repoid]['pkg']:
mirrors_tainted, mirrors_errors, \
successfull_mirrors, broken_mirrors, \
check_data = Entropy.MirrorsService.sync_packages(
ask = False, pretend = repository_data[repoid]['pretend'],
packages_check = repository_data[repoid]['pkg_check'], repo = repoid)
repo_data[repoid]['mirrors_tainted'] = mirrors_tainted
repo_data[repoid]['mirrors_errors'] = mirrors_errors
repo_data[repoid]['successfull_mirrors'] = successfull_mirrors
repo_data[repoid]['broken_mirrors'] = broken_mirrors
repo_data[repoid]['check_data'] = check_data
if (not successfull_mirrors) and (not repository_data[repoid]['pretend']): continue
if (not mirrors_errors) and repository_data[repoid]['db']:
if mirrors_tainted and etpConst['rss-feed']:
commit_msg = repository_data[repoid]['commit_msg']
if not commit_msg: commit_msg = "Autodriven update"
Entropy.rssMessages['commitmessage'] = commit_msg
errors, fine, broken = sync_remote_databases(repoid, repository_data[repoid]['pretend'])
repo_data[repoid]['db_errors'] = errors
repo_data[repoid]['db_fine'] = fine.copy()
repo_data[repoid]['db_broken'] = broken.copy()
if errors: continue
Entropy.MirrorsService.lock_mirrors(lock = False, repo = repoid)
Entropy.MirrorsService.tidy_mirrors(
repo = repoid, ask = False,
pretend = repository_data[repoid]['pretend']
)
return True, repo_data
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
data = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return data
def get_spm_glsa_data(self, queue_id, list_type):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
data = {}
glsa_ids = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.list_glsa_packages(list_type)
if not glsa_ids: return False,data
for myid in glsa_ids:
data[myid] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_glsa_id_information(myid)
return True,data
def get_notice_board(self, queue_id, repoid):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
data = self.SystemManagerExecutor.SystemInterface.Entropy.MirrorsService.read_notice_board(repo = repoid)
if data == None:
return False,None
return True,data
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
mydata = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return mydata
def remove_notice_board_entries(self, queue_id, repoid, entry_ids):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
for entry_id in entry_ids:
data = self.SystemManagerExecutor.SystemInterface.Entropy.MirrorsService.remove_from_notice_board(entry_id, repo = repoid)
self.SystemManagerExecutor.SystemInterface.Entropy.MirrorsService.upload_notice_board(repo = repoid)
return True,data
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
mydata = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return mydata
def add_notice_board_entry(self, queue_id, repoid, title, notice_text, link):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id)
if queue_data == None:
return False,'no item in queue'
stdout_err = open(queue_data['stdout'],"a+")
def myfunc():
sys.stdout = stdout_err
sys.stderr = stdout_err
mystdin = self._get_stdin(queue_id)
if mystdin: sys.stdin = os.fdopen(mystdin, 'rb')
try:
data = self.SystemManagerExecutor.SystemInterface.Entropy.MirrorsService.update_notice_board(title, notice_text, link = link, repo = repoid)
return True,data
except Exception, e:
self.entropyTools.printTraceback()
return False,unicode(e)
finally:
sys.stdout.write("\n### Done ###\n")
sys.stdout.flush()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.stdin = sys.__stdin__
def write_pid(pid):
self._set_processing_pid(queue_id, pid)
mydata = self.entropyTools.spawnFunction(myfunc, write_pid_func = write_pid)
stdout_err.close()
return mydata
def _get_stdin(self, queue_id):
mystdin = None
std_data = self.SystemManagerExecutor.SystemInterface.ManagerQueueStdInOut.get(queue_id)
if std_data != None: mystdin = std_data[0]
return mystdin
def _file_updateProgress(self, f, *myargs, **mykwargs):
f.flush()
back = mykwargs.get("back")
count = mykwargs.get("count")
header = mykwargs.get("header")
percent = mykwargs.get("percent")
text = myargs[0].encode('utf-8')
if not header: header = ''
count_str = ""
if count:
if len(count) > 1:
if percent:
count_str = " ("+str(round((float(count[0])/count[1])*100,1))+"%) "
else:
count_str = " (%s/%s) " % (red(str(count[0])),blue(str(count[1])),)
def is_last_newline(f):
try:
f.seek(-1,2)
last = f.read(1)
if last == "\n":
return True
except IOError:
pass
return False
if back:
self.entropyTools.seek_till_newline(f)
txt = header+count_str+text
else:
if not is_last_newline(f): f.write("\n")
txt = header+count_str+text+"\n"
f.write(txt)
f.flush()
# !!! duplicate
def _get_entropy_pkginfo(self, dbconn, idpackage, repoid):
data = {}
try:
data['atom'], data['name'], data['version'], data['versiontag'], \
data['description'], data['category'], data['chost'], \
data['cflags'], data['cxxflags'],data['homepage'], \
data['license'], data['branch'], data['download'], \
data['digest'], data['slot'], data['etpapi'], \
data['datecreation'], data['size'], data['revision'] = dbconn.getBaseData(idpackage)
except TypeError:
return data
data['injected'] = dbconn.isInjected(idpackage)
data['repoid'] = repoid
data['idpackage'] = idpackage
return data
def _get_spm_pkginfo(self, matched_atom, from_installed = False):
data = {}
data['atom'] = matched_atom
data['key'] = self.entropyTools.dep_getkey(matched_atom)
try:
if from_installed:
data['slot'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_installed_package_slot(matched_atom)
portage_matched_atom = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_best_atom("%s:%s" % (data['key'],data['slot'],))
# get installed package description
data['available_atom'] = portage_matched_atom
if portage_matched_atom:
data['use'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_package_useflags(portage_matched_atom)
else:
# get use flags of the installed package
data['use'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_installed_package_useflags(matched_atom)
data['description'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_installed_package_description(matched_atom)
else:
data['slot'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_package_slot(matched_atom)
data['use'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_package_useflags(matched_atom)
data['installed_atom'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_installed_atom("%s:%s" % (data['key'],data['slot'],))
data['description'] = self.SystemManagerExecutor.SystemInterface.Entropy.SpmService.get_package_description(matched_atom)
except KeyError:
pass
return data
class SystemManagerRepositoryCommands(SocketCommandsSkel):
import entropyTools, dumpTools
def __init__(self, HostInterface):
SocketCommandsSkel.__init__(self, HostInterface, inst_name = "srvrepo")
self.raw_commands = [
'srvrepo:enable_uses_for_atoms',
'srvrepo:disable_uses_for_atoms',
'srvrepo:compile_atoms',
'srvrepo:spm_remove_atoms',
'srvrepo:run_custom_shell_command',
'srvrepo:remove_entropy_packages',
'srvrepo:search_entropy_packages',
'srvrepo:run_entropy_database_updates',
'srvrepo:run_entropy_mirror_updates',
'srvrepo:add_notice_board_entry'
]
self.valid_commands = {
'srvrepo:sync_spm': {
'auth': True,
'built_in': False,
'cb': self.docmd_sync_spm,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "spawn portage sync (emerge --sync)",
'syntax': "<SESSION_ID> srvrepo:sync_spm",
'from': unicode(self)
},
'srvrepo:compile_atoms': {
'auth': True,
'built_in': False,
'cb': self.docmd_compile_atoms,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "compile specified atoms using Spm (Portage?)",
'syntax': "<SESSION_ID> srvrepo:compile_atoms <xml string containing atoms and compile options>",
'from': unicode(self)
},
'srvrepo:spm_remove_atoms': {
'auth': True,
'built_in': False,
'cb': self.docmd_spm_remove_atoms,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "remove specified atoms using Spm (Portage?)",
'syntax': "<SESSION_ID> srvrepo:spm_remove_atoms <xml string containing atoms and remove options>",
'from': unicode(self)
},
'srvrepo:get_spm_categories_updates': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_spm_categories_updates,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "get SPM updates for the specified package categories",
'syntax': "<SESSION_ID> srvrepo:get_spm_categories_updates <category 1> <category 2> <...>",
'from': unicode(self)
},
'srvrepo:get_spm_categories_installed': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_spm_categories_installed,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "get SPM installed packages for the specified package categories",
'syntax': "<SESSION_ID> srvrepo:get_spm_categories_installed <category 1> <category 2> <...>",
'from': unicode(self)
},
'srvrepo:enable_uses_for_atoms': {
'auth': True,
'built_in': False,
'cb': self.docmd_enable_uses_for_atoms,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "enable use flags for the specified atom",
'syntax': "<SESSION_ID> srvrepo:enable_uses_for_atom <xml string containing atoms and use flags>",
'from': unicode(self)
},
'srvrepo:disable_uses_for_atoms': {
'auth': True,
'built_in': False,
'cb': self.docmd_disable_uses_for_atoms,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "enable use flags for the specified atom",
'syntax': "<SESSION_ID> srvrepo:disable_uses_for_atom <xml string containing atoms and use flags>",
'from': unicode(self)
},
'srvrepo:get_spm_atoms_info': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_spm_atoms_info,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "get info from SPM for the specified atoms",
'syntax': "<SESSION_ID> srvrepo:get_spm_atoms_info <atom1> <atom2> <atom3>",
'from': unicode(self)
},
'srvrepo:run_spm_info': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_spm_info,
'args': ["cmd","authenticator"],
'as_user': False,
'desc': "run SPM info command",
'syntax': "<SESSION_ID> srvrepo:run_spm_info",
'from': unicode(self)
},
'srvrepo:run_custom_shell_command': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_custom_shell_command,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "run custom shell command",
'syntax': "<SESSION_ID> srvrepo:run_custom_shell_command <shell command blah blah>",
'from': unicode(self)
},
'srvrepo:get_spm_glsa_data': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_spm_glsa_data,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "get SPM security updates info",
'syntax': "<SESSION_ID> srvrepo:get_spm_glsa_data <list_type string (affected,new,all)>",
'from': unicode(self)
},
'srvrepo:get_available_repositories': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_available_repositories,
'args': [],
'as_user': False,
'desc': "get information about available Entropy repositories",
'syntax': "<SESSION_ID> srvrepo:get_available_repositories",
'from': unicode(self)
},
'srvrepo:set_default_repository': {
'auth': True,
'built_in': False,
'cb': self.docmd_set_default_repository,
'args': ["myargs"],
'as_user': False,
'desc': "set a new default Entropy Server repository",
'syntax': "<SESSION_ID> srvrepo:set_default_repository <repoid>",
'from': unicode(self)
},
'srvrepo:get_available_entropy_packages': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_available_entropy_packages,
'args': ["myargs"],
'as_user': False,
'desc': "get available Entropy packages from the chosen repository id",
'syntax': "<SESSION_ID> srvrepo:get_available_entropy_packages <repoid>",
'from': unicode(self)
},
'srvrepo:get_entropy_idpackage_information': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_entropy_idpackage_information,
'args': ["myargs"],
'as_user': False,
'desc': "get Entropy package metadata using its idpackage and repository id",
'syntax': "<SESSION_ID> srvrepo:get_entropy_idpackage_information <idpackage> <repoid>",
'from': unicode(self)
},
'srvrepo:remove_entropy_packages': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_entropy_packages,
'args': ["myargs"],
'as_user': False,
'desc': "remove Entropy packages using their match -> (idpackage,repo)",
'syntax': "<SESSION_ID> srvrepo:remove_entropy_packages idpackage:repoid,idpackage,repoid,...",
'from': unicode(self)
},
'srvrepo:search_entropy_packages': {
'auth': True,
'built_in': False,
'cb': self.docmd_search_entropy_packages,
'args': ["myargs"],
'as_user': False,
'desc': "search Entropy packages using a defined search type, search string inside the specified repository",
'syntax': "<SESSION_ID> srvrepo:search_entropy_packages <repoid> <search_type> <search string...>",
'from': unicode(self)
},
'srvrepo:move_entropy_packages_to_repository': {
'auth': True,
'built_in': False,
'cb': self.docmd_move_entropy_packages_to_repository,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "move or copy Entropy packages from a repository to another",
'syntax': "<SESSION_ID> srvrepo:move_entropy_packages_to_repository <from_repo> <to_repo> <do_copy (True: copy, False: move)> <idpackages...>",
'from': unicode(self)
},
'srvrepo:scan_entropy_packages_database_changes': {
'auth': True,
'built_in': False,
'cb': self.docmd_scan_entropy_packages_database_changes,
'args': ["cmd","authenticator"],
'as_user': False,
'desc': "scan Spm package changes to retrieve a list of action that should be run on the repositories",
'syntax': "<SESSION_ID> srvrepo:scan_entropy_packages_database_changes",
'from': unicode(self)
},
'srvrepo:run_entropy_database_updates': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_entropy_database_updates,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "run Entropy database updates",
'syntax': "<SESSION_ID> srvrepo:run_entropy_database_updates <to_add: atom:counter:repoid,atom:counter:repoid,...> <to_remove: idpackage:repoid,idpackage:repoid,...> <to_inject: idpackage:repoid,idpackage:repoid,...>",
'from': unicode(self)
},
'srvrepo:run_entropy_dependency_test': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_entropy_dependency_test,
'args': ["cmd","authenticator"],
'as_user': False,
'desc': "run Entropy dependency test",
'syntax': "<SESSION_ID> srvrepo:run_entropy_dependency_test",
'from': unicode(self)
},
'srvrepo:run_entropy_library_test': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_entropy_library_test,
'args': ["cmd","authenticator"],
'as_user': False,
'desc': "run Entropy dependency test",
'syntax': "<SESSION_ID> srvrepo:run_entropy_library_test",
'from': unicode(self)
},
'srvrepo:run_entropy_treeupdates': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_entropy_treeupdates,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "run Entropy database treeupdates",
'syntax': "<SESSION_ID> srvrepo:run_entropy_treeupdates <repoid>",
'from': unicode(self)
},
'srvrepo:scan_entropy_mirror_updates': {
'auth': True,
'built_in': False,
'cb': self.docmd_scan_entropy_mirror_updates,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "scan mirror updates for the specified repository identifiers",
'syntax': "<SESSION_ID> srvrepo:scan_entropy_mirror_updates <repoid 1> <repoid 2> <...>",
'from': unicode(self)
},
'srvrepo:run_entropy_mirror_updates': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_entropy_mirror_updates,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "run mirror updates for the provided repositories",
'syntax': "<SESSION_ID> srvrepo:run_entropy_mirror_updates <xml data, properly formatted>",
'from': unicode(self)
},
'srvrepo:run_entropy_checksum_test': {
'auth': True,
'built_in': False,
'cb': self.docmd_run_entropy_checksum_test,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "run Entropy packages checksum verification tool",
'syntax': "<SESSION_ID> srvrepo:run_entropy_checksum_test <repoid> <mode>",
'from': unicode(self)
},
'srvrepo:get_notice_board': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_notice_board,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "get repository notice board",
'syntax': "<SESSION_ID> srvrepo:get_notice_board <repoid>",
'from': unicode(self)
},
'srvrepo:remove_notice_board_entries': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_notice_board_entries,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "remove notice board entries",
'syntax': "<SESSION_ID> srvrepo:remove_notice_board_entries <repoid> <entry_id1> <entry_id2> <...>",
'from': unicode(self)
},
'srvrepo:add_notice_board_entry': {
'auth': True,
'built_in': False,
'cb': self.docmd_add_notice_board_entry,
'args': ["cmd","myargs","authenticator"],
'as_user': False,
'desc': "remove notice board entry",
'syntax': "<SESSION_ID> srvrepo:add_notice_board_entry <xml formatted data>",
'from': unicode(self)
},
}
def docmd_sync_spm(self, cmd, myargs, authenticator):
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'sync_spm', [], {}, False, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_compile_atoms(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
xml_string = ' '.join(myargs)
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not ( mydict.has_key('atoms') and mydict.has_key('pretend') and \
mydict.has_key('oneshot') and mydict.has_key('verbose') and \
mydict.has_key('fetchonly') and mydict.has_key('buildonly') and \
mydict.has_key('nodeps') and \
mydict.has_key('nocolor') and mydict.has_key('custom_use') and \
mydict.has_key('ldflags') and mydict.has_key('cflags') ):
return None,'wrong dict arguments, xml must have 10 items with attr value' + \
' -> atoms, pretend, oneshot, verbose, nocolor, fetchonly, ' + \
'buildonly, nodeps, custom_use, ldflags, cflags'
atoms = mydict.get('atoms')
if atoms: atoms = atoms.split()
pretend = mydict.get('pretend')
oneshot = mydict.get('oneshot')
verbose = mydict.get('verbose')
nocolor = mydict.get('nocolor')
fetchonly = mydict.get('fetchonly')
buildonly = mydict.get('buildonly')
nodeps = mydict.get('nodeps')
custom_use = mydict.get('custom_use')
ldflags = mydict.get('ldflags')
cflags = mydict.get('cflags')
if pretend == "1": pretend = True
else: pretend = False
if oneshot == "1": oneshot = True
else: oneshot = False
if verbose == "1": verbose = True
else: verbose = False
if nocolor == "1": nocolor = True
else: nocolor = False
if fetchonly == "1": fetchonly = True
else: fetchonly = False
if buildonly == "1": buildonly = True
else: buildonly = False
if nodeps == "1": nodeps = True
else: nodeps = False
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
add_dict = {
'pretend': pretend,
'oneshot': oneshot,
'verbose': verbose,
'nocolor': nocolor,
'fetchonly': fetchonly,
'buildonly': buildonly,
'nodeps': nodeps,
'custom_use': custom_use,
'ldflags': ldflags,
'cflags': cflags,
}
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'compile_atoms', [atoms[:]], add_dict.copy(), False, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_spm_remove_atoms(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
xml_string = ' '.join(myargs)
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not ( mydict.has_key('atoms') and mydict.has_key('pretend') and \
mydict.has_key('verbose') and mydict.has_key('nocolor') ):
return None,'wrong dict arguments, xml must have 4 items with attr value -> atoms, pretend, verbose, nocolor'
atoms = mydict.get('atoms')
if atoms: atoms = atoms.split()
pretend = mydict.get('pretend')
verbose = mydict.get('verbose')
nocolor = mydict.get('nocolor')
if pretend == "1": pretend = True
else: pretend = False
if verbose == "1": verbose = True
else: verbose = False
if nocolor == "1": nocolor = True
else: nocolor = False
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
add_dict = {
'pretend': pretend,
'verbose': verbose,
'nocolor': nocolor,
}
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'spm_remove_atoms', [atoms[:]], add_dict.copy(), False, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_get_spm_categories_updates(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'get_spm_categories_updates', [myargs], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_get_spm_categories_installed(self, cmd, myargs, authenticator):
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'get_spm_categories_installed', [myargs], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_enable_uses_for_atoms(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
xml_string = ' '.join(myargs)
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('atoms') and mydict.has_key('useflags')):
return None,'wrong dict arguments, xml must have 2 items with attr value -> atoms, useflags'
atoms = mydict.get('atoms')
useflags = mydict.get('useflags')
if atoms: atoms = atoms.split()
if useflags: useflags = useflags.split()
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'enable_uses_for_atoms', [atoms,useflags], {}, True, True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_disable_uses_for_atoms(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
xml_string = ' '.join(myargs)
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('atoms') and mydict.has_key('useflags')):
return None,'wrong dict arguments, xml must have 2 items with attr value -> atoms, useflags'
atoms = mydict.get('atoms')
useflags = mydict.get('useflags')
if atoms: atoms = atoms.split()
if useflags: useflags = useflags.split()
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'disable_uses_for_atoms', [atoms,useflags], {}, True, True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_get_spm_atoms_info(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'get_spm_atoms_info', [myargs], {}, True, True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_spm_info(self, cmd, authenticator):
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, '', uid, gid, 'run_spm_info', [], {}, True, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_custom_shell_command(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
command = ' '.join(myargs)
queue_id = self.HostInterface.add_to_queue(cmd, command, uid, gid, 'run_custom_shell_command', [command], {}, True, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_get_spm_glsa_data(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'get_spm_glsa_data', [myargs[0]], {}, True, True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_get_available_repositories(self):
data = {}
data['available'] = self.HostInterface.Entropy.get_available_repositories()
if etpConst['clientserverrepoid'] in data['available']:
data['available'].pop(etpConst['clientserverrepoid'])
data['community_mode'] = self.HostInterface.Entropy.community_repo
data['current'] = self.HostInterface.Entropy.default_repository
data['branches'] = etpConst['branches']
data['branch'] = etpConst['branch']
return True, data
def docmd_set_default_repository(self, myargs):
if not myargs:
return False,'wrong arguments'
repoid = myargs[0]
if repoid not in self.HostInterface.Entropy.get_available_repositories():
return False,'repository id not available'
status = True
msg = 'ok'
try:
self.HostInterface.Entropy.switch_default_repository(repoid, save = True, handle_uninitialized = False)
except Exception, e:
status = False
msg = unicode(e)
return status, msg
def docmd_get_available_entropy_packages(self, myargs):
if not myargs:
return False,'wrong arguments'
repoid = myargs[0]
if repoid not in self.HostInterface.Entropy.get_available_repositories():
return False,'repository id not available'
dbconn = self.HostInterface.Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
idpackages = dbconn.listAllIdpackages(order_by = 'atom')
package_data = []
package_data = {
'ordered_idpackages': idpackages,
'data': {},
}
for idpackage in idpackages:
data = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
if not data: continue
package_data['data'][idpackage] = data.copy()
dbconn.closeDB()
return True,package_data
def docmd_get_entropy_idpackage_information(self, myargs):
if len(myargs) < 2:
return False,'wrong arguments'
idpackage = myargs[0]
repoid = myargs[1]
dbconn = self.HostInterface.Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
package_data = dbconn.getPackageData(idpackage, trigger_unicode = True)
dbconn.closeDB()
return True,package_data
def docmd_remove_entropy_packages(self, myargs):
if not myargs:
return False,'wrong arguments'
string = myargs[0].split(",")
matched_atoms = []
try:
for item in string:
mysplit = item.split(":")
matched_atoms.append((int(mysplit[0]),mysplit[1],))
except:
return False,'cannot eval() string correctly'
repo_data = {}
for idpackage,repoid in matched_atoms:
if not repo_data.has_key(repoid):
repo_data[repoid] = []
repo_data[repoid].append(idpackage)
status = True
msg = 'ok'
try:
for repoid in repo_data:
self.HostInterface.Entropy.remove_packages(repo_data[repoid],repo = repoid)
except Exception, e:
status = False
msg = unicode(e)
return status, msg
def docmd_move_entropy_packages_to_repository(self, cmd, myargs, authenticator):
if len(myargs) < 4:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
from_repo = myargs[0]
to_repo = myargs[1]
do_copy = myargs[2]
idpackages = myargs[3:]
queue_id = self.HostInterface.add_to_queue(
cmd, ' '.join([str(x) for x in myargs]),
uid, gid, 'move_entropy_packages_to_repository',
[from_repo,to_repo,idpackages,do_copy], {}, False, True,
interactive = True
)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_scan_entropy_packages_database_changes(self, cmd, authenticator):
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, '', uid, gid, 'scan_entropy_packages_database_changes', [], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_entropy_dependency_test(self, cmd, authenticator):
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, '', uid, gid, 'run_entropy_dependency_test', [], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_entropy_library_test(self, cmd, authenticator):
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, '', uid, gid, 'run_entropy_library_test', [], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_entropy_checksum_test(self, cmd, myargs, authenticator):
if len(myargs) < 2:
return False,'wrong arguments'
repoid = myargs[0]
mode = myargs[1]
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'run_entropy_checksum_test', [repoid,mode], {}, True, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_entropy_treeupdates(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'run_entropy_treeupdates', [myargs[0]], {}, False, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_scan_entropy_mirror_updates(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'scan_entropy_mirror_updates', [myargs], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_entropy_mirror_updates(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
serialized_string = '\n'.join(myargs)
try:
mydict = self.dumpTools.unserialize_string(serialized_string)
except Exception, e:
return False,'cannot parse data: %s' % (e,)
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, '<raw data>', uid, gid, 'run_entropy_mirror_updates', [mydict], {}, False, False, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_run_entropy_database_updates(self, cmd, myargs, authenticator):
to_add = {}
to_remove = []
to_inject = []
to_add_string = ''
to_remove_string = ''
to_inject_string = ''
if myargs:
to_add_string = myargs[0].split(",")
if len(myargs) > 1:
to_remove_string = myargs[1].split(",")
if len(myargs) > 2:
to_inject_string = myargs[2].split(",")
try:
for item in to_add_string:
atom, counter, repoid = item.split(":")
if not to_add.has_key(repoid):
to_add[repoid] = []
to_add[repoid].append(atom)
for item in to_remove_string:
idpackage, repoid = item.split(":")
to_remove.append((idpackage, repoid,))
for item in to_inject_string:
idpackage, repoid = item.split(":")
to_inject.append((idpackage, repoid,))
except Exception, e:
return False,'cannot run database updates properly: %s' % (e,)
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(
cmd, ' '.join(myargs), uid, gid,
'run_entropy_database_updates', [to_add,to_remove,to_inject],
{}, False, True, interactive = True
)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_search_entropy_packages(self, myargs):
if len(myargs) < 3:
return False,'wrong arguments'
repoid = myargs[0]
search_type = myargs[1]
search_string = ' '.join(myargs[2:])
avail_search_types = ['atom','needed','depends','tag','file','description']
if search_type not in avail_search_types:
return False, 'available search types: %s' % (avail_search_types,)
search_results = {
'ordered_idpackages': set(),
'data': {},
}
dbconn = self.HostInterface.Entropy.openServerDatabase(repo = repoid, just_reading = True, warnings = False, do_cache = False)
if search_type == "atom":
mysearchlist = search_string.split()
for mystring in mysearchlist:
results = dbconn.searchPackages(mystring)
for atom, idpackage, branch in results:
data = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
if not data: continue
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = data.copy()
elif search_type == "needed":
mysearchlist = search_string.split()
for mystring in mysearchlist:
idpackages = dbconn.searchNeeded(mystring, like = True)
for idpackage in idpackages:
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
elif search_type == "depends":
mysearchlist = search_string.split()
for mystring in mysearchlist:
m_idpackage, m_result = dbconn.atomMatch(mystring)
if m_idpackage == -1: continue
idpackages = dbconn.retrieveDepends(m_idpackage)
for idpackage in idpackages:
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
elif search_type == "tag":
mysearchlist = search_string.split()
for mystring in mysearchlist:
idpackages = dbconn.searchTaggedPackages(mystring)
for idpackage in idpackages:
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
elif search_type == "file":
# belong
like = False
if search_string.find("*") != -1:
search_string.replace("*","%")
like = True
idpackages = dbconn.searchBelongs(search_string, like)
for idpackage in idpackages:
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
elif search_type == "description":
results = dbconn.searchPackagesByDescription(search_string)
for atom, idpackage in results:
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
elif search_type == "eclass":
mysearchlist = search_string.split()
for eclass in mysearchlist:
idpackages = dbconn.searchEclassedPackages(eclass)
for idpackage in idpackages:
search_results['ordered_idpackages'].add(idpackage)
search_results['data'][idpackage] = self._get_entropy_pkginfo(dbconn, idpackage, repoid)
dbconn.closeDB()
return True, search_results
def docmd_get_notice_board(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
repoid = myargs[0]
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(cmd, ' '.join(myargs), uid, gid, 'get_notice_board', [repoid], {}, True, True, interactive = True)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_remove_notice_board_entries(self, cmd, myargs, authenticator):
if len(myargs) < 2:
return False,'wrong arguments'
repoid = myargs[0]
entry_ids = myargs[1:]
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(
cmd, ' '.join([unicode(x) for x in myargs]), uid, gid,
'remove_notice_board_entries', [repoid,entry_ids], {}, True, False, interactive = True
)
if queue_id < 0: return False, queue_id
return True, queue_id
def docmd_add_notice_board_entry(self, cmd, myargs, authenticator):
if not myargs:
return False,'wrong arguments'
xml_string = ' '.join(myargs)
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('repoid') and mydict.has_key('title') and \
mydict.has_key('notice_text') and mydict.has_key('link')):
return None,'wrong dict arguments, xml must have 4 items with attr value -> repoid, title, notice_text, link'
repoid = mydict.get('repoid')
title = mydict.get('title')
notice_text = mydict.get('notice_text')
link = mydict.get('link')
status, userdata, err_str = authenticator.docmd_userdata()
uid = userdata.get('uid')
gid = userdata.get('gid')
queue_id = self.HostInterface.add_to_queue(
cmd, ' '.join(myargs), uid, gid,
'add_notice_board_entry', [repoid,title,notice_text,link], {}, True, False, interactive = True
)
if queue_id < 0: return False, queue_id
return True, queue_id
def _get_entropy_pkginfo(self, dbconn, idpackage, repoid):
data = {}
try:
data['atom'], data['name'], data['version'], data['versiontag'], \
data['description'], data['category'], data['chost'], \
data['cflags'], data['cxxflags'],data['homepage'], \
data['license'], data['branch'], data['download'], \
data['digest'], data['slot'], data['etpapi'], \
data['datecreation'], data['size'], data['revision'] = dbconn.getBaseData(idpackage)
except TypeError:
return data
data['injected'] = dbconn.isInjected(idpackage)
data['repoid'] = repoid
data['idpackage'] = idpackage
return data
class SystemManagerServerInterface(SocketHostInterface):
class SystemCommands(SocketCommandsSkel):
import entropyTools
def __init__(self, HostInterface):
SocketCommandsSkel.__init__(self, HostInterface, inst_name = "systemsrv")
self.raw_commands = [
'systemsrv:add_to_pinboard',
'systemsrv:write_to_running_command_pipe'
]
self.valid_commands = {
'systemsrv:get_queue': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_queue,
'args': ["myargs"],
'as_user': False,
'desc': "get current queue",
'syntax': "<SESSION_ID> systemsrv:get_queue",
'from': unicode(self),
},
'systemsrv:get_queue_item_by_id': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_queue_item_by_id,
'args': ["myargs"],
'as_user': False,
'desc': "get current queue item through its queue id",
'syntax': "<SESSION_ID> systemsrv:get_queue_item_by_id <queue_id>",
'from': unicode(self),
},
'systemsrv:get_queue_id_stdout': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_queue_id_stdout,
'args': ["myargs"],
'as_user': False,
'desc': "get current queue item stdout/stderr output",
'syntax': "<SESSION_ID> systemsrv:get_queue_id_stdout <queue_id> <how many bytes (from tail)>",
'from': unicode(self),
},
'systemsrv:get_queue_id_result': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_queue_id_result,
'args': ["myargs"],
'as_user': False,
'desc': "get current queue item result output",
'syntax': "<SESSION_ID> systemsrv:get_queue_id_result <queue_id>",
'from': unicode(self),
},
'systemsrv:remove_queue_ids': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_queue_ids,
'args': ["myargs"],
'as_user': False,
'desc': "remove queue items using their queue ids",
'syntax': "<SESSION_ID> systemsrv:remove_queue_ids <queue_id 1> <queue_id 2> <...>",
'from': unicode(self),
},
'systemsrv:pause_queue': {
'auth': True,
'built_in': False,
'cb': self.docmd_pause_queue,
'args': ["myargs"],
'as_user': False,
'desc': "toggle queue pause (understood?)",
'syntax': "<SESSION_ID> systemsrv:pause_queue <True/False>",
'from': unicode(self),
},
'systemsrv:kill_processing_queue_id': {
'auth': True,
'built_in': False,
'cb': self.docmd_kill_processing_queue_id,
'args': ["myargs"],
'as_user': False,
'desc': "kill a running process using its queue id",
'syntax': "<SESSION_ID> systemsrv:kill_processing_queue_id <queue_id>",
'from': unicode(self),
},
'systemsrv:swap_items_in_queue': {
'auth': True,
'built_in': False,
'cb': self.docmd_swap_items_in_queue,
'args': ["myargs"],
'as_user': False,
'desc': "swap items in queue to change their order",
'syntax': "<SESSION_ID> systemsrv:swap_items_in_queue <queue_id1> <queue_id1>",
'from': unicode(self),
},
'systemsrv:get_pinboard_data': {
'auth': True,
'built_in': False,
'cb': self.docmd_get_pinboard_data,
'args': [],
'as_user': False,
'desc': "get pinboard content",
'syntax': "<SESSION_ID> systemsrv:get_pinboard_data",
'from': unicode(self),
},
'systemsrv:add_to_pinboard': {
'auth': True,
'built_in': False,
'cb': self.docmd_add_to_pinboard,
'args': ["myargs"],
'as_user': False,
'desc': "add item to pinboard",
'syntax': "<SESSION_ID> systemsrv:add_to_pinboard <xml string containing pinboard note and extended text>",
'from': unicode(self),
},
'systemsrv:remove_from_pinboard': {
'auth': True,
'built_in': False,
'cb': self.docmd_remove_from_pinboard,
'args': ["myargs"],
'as_user': False,
'desc': "remove item from pinboard",
'syntax': "<SESSION_ID> systemsrv:remove_from_pinboard <pinboard identifier 1> <pinboard identifier 2> <...>",
'from': unicode(self),
},
'systemsrv:set_pinboard_items_done': {
'auth': True,
'built_in': False,
'cb': self.docmd_set_pinboard_items_done,
'args': ["myargs"],
'as_user': False,
'desc': "set pinboard items status using their pinboard identifiers",
'syntax': "<SESSION_ID> systemsrv:set_pinboard_items_done <pinboard identifier 1> <pinboard identifier 2> <...> <status (True/False)>",
'from': unicode(self),
},
'systemsrv:write_to_running_command_pipe': {
'auth': True,
'built_in': False,
'cb': self.docmd_write_to_running_command_pipe,
'args': ["myargs"],
'as_user': False,
'desc': "write text to stdin of a running command",
'syntax': "<SESSION_ID> systemsrv:write_to_running_command_pipe <queue_id> <write stdout (True/False)> <txt ...>",
'from': unicode(self),
},
}
def docmd_get_queue(self, myargs):
self.HostInterface.queue_lock_acquire()
try:
myqueue = copy.deepcopy(self.HostInterface.ManagerQueue)
extended = False
if myargs:
extended = myargs[0]
if not extended:
for key in self.HostInterface.done_queue_keys:
for queue_id in myqueue.get(key):
item = myqueue[key].get(queue_id)
if not item.has_key('extended_result'):
continue
item['extended_result'] = None
return True, myqueue
finally:
self.HostInterface.queue_lock_release()
def docmd_get_queue_item_by_id(self, myargs):
if not myargs:
return False,'wrong arguments'
queue_id = myargs[0]
try:
queue_id = int(queue_id)
except ValueError:
return False,'wrong argument: queue_id'
item, key = self.HostInterface.get_item_by_queue_id(queue_id)
if item == None:
return False,'wrong queue id'
item = item.copy()
return True,item
def docmd_get_queue_id_stdout(self, myargs):
if len(myargs) < 1:
return False,'wrong arguments'
queue_id = myargs[0]
bytes_from_tail = myargs[1]
try:
queue_id = int(queue_id)
except ValueError:
return False,'wrong argument: queue_id'
try:
bytes_from_tail = int(bytes_from_tail)
except ValueError:
return False,'wrong argument: lines from tail'
item, key = self.HostInterface.get_item_by_queue_id(queue_id)
if item == None:
return False,'wrong queue id'
item = item.copy()
file_path = item['stdout']
if not (os.path.isfile(file_path) and os.access(file_path,os.R_OK)):
text = ''
else:
f = open(file_path,"r")
f.seek(0,2)
tell_me = f.tell()
if bytes_from_tail < 1:
bytes_from_tail = tell_me
if bytes_from_tail > tell_me:
bytes_from_tail = tell_me
f.seek(-1*bytes_from_tail,2)
text = f.read()
f.close()
return True,text
def docmd_get_queue_id_result(self, myargs):
if not myargs:
return False,'wrong arguments'
queue_id = myargs[0]
try:
queue_id = int(queue_id)
except ValueError:
return False,'wrong argument: queue_id'
item, key = self.HostInterface.get_item_by_queue_id(queue_id)
if item == None:
return False,'wrong queue id'
if key not in self.HostInterface.done_queue_keys:
return False,'process not completed yet'
if not item.has_key('result'):
return False,'result not available'
item = item.copy()
ext_result = None
if item.has_key('extended_result'):
ext_result = item['extended_result']
return True,(item['result'],ext_result,)
def docmd_remove_queue_ids(self, myargs):
if not myargs:
return False,'wrong arguments'
valid_queue_ids = set()
for queue_id in myargs:
item, key = self.HostInterface.get_item_by_queue_id(queue_id)
if (item != None) and (key in self.HostInterface.removable_queue_keys):
valid_queue_ids.add(queue_id)
if not valid_queue_ids:
return False,'no valid queue ids'
# remove
self.HostInterface.remove_from_queue(valid_queue_ids)
return True,'ok'
def docmd_pause_queue(self, myargs):
if not myargs:
return False,'wrong arguments'
do = myargs[0]
if do:
self.HostInterface.pause_queue()
else:
self.HostInterface.play_queue()
return self.HostInterface.ManagerQueue['pause'],'ok'
def docmd_kill_processing_queue_id(self, myargs):
if not myargs:
return False,'wrong arguments'
queue_id = myargs[0]
self.HostInterface.kill_processing_queue_id(queue_id)
return True,'ok'
def docmd_swap_items_in_queue(self, myargs):
if len(myargs) < 2:
return False,'wrong arguments'
queue_id1 = myargs[0]
queue_id2 = myargs[1]
status = self.HostInterface.swap_items_in_queue(queue_id1,queue_id2)
if status:
return True,'ok'
return False,'not done'
def docmd_get_pinboard_data(self):
data = self.HostInterface.get_pinboard_data()
return True,data.copy()
def docmd_add_to_pinboard(self, myargs):
if not myargs:
return False,'wrong arguments'
xml_string = ' '.join(myargs)
try:
mydict = self.entropyTools.dict_from_xml(xml_string)
except Exception, e:
return None,"error: %s" % (e,)
if not (mydict.has_key('note') and mydict.has_key('extended_text')):
return None,'wrong dict arguments, xml must have 2 items with attr value -> note, extended_text'
note = mydict.get('note')
extended_text = mydict.get('extended_text')
self.HostInterface.add_to_pinboard(note, extended_text)
return True,'ok'
def docmd_remove_from_pinboard(self, myargs):
if not myargs:
return False,'wrong arguments'
for pinboard_id in myargs:
try:
pinboard_id = int(pinboard_id)
except ValueError:
continue
self.HostInterface.remove_from_pinboard(pinboard_id)
return True,'ok'
def docmd_set_pinboard_items_done(self, myargs):
if len(myargs) < 2:
return False,'wrong arguments'
status = myargs[-1]
pinboard_ids = myargs[:-1]
for pinboard_id in pinboard_ids:
try:
pinboard_id = int(pinboard_id)
except ValueError:
continue
self.HostInterface.set_pinboard_item_status(pinboard_id, status)
return True,'ok'
def docmd_write_to_running_command_pipe(self, myargs):
if len(myargs) < 2:
return False, 'wrong arguments'
try:
queue_id = int(myargs[0])
except ValueError:
return False,'invalid queue id'
try:
write_stdout = bool(myargs[1])
except ValueError:
write_stdout = False
txt = ' '.join(myargs[2:])+'\n'
item, key = self.HostInterface.get_item_by_queue_id(queue_id)
if key not in self.HostInterface.processing_queue_keys:
return False,'not running'
mypipe = self.HostInterface.ManagerQueueStdInOut.get(queue_id)
if mypipe == None:
return False,'pipe not available'
try:
w_fd = mypipe[1]
except (IndexError, ValueError,):
return False,'pipe vanished'
if not isinstance(w_fd,int):
return False,'stdout fd not an int'
if write_stdout:
stdout = open(item['stdout'],"a+")
try:
os.write(w_fd,txt)
if write_stdout:
stdout.write(txt)
except OSError, e:
return False,'OSError: %s' % (e,)
except IOError, e:
return False,'IOError: %s' % (e,)
finally:
if write_stdout:
stdout.flush()
stdout.close()
return True,'ok'
class FakeServiceInterface:
def __init__(self, *args, **kwargs):
pass
class BuiltInSystemManagerExecutorCommands:
def __init__(self, SystemManagerExecutorInstance, *args, **kwargs):
self.SystemManagerExecutor = SystemManagerExecutorInstance
self.available_commands = {
'hello_world': {
'func': self.hello_world,
'args': 0,
}
}
def hello_world(self):
rc = subprocess.call('echo hello world', shell = True)
return True,rc
queue_file = 'system_manager_queue'
pinboard_file = "system_manager_pinboard"
STDOUT_STORAGE_DIR = os.path.join(etpConst['dumpstoragedir'],'system_manager_stdout')
def __init__(self, EntropyInterface, do_ssl = False, stdout_logging = True, fork_requests = False, entropy_interface_kwargs = {}, **kwargs):
#nocolor()
self.queue_loaded = False
import entropyTools, dumpTools
self.entropyTools, self.dumpTools = entropyTools, dumpTools
from datetime import datetime
self.datetime = datetime
self.setup_stdout_storage_dir()
if not kwargs.has_key('external_cmd_classes'):
kwargs['external_cmd_classes'] = []
kwargs['external_cmd_classes'].insert(0,self.SystemCommands)
self.Entropy = EntropyInterface(**entropy_interface_kwargs)
self.Text = TextInterface()
self.SystemExecutor = SystemManagerExecutorInterface(self, self.Entropy)
self.ExecutorCommandClasses = [(self.BuiltInSystemManagerExecutorCommands,[],{},)]
self.ExecutorCommandInstances = []
if kwargs.has_key('external_executor_cmd_classes'):
self.ExecutorCommandClasses += kwargs.pop('external_executor_cmd_classes')
self.handle_executor_command_classes_initialization()
self.QueueProcessor = None
self.QueueProcessorParallel = None
self.QueueLock = thread.allocate_lock()
self.PinboardLock = thread.allocate_lock()
self.do_ssl = do_ssl
self.ServiceInterface = EntropyInterface
self.PinboardData = {}
self.load_pinboard()
self.done_queue_keys = ['processed','errored']
self.removable_queue_keys = ['processed','errored','queue']
self.processing_queue_keys = ['processing']
self.dict_queue_keys = ['queue','processing','processed','errored']
self.ManagerQueueStdInOut = {}
self.ManagerQueue = {
'queue': {},
'queue_order': [],
'processing': {},
'processing_order': [],
'processed': {},
'processed_order': [],
'errored' : {},
'errored_order': [],
'pause': True
}
self.load_queue()
self.queue_loaded = True
if self.ManagerQueue['processing'] or self.ManagerQueue['processing_order']:
self.ManagerQueue['processing'].clear()
del self.ManagerQueue['processing_order'][:]
self.store_queue()
SocketHostInterface.__init__(
self,
self.FakeServiceInterface, #self.ServiceInterface,
sock_output = self.Text,
ssl = do_ssl,
**kwargs
)
self.stdout_logging = stdout_logging
self.fork_requests = fork_requests
self.load_queue_processors()
# here we can put anything that must be loaded before the queue processor execution
self.play_queue()
def __del__(self):
if hasattr(self,'queue_loaded'):
if self.queue_loaded:
self.store_queue()
def handle_executor_command_classes_initialization(self):
for myclass, args, kwargs in self.ExecutorCommandClasses:
myintf = myclass(self.SystemExecutor, *args,**kwargs)
if hasattr(myintf,'available_commands'):
self.SystemExecutor.register(myintf.available_commands)
self.ExecutorCommandInstances.append(myintf)
else:
del myintf
def setup_stdout_storage_dir(self):
if os.path.isfile(self.STDOUT_STORAGE_DIR) or os.path.islink(self.STDOUT_STORAGE_DIR):
os.remove(self.STDOUT_STORAGE_DIR)
if not os.path.isdir(self.STDOUT_STORAGE_DIR):
os.makedirs(self.STDOUT_STORAGE_DIR,0775)
if etpConst['entropygid'] != None:
const_setup_perms(self.STDOUT_STORAGE_DIR,etpConst['entropygid'])
def pinboard_lock_acquire(self):
self.PinboardLock.acquire()
def pinboard_lock_release(self):
self.PinboardLock.release()
def load_pinboard(self):
obj = self.get_stored_pinboard()
if isinstance(obj,dict):
self.PinboardData = obj
return True
return False
def get_stored_pinboard(self):
return self.dumpTools.loadobj(self.pinboard_file)
def store_pinboard(self):
self.dumpTools.dumpobj(self.pinboard_file, self.PinboardData)
def add_to_pinboard(self, note, extended_text):
self.pinboard_lock_acquire()
try:
mydata = {
'note': note,
'extended_text': extended_text,
'ts': self.get_ts(),
'done': False,
}
pinboard_id = self.get_pinboard_id()
self.PinboardData[pinboard_id] = mydata
self.store_pinboard()
finally:
self.pinboard_lock_release()
def remove_from_pinboard(self, pinboard_id):
self.pinboard_lock_acquire()
try:
if self.PinboardData.has_key(pinboard_id):
self.PinboardData.pop(pinboard_id)
self.store_pinboard()
return True
return False
finally:
self.pinboard_lock_release()
def set_pinboard_item_status(self, pinboard_id, status):
self.pinboard_lock_acquire()
try:
if self.PinboardData.has_key(pinboard_id):
self.PinboardData[pinboard_id]['done'] = status
self.store_pinboard()
return True
return False
finally:
self.pinboard_lock_release()
def get_pinboard_id(self):
numbers = self.PinboardData.keys()
if numbers:
number = max(numbers)+1
else:
number = 1
return number
def get_pinboard_data(self):
self.pinboard_lock_acquire()
try:
return self.PinboardData.copy()
finally:
self.pinboard_lock_release()
def load_queue_processors(self):
self.QueueProcessor = self.entropyTools.parallelTask(self.queue_processor, False)
self.QueueProcessor.start()
self.QueueProcessorParallel = self.entropyTools.parallelTask(self.queue_processor, True)
self.QueueProcessorParallel.start()
def queue_lock_acquire(self):
self.QueueLock.acquire()
def queue_lock_release(self):
self.QueueLock.release()
def get_stored_queue(self):
return self.dumpTools.loadobj(self.queue_file)
def load_queue(self):
obj = self.get_stored_queue()
if isinstance(obj,dict):
for key in self.ManagerQueue.keys():
if not obj.has_key(key):
return False
self.ManagerQueue = obj
return True
return False
def store_queue(self):
self.dumpTools.dumpobj(self.queue_file, self.ManagerQueue)
def get_ts(self):
return self.datetime.fromtimestamp(time.time())
def swap_items_in_queue(self, queue_id1, queue_id2):
self.queue_lock_acquire()
try:
item1, key1 = self.get_item_by_queue_id(queue_id1)
item2, key2 = self.get_item_by_queue_id(queue_id2)
if key1 != key2:
return False
t_item = item1.copy()
item1.clear()
item1.update(item2)
item2.clear()
item2.update(t_item)
# fix the _order
queue_id1_idx = self.ManagerQueue[key1+"_order"].index(queue_id1)
queue_id2_idx = self.ManagerQueue[key2+"_order"].index(queue_id2)
self.ManagerQueue[key1+"_order"][queue_id1_idx] = queue_id2
self.ManagerQueue[key2+"_order"][queue_id2_idx] = queue_id1
self.store_queue()
finally:
self.queue_lock_release()
return True
def add_to_queue(self, command_name, command_text, user_id, group_id, function, args, kwargs, do_parallel, extended_result, interactive = False):
if function not in self.SystemExecutor.available_commands:
return -1
self.queue_lock_acquire()
try:
queue_id = self.generate_unique_queue_id()
if interactive:
self.ManagerQueueStdInOut[queue_id] = os.pipe()
myqueue_dict = {
'queue_id': queue_id,
'command_name': command_name,
'command_desc': self.valid_commands[command_name]['desc'],
'command_text': command_text,
'call': function,
'args': copy.deepcopy(args),
'kwargs': copy.deepcopy(kwargs),
'user_id': user_id,
'group_id': group_id,
'stdout': self.assign_unique_stdout_file(queue_id),
'queue_ts': "%s" % (self.get_ts(),),
'kill': False,
'processing_pid': None,
'do_parallel': do_parallel,
'interactive': False,
}
if extended_result:
myqueue_dict['extended_result'] = None
self.ManagerQueue['queue'][queue_id] = myqueue_dict
self.ManagerQueue['queue_order'].append(queue_id)
self.store_queue()
finally:
self.queue_lock_release()
return queue_id
def remove_from_queue(self, queue_ids):
self.queue_lock_acquire()
removed = False
try:
for key in self.ManagerQueue:
if key not in self.dict_queue_keys:
continue
for queue_id in queue_ids:
item = None
try:
item = self.ManagerQueue[key].pop(queue_id)
except KeyError:
continue
if item:
self.flush_item(item, queue_id)
if queue_id in self.ManagerQueue[key+"_order"]:
self.ManagerQueue[key+"_order"].remove(queue_id)
removed = True
if removed:
self.store_queue()
finally:
self.queue_lock_release()
return removed
def kill_processing_queue_id(self, queue_id):
self.queue_lock_acquire()
try:
item, key = self.get_item_by_queue_id(queue_id)
if key in self.processing_queue_keys:
item['kill'] = True
finally:
self.queue_lock_release()
def pause_queue(self):
self.queue_lock_acquire()
try:
self.ManagerQueue['pause'] = True
finally:
self.queue_lock_release()
def play_queue(self):
self.queue_lock_acquire()
try:
self.ManagerQueue['pause'] = False
finally:
self.queue_lock_release()
def flush_item(self, item, queue_id):
if not isinstance(item,dict):
return False
if item.has_key('stdout'):
stdout = item['stdout']
if (os.path.isfile(stdout) and os.access(stdout,os.W_OK)):
os.remove(stdout)
if item.has_key('interactive'):
if item['interactive'] and (queue_id in self.ManagerQueueStdInOut):
stdin, stdout = self.ManagerQueueStdInOut.pop(queue_id)
os.close(stdin)
os.close(stdout)
return True
def assign_unique_stdout_file(self, queue_id):
stdout = os.path.join(self.STDOUT_STORAGE_DIR,"%d.%s" % (queue_id,"stdout",))
if os.path.isfile(stdout):
os.remove(stdout)
count = 0
orig_stdout = stdout
while os.path.lexists(stdout):
count += 1
stdout = "%s.%d" % (orig_stdout,count,)
return stdout
def generate_unique_queue_id(self):
current_ids = set()
for key in self.ManagerQueue:
if not key.endswith("_order"):
continue
current_ids |= set(self.ManagerQueue[key])
while 1:
queue_id = abs(hash(os.urandom(20)))
if queue_id not in current_ids:
return queue_id
def get_item_by_queue_id(self, queue_id):
for key in self.dict_queue_keys:
item = self.ManagerQueue[key].get(queue_id)
if item != None:
return item, key
return None, None
def _pop_item_from_queue_by_parallel(self, parallel):
self.queue_lock_acquire()
try:
for idx in range(len(self.ManagerQueue['queue_order'])):
queue_id = self.ManagerQueue['queue_order'][idx]
if parallel == self.ManagerQueue['queue'][queue_id]['do_parallel']:
return self.ManagerQueue['queue'].pop(queue_id), self.ManagerQueue['queue_order'].pop(idx)
finally:
self.queue_lock_release()
return None, None
def queue_processor(self, parallel_mode, fork_data = None):
def do_store_and_free():
self.store_queue()
self.queue_lock_release()
def wait_and_takeover():
self.queue_lock_acquire()
def copy_obj(obj):
if isinstance(obj,(dict,set,)):
return obj.copy()
elif isinstance(obj,(list,tuple,)):
return obj[:]
return obj
while 1:
try:
if not fork_data:
if self.ManagerQueue['pause']:
time.sleep(0.1)
continue
if not self.ManagerQueue['queue_order']:
time.sleep(0.1)
continue
command_data, queue_id = self._pop_item_from_queue_by_parallel(parallel_mode)
if not command_data:
time.sleep(0.5)
continue
wait_and_takeover()
command_data['processing_ts'] = "%s" % (self.get_ts(),)
self.ManagerQueue['processing'][queue_id] = command_data
self.ManagerQueue['processing_order'].append(queue_id)
do_store_and_free()
else:
command_data, queue_id = fork_data
try:
if parallel_mode:
t = self.entropyTools.parallelTask(self.queue_processor, False, fork_data = (command_data, queue_id,))
t.start()
continue
else:
done, result = self.SystemExecutor.execute_task(command_data)
except Exception, e:
self.queue_lock_release()
self.entropyTools.printTraceback()
done = False
result = (False, unicode(e),)
wait_and_takeover()
if command_data.has_key('extended_result') and done:
try:
command_data['result'], command_data['extended_result'] = copy_obj(result)
except TypeError:
done = False
command_data['result'] = 'wrong tuple split from queue processor (1)'
command_data['extended_result'] = None
else:
command_data['result'] = copy_obj(result)
if not done:
try:
self.ManagerQueue['processing'].pop(queue_id)
except KeyError:
pass
if queue_id in self.ManagerQueue['processing_order']:
self.ManagerQueue['processing_order'].remove(queue_id)
command_data['errored_ts'] = "%s" % (self.get_ts(),)
self.ManagerQueue['errored'][queue_id] = command_data
self.ManagerQueue['errored_order'].append(queue_id)
do_store_and_free()
if fork_data:
break
continue
try:
done, cmd_result = result
except TypeError:
done = False
command_data['result'] = 'wrong tuple split from queue processor (2)'
if not done:
try:
self.ManagerQueue['processing'].pop(queue_id)
except KeyError:
pass
if queue_id in self.ManagerQueue['processing_order']:
self.ManagerQueue['processing_order'].remove(queue_id)
command_data['errored_ts'] = "%s" % (self.get_ts(),)
self.ManagerQueue['errored'][queue_id] = command_data
self.ManagerQueue['errored_order'].append(queue_id)
do_store_and_free()
if fork_data:
break
continue
try:
self.ManagerQueue['processing'].pop(queue_id)
except KeyError:
pass
if queue_id in self.ManagerQueue['processing_order']:
self.ManagerQueue['processing_order'].remove(queue_id)
command_data['processed_ts'] = "%s" % (self.get_ts(),)
self.ManagerQueue['processed'][queue_id] = command_data
self.ManagerQueue['processed_order'].append(queue_id)
do_store_and_free()
if fork_data:
break
except:
try:
self.queue_lock_release()
except thread.error:
pass
raise
def killall(self):
SocketHostInterface.killall(self)
if self.QueueProcessor != None:
self.QueueProcessor.kill()
if self.QueueProcessorParallel != None:
self.QueueProcessorParallel.kill()
class SystemManagerClientCommands(EntropySocketClientCommands):
import entropyTools
def __init__(self, *args, **kwargs):
EntropySocketClientCommands.__init__(self, *args, **kwargs)
def service_login(self, username, password, session_id):
cmd = "%s %s %s %s %s" % (
session_id,
'login',
username,
'plain',
password,
)
return self.do_generic_handler(cmd, session_id)
def get_queue(self, session_id, extended):
cmd = "%s %s %s" % (
session_id,
'systemsrv:get_queue',
extended,
)
return self.do_generic_handler(cmd, session_id)
def get_queue_item_by_id(self, session_id, queue_id):
cmd = "%s %s %d" % (
session_id,
'systemsrv:get_queue_item_by_id',
queue_id,
)
return self.do_generic_handler(cmd, session_id)
def get_queue_id_stdout(self, session_id, queue_id, last_bytes):
cmd = "%s %s %d %d" % (
session_id,
'systemsrv:get_queue_id_stdout',
queue_id,
last_bytes,
)
# enable zlib compression
compression = self.set_gzip_compression(session_id, True)
rc = self.do_generic_handler(cmd, session_id, compression = compression)
# disable compression
self.set_gzip_compression(session_id, False)
return rc
def get_queue_id_result(self, session_id, queue_id):
cmd = "%s %s %d" % (
session_id,
'systemsrv:get_queue_id_result',
queue_id,
)
return self.do_generic_handler(cmd, session_id)
def remove_queue_ids(self, session_id, queue_ids):
cmd = "%s %s %s" % (
session_id,
'systemsrv:remove_queue_ids',
' '.join([str(x) for x in queue_ids]),
)
return self.do_generic_handler(cmd, session_id)
def pause_queue(self, session_id, do_pause):
cmd = "%s %s %s" % (
session_id,
'systemsrv:pause_queue',
do_pause,
)
return self.do_generic_handler(cmd, session_id)
def kill_processing_queue_id(self, session_id, queue_id):
cmd = "%s %s %s" % (
session_id,
'systemsrv:kill_processing_queue_id',
queue_id,
)
return self.do_generic_handler(cmd, session_id)
def swap_items_in_queue(self, session_id, queue_id1, queue_id2):
cmd = "%s %s %d %d" % (
session_id,
'systemsrv:swap_items_in_queue',
queue_id1,
queue_id2,
)
return self.do_generic_handler(cmd, session_id)
def get_pinboard_data(self, session_id):
cmd = "%s %s" % (
session_id,
'systemsrv:get_pinboard_data',
)
return self.do_generic_handler(cmd, session_id)
def add_to_pinboard(self, session_id, note, extended_text):
mydict = {
'note': note,
'extended_text': extended_text,
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s" % (
session_id,
'systemsrv:add_to_pinboard',
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def remove_from_pinboard(self, session_id, pinboard_ids):
cmd = "%s %s %s" % (
session_id,
'systemsrv:remove_from_pinboard',
' '.join([str(x) for x in pinboard_ids]),
)
return self.do_generic_handler(cmd, session_id)
def set_pinboard_items_done(self, session_id, pinboard_ids, status):
cmd = "%s %s %s %s" % (
session_id,
'systemsrv:set_pinboard_items_done',
' '.join([str(x) for x in pinboard_ids]),
status,
)
return self.do_generic_handler(cmd, session_id)
def write_to_running_command_pipe(self, session_id, queue_id, write_to_stdout, txt):
cmd = "%s %s %s %s %s" % (
session_id,
'systemsrv:write_to_running_command_pipe',
queue_id,
write_to_stdout,
txt,
)
return self.do_generic_handler(cmd, session_id)
class SystemManagerRepositoryClientCommands(SystemManagerClientCommands):
import dumpTools
def sync_spm(self, session_id):
cmd = "%s %s" % (
session_id,
'srvrepo:sync_spm',
)
return self.do_generic_handler(cmd, session_id)
def compile_atoms(self, session_id, atoms, pretend = False, oneshot = False, verbose = False, nocolor = True, fetchonly = False, buildonly = False, nodeps = False, custom_use = '', ldflags = '', cflags = ''):
s_pretend = "0"
s_oneshot = "0"
s_verbose = "0"
s_nocolor = "0"
s_fetchonly = "0"
s_buildonly = "0"
s_nodeps = "0"
if pretend: s_pretend = "1"
if oneshot: s_oneshot = "1"
if verbose: s_verbose = "1"
if nocolor: s_nocolor = "1"
if fetchonly: s_fetchonly = "1"
if buildonly: s_buildonly = "1"
if nodeps: s_nodeps = "1"
mydict = {
'atoms': ' '.join(atoms),
'pretend': s_pretend,
'oneshot': s_oneshot,
'verbose': s_verbose,
'nocolor': s_nocolor,
'fetchonly': s_fetchonly,
'buildonly': s_buildonly,
'nodeps': s_nodeps,
'custom_use': custom_use,
'ldflags': ldflags,
'cflags': cflags,
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s" % (
session_id,
'srvrepo:compile_atoms',
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def spm_remove_atoms(self, session_id, atoms, pretend = False, verbose = False, nocolor = True):
s_pretend = "0"
s_verbose = "0"
s_nocolor = "0"
if pretend: s_pretend = "1"
if verbose: s_verbose = "1"
if nocolor: s_nocolor = "1"
mydict = {
'atoms': ' '.join(atoms),
'pretend': s_pretend,
'verbose': s_verbose,
'nocolor': s_nocolor,
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s" % (
session_id,
'srvrepo:spm_remove_atoms',
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def get_spm_categories_updates(self, session_id, categories):
cmd = "%s %s %s" % (
session_id,
'srvrepo:get_spm_categories_updates',
' '.join(categories),
)
return self.do_generic_handler(cmd, session_id)
def get_spm_categories_installed(self, session_id, categories):
cmd = "%s %s %s" % (
session_id,
'srvrepo:get_spm_categories_installed',
' '.join(categories),
)
return self.do_generic_handler(cmd, session_id)
def enable_uses_for_atoms(self, session_id, atoms, useflags):
mydict = {
'atoms': ' '.join(atoms),
'useflags': ' '.join(useflags),
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s" % (
session_id,
'srvrepo:enable_uses_for_atoms',
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def disable_uses_for_atoms(self, session_id, atoms, useflags):
mydict = {
'atoms': ' '.join(atoms),
'useflags': ' '.join(useflags),
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s" % (
session_id,
'srvrepo:disable_uses_for_atoms',
xml_string,
)
return self.do_generic_handler(cmd, session_id)
def get_spm_atoms_info(self, session_id, atoms):
cmd = "%s %s %s" % (
session_id,
'srvrepo:get_spm_atoms_info',
' '.join(atoms),
)
return self.do_generic_handler(cmd, session_id)
def run_spm_info(self, session_id):
cmd = "%s %s" % (
session_id,
'srvrepo:run_spm_info',
)
return self.do_generic_handler(cmd, session_id)
def run_custom_shell_command(self, session_id, command):
cmd = "%s %s %s" % (
session_id,
'srvrepo:run_custom_shell_command',
command,
)
return self.do_generic_handler(cmd, session_id)
def get_spm_glsa_data(self, session_id, list_type):
cmd = "%s %s %s" % (
session_id,
'srvrepo:get_spm_glsa_data',
list_type,
)
return self.do_generic_handler(cmd, session_id)
def get_available_repositories(self, session_id):
cmd = "%s %s" % (
session_id,
'srvrepo:get_available_repositories',
)
return self.do_generic_handler(cmd, session_id)
def set_default_repository(self, session_id, repoid):
cmd = "%s %s %s" % (
session_id,
'srvrepo:set_default_repository',
repoid,
)
return self.do_generic_handler(cmd, session_id)
def get_available_entropy_packages(self, session_id, repoid):
cmd = "%s %s %s" % (
session_id,
'srvrepo:get_available_entropy_packages',
repoid,
)
return self.do_generic_handler(cmd, session_id)
def get_entropy_idpackage_information(self, session_id, idpackage, repoid):
cmd = "%s %s %d %s" % (
session_id,
'srvrepo:get_entropy_idpackage_information',
idpackage,
repoid,
)
return self.do_generic_handler(cmd, session_id)
def remove_entropy_packages(self, session_id, matched_atoms):
cmd = "%s %s %s" % (
session_id,
'srvrepo:remove_entropy_packages',
','.join(["%s:%s" % (str(x[0]),str(x[1]),) for x in matched_atoms]), # 1:repoid,2:repoid
)
return self.do_generic_handler(cmd, session_id)
def search_entropy_packages(self, session_id, search_type, search_string, repoid):
cmd = "%s %s %s %s %s" % (
session_id,
'srvrepo:search_entropy_packages',
repoid,
search_type,
search_string,
)
return self.do_generic_handler(cmd, session_id)
def move_entropy_packages_to_repository(self, session_id, idpackages, from_repo, to_repo, do_copy):
cmd = "%s %s %s %s %s %s" % (
session_id,
'srvrepo:move_entropy_packages_to_repository',
from_repo,
to_repo,
do_copy,
' '.join([str(x) for x in idpackages]),
)
return self.do_generic_handler(cmd, session_id)
def scan_entropy_packages_database_changes(self, session_id):
cmd = "%s %s" % (
session_id,
'srvrepo:scan_entropy_packages_database_changes',
)
return self.do_generic_handler(cmd, session_id)
def run_entropy_database_updates(self, session_id, to_add, to_remove, to_inject):
cmd = "%s %s %s %s %s" % (
session_id,
'srvrepo:run_entropy_database_updates',
','.join(["%s:%s:%s" % (str(x[0]),str(x[1]),str(x[2]),) for x in to_add]),
','.join(["%s:%s" % (str(x[0]),str(x[1]),) for x in to_remove]),
','.join(["%s:%s" % (str(x[0]),str(x[1]),) for x in to_inject]),
)
return self.do_generic_handler(cmd, session_id)
def run_entropy_dependency_test(self, session_id):
cmd = "%s %s" % (
session_id,
'srvrepo:run_entropy_dependency_test',
)
return self.do_generic_handler(cmd, session_id)
def run_entropy_library_test(self, session_id):
cmd = "%s %s" % (
session_id,
'srvrepo:run_entropy_library_test',
)
return self.do_generic_handler(cmd, session_id)
def run_entropy_treeupdates(self, session_id, repoid):
cmd = "%s %s %s" % (
session_id,
'srvrepo:run_entropy_treeupdates',
repoid,
)
return self.do_generic_handler(cmd, session_id)
def scan_entropy_mirror_updates(self, session_id, repositories):
cmd = "%s %s %s" % (
session_id,
'srvrepo:scan_entropy_mirror_updates',
' '.join(repositories),
)
return self.do_generic_handler(cmd, session_id)
def run_entropy_mirror_updates(self, session_id, repository_data):
serialized_string = self.dumpTools.serialize_string(repository_data)
cmd = "%s %s %s" % (
session_id,
'srvrepo:run_entropy_mirror_updates',
serialized_string,
)
return self.do_generic_handler(cmd, session_id)
def run_entropy_checksum_test(self, session_id, repoid, mode):
cmd = "%s %s %s %s" % (
session_id,
'srvrepo:run_entropy_checksum_test',
repoid,
mode,
)
return self.do_generic_handler(cmd, session_id)
def get_notice_board(self, session_id, repoid):
cmd = "%s %s %s" % (
session_id,
'srvrepo:get_notice_board',
repoid,
)
return self.do_generic_handler(cmd, session_id)
def remove_notice_board_entries(self, session_id, repoid, entry_ids):
cmd = "%s %s %s %s" % (
session_id,
'srvrepo:remove_notice_board_entries',
repoid,
' '.join([str(x) for x in entry_ids]),
)
return self.do_generic_handler(cmd, session_id)
def add_notice_board_entry(self, session_id, repoid, title, notice_text, link):
mydict = {
'repoid': repoid,
'title': title,
'notice_text': notice_text,
'link': link,
}
xml_string = self.entropyTools.xml_from_dict(mydict)
cmd = "%s %s %s" % (
session_id,
'srvrepo:add_notice_board_entry',
xml_string,
)
return self.do_generic_handler(cmd, session_id)
class SystemManagerMethodsInterface:
def __init__(self, SystemManagerClientInstance):
self.Manager = SystemManagerClientInstance
self.available_commands = {
'get_available_commands': {
'desc': _("Get a list of remotely available commands"),
'params': [],
'call': self.get_available_commands,
'private': True,
},
'get_queue': {
'desc': _("Get current queue content"),
'params': [
('extended',bool,_('Extended results'),False,)
],
'call': self.get_queue,
'private': True,
},
'get_queue_item_by_id': {
'desc': _("Get queue item using its queue unique identifier"),
'params': [('queue_id',int,_('Queue Identifier'),True,)],
'call': self.get_queue_item_by_id,
'private': True,
},
'get_queue_id_stdout': {
'desc': _("Get queue stdout/stderr using its queue unique identifier"),
'params': [('queue_id',int,_('Queue Identifier'),True,)],
'call': self.get_queue_id_stdout,
'private': True,
},
'get_queue_id_stdout': {
'desc': _("Get queued command result using its queue unique identifier"),
'params': [('queue_id',int,_('Queue Identifier'),True,)],
'call': self.get_queue_id_result,
'private': True,
},
'remove_queue_ids': {
'desc': _("Remove queued commands using their queue unique identifiers"),
'params': [('queue_ids',list,_('Queue Identifiers'),True,)],
'call': self.remove_queue_ids,
'private': True,
},
'pause_queue': {
'desc': _("Toggle queue pause (True/False)"),
'params': [('do_pause',bool,_('Pause or not'),True,)],
'call': self.pause_queue,
'private': True,
},
'kill_processing_queue_id': {
'desc': _("Kill a running process through its queue id"),
'params': [('queue_id',int,_('Queue Identifier'),True,)],
'call': self.kill_processing_queue_id,
'private': True,
},
'swap_items_in_queue': {
'desc': _("Swap items in queue using their queue ids"),
'params': [
('queue_id1',int,_('Queue Identifier'),True,),
('queue_id2',int,_('Queue Identifier'),True,)
],
'call': self.swap_items_in_queue,
'private': True,
},
'get_pinboard_data': {
'desc': _("Get pinboard content"),
'params': [],
'call': self.get_pinboard_data,
'private': True,
},
'add_to_pinboard': {
'desc': _("Add item to pinboard"),
'params': [
('note',basestring,_('Note'),True,),
('extended_text',basestring,_('Extended text'),True,)
],
'call': self.add_to_pinboard,
'private': True,
},
'remove_from_pinboard': {
'desc': _("Remove item from pinboard"),
'params': [('pinboard_ids',list,_('Pinboard identifiers'),True,)],
'call': self.remove_from_pinboard,
'private': True,
},
'set_pinboard_items_done': {
'desc': _("Set pinboard items status (done/not done)"),
'params': [
('pinboard_ids',list,_('Pinboard identifiers'),True,),
('done_status',bool,_('Done status'),True,),
],
'call': self.set_pinboard_items_done,
'private': True,
},
'write_to_running_command_pipe': {
'desc': _("Write to a remote running command stdin"),
'params': [
('queue_id',int,_('Queue Identifier'),True,),
('write_to_stdout',bool,_('Write to stdout?'),True,),
('txt',basestring,_('Text'),True,),
],
'call': self.write_to_running_command_pipe,
'private': True,
},
}
def get_available_commands(self):
return self.Manager.do_cmd(False, "available_commands", [], {})
def get_queue(self, extended = False):
return self.Manager.do_cmd(True, "get_queue", [extended], {})
def get_queue_item_by_id(self, queue_id):
return self.Manager.do_cmd(True, "get_queue_item_by_id", [queue_id], {})
def get_queue_id_stdout(self, queue_id, last_bytes = 0):
return self.Manager.do_cmd(True, "get_queue_id_stdout", [queue_id, last_bytes], {})
def get_queue_id_result(self, queue_id):
return self.Manager.do_cmd(True, "get_queue_id_result", [queue_id], {})
def remove_queue_ids(self, queue_ids):
return self.Manager.do_cmd(True, "remove_queue_ids", [queue_ids], {})
def pause_queue(self, do_queue):
return self.Manager.do_cmd(True, "pause_queue", [do_queue], {})
def kill_processing_queue_id(self, queue_id):
return self.Manager.do_cmd(True, "kill_processing_queue_id", [queue_id], {})
def swap_items_in_queue(self, queue_id1, queue_id2):
return self.Manager.do_cmd(True, "swap_items_in_queue", [queue_id1,queue_id2], {})
def get_pinboard_data(self):
return self.Manager.do_cmd(True, "get_pinboard_data", [], {})
def add_to_pinboard(self, note, extended_text):
return self.Manager.do_cmd(True, "add_to_pinboard", [note,extended_text], {})
def remove_from_pinboard(self, pinboard_ids):
return self.Manager.do_cmd(True, "remove_from_pinboard", [pinboard_ids], {})
def set_pinboard_items_done(self, pinboard_ids, done_status):
return self.Manager.do_cmd(True, "set_pinboard_items_done", [pinboard_ids,done_status], {})
def write_to_running_command_pipe(self, queue_id, write_to_stdout, txt):
return self.Manager.do_cmd(True, "write_to_running_command_pipe", [queue_id, write_to_stdout, txt], {})
class SystemManagerRepositoryMethodsInterface(SystemManagerMethodsInterface):
def __init__(self, *args, **kwargs):
SystemManagerMethodsInterface.__init__(self, *args, **kwargs)
self.available_commands.update({
'sync_spm': {
'desc': _("Update Spm Repository (emerge --sync)"),
'params': [],
'call': self.sync_spm,
'private': False,
},
'compile_atoms': {
'desc': _("Compile specified atoms with specified parameters"),
'params': [
('atoms',list,_('Atoms'),True,),
('pretend',bool,_('Pretend'),False,),
('oneshot',bool,_('Oneshot'),False,),
('verbose',bool,_('Verbose'),False,),
('nocolor',bool,_('No color'),False,),
('fetchonly',bool,_('Fetch only'),False,),
('buildonly',bool,_('Build only'),False,),
('nodeps',bool,_('No dependencies'),False,),
('custom_use',basestring,_('Custom USE'),False,),
('ldflags',basestring,_('Custom LDFLAGS'),False,),
('cflags',basestring,_('Custom CFLAGS'),False,),
],
'call': self.compile_atoms,
'private': False,
},
'spm_remove_atoms': {
'desc': _("Remove specified atoms with specified parameters"),
'params': [
('atoms',list,_('Atoms'),True,),
('pretend',bool,_('Pretend'),False,),
('verbose',bool,_('Verbose'),False,),
('nocolor',bool,_('No color'),False,),
],
'call': self.spm_remove_atoms,
'private': False,
},
'get_spm_categories_updates': {
'desc': _("Get SPM updates for the specified categories"),
'params': [('categories',list,_('Categories'),True,)],
'call': self.get_spm_categories_updates,
'private': False,
},
'get_spm_categories_installed': {
'desc': _("Get SPM installed packages for the specified categories"),
'params': [('categories',list,_('Categories'),True,)],
'call': self.get_spm_categories_installed,
'private': False,
},
'enable_uses_for_atoms': {
'desc': _("Enable USE flags for the specified atoms"),
'params': [
('atoms',list,_('Atoms'),True,),
('useflags',list,_('USE flags'),True,)
],
'call': self.enable_uses_for_atoms,
'private': False,
},
'disable_uses_for_atoms': {
'desc': _("Disable USE flags for the specified atoms"),
'params': [
('atoms',list,_('Atoms'),True,),
('useflags',list,_('USE flags'),True,)
],
'call': self.disable_uses_for_atoms,
'private': False,
},
'get_spm_atoms_info': {
'desc': _("Get info for the specified atoms"),
'params': [('atoms',list,_('Atoms'),True,)],
'call': self.get_spm_atoms_info,
'private': False,
},
'run_spm_info': {
'desc': _("Run SPM info command"),
'params': [],
'call': self.run_spm_info,
'private': False,
},
'run_custom_shell_command': {
'desc': _("Run custom shell command"),
'params': [
('command',basestring,_('Command'),True,)
],
'call': self.run_custom_shell_command,
'private': False,
},
'get_spm_glsa_data': {
'desc': _("Get Spm security updates information"),
'params': [
('list_type',basestring,_('List type (affected,new,all)'),True,)
],
'call': self.get_spm_glsa_data,
'private': False,
},
'get_available_repositories': {
'desc': _("Get information about available Entropy repositories"),
'params': [],
'call': self.get_available_repositories,
'private': False,
},
'set_default_repository': {
'desc': _("Set default Entropy Server repository"),
'params': [
('repoid',basestring,_('Repository Identifier'),True,)
],
'call': self.set_default_repository,
'private': False,
},
'get_available_entropy_packages': {
'desc': _("Get available packages inside the specified repository"),
'params': [
('repoid',basestring,_('Repository Identifier'),True,)
],
'call': self.get_available_entropy_packages,
'private': False,
},
'get_entropy_idpackage_information': {
'desc': _("Get idpackage metadata using its idpackage in the specified repository"),
'params': [
('idpackage',int,_('Package Identifier'),True,),
('repoid',basestring,_('Repository Identifier'),True,)
],
'call': self.get_entropy_idpackage_information,
'private': False,
},
'remove_entropy_packages': {
'desc': _("Remove the specified Entropy package matches (idpackage,repoid)"),
'params': [
('matched_atoms',list,_('Matched atoms'),True,)
],
'call': self.remove_entropy_packages,
'private': False,
},
'search_entropy_packages': {
'desc': _("Search Entropy packages using a defined set of search types in the specified repository"),
'params': [
('search_type',basestring,_('Search type'),True,),
('search_string',basestring,_('Search string'),True,),
('repoid',basestring,_('Repository Identifier'),True,)
],
'call': self.search_entropy_packages,
'private': False,
},
'move_entropy_packages_to_repository': {
'desc': _("Move or copy a package from a repository to another"),
'params': [
('idpackages',list,_('Package identifiers'),True,),
('from_repo',basestring,_('From repository'),True,),
('to_repo',basestring,_('To repository'),True,),
('do_copy',bool,_('Copy instead of move?'),False,)
],
'call': self.search_entropy_packages,
'private': False,
},
'scan_entropy_packages_database_changes': {
'desc': _("Scan Spm package changes and retrieve a list of action that should be run on the repositories"),
'params': [],
'call': self.scan_entropy_packages_database_changes,
'private': False,
},
'run_entropy_database_updates': {
'desc': _("Run Entropy database updates"),
'params': [
('to_add',list,_('Matches to add from Spm'),True,),
('to_remove',list,_('Matches to remove from repository database'),True,),
('to_inject',list,_('Matches to inject on repository database'),True,),
],
'call': self.run_entropy_database_updates,
'private': False,
},
'run_entropy_dependency_test': {
'desc': _("Run Entropy dependency test"),
'params': [],
'call': self.run_entropy_dependency_test,
'private': False,
},
'run_entropy_library_test': {
'desc': _("Run Entropy library test"),
'params': [],
'call': self.run_entropy_library_test,
'private': False,
},
'run_entropy_treeupdates': {
'desc': _("Run Entropy tree updates"),
'params': [
('repoid',basestring,_('Repository Identifier'),True,),
],
'call': self.run_entropy_treeupdates,
'private': False,
},
'scan_entropy_mirror_updates': {
'desc': _("Scan for Mirror updates and retrieve a list of action that should be run"),
'params': [
('repositories',list,_('list of repository identifiers'),True,),
],
'call': self.scan_entropy_mirror_updates,
'private': False,
},
'run_entropy_mirror_updates': {
'desc': _("Run Mirror updates for the provided repositories and its data"),
'params': [
('repository_data',dict,_('composed repository data'),True,),
],
'call': self.run_entropy_mirror_updates,
'private': False,
},
'run_entropy_checksum_test': {
'desc': _("Run Entropy packages digest verification test"),
'params': [
('repoid',basestring,_('Repository Identifier'),True,),
('mode',basestring,_('Check mode'),False,),
],
'call': self.run_entropy_mirror_updates,
'private': False,
},
'get_notice_board': {
'desc': _("Get repository notice board"),
'params': [('repoid',basestring,_('Repository Identifier'),True,),],
'call': self.get_notice_board,
'private': False,
},
'remove_notice_board_entries': {
'desc': _("Remove notice board entry"),
'params': [
('repoid',basestring,_('Repository Identifier'),True,),
('entry_ids',list,_('Entry Identifiers'),True,),
],
'call': self.remove_notice_board_entries,
'private': False,
},
'add_notice_board_entry': {
'desc': _("Add notice board entry"),
'params': [
('repoid',basestring,_('Repository Identifier'),True,),
('title',basestring,_('Title'),True,),
('notice_text',basestring,_('Text'),True,),
('link',basestring,_('Notice link'),True,),
],
'call': self.add_notice_board_entry,
'private': False,
},
})
def sync_spm(self):
return self.Manager.do_cmd(True, "sync_spm", [], {})
def compile_atoms(self, atoms, pretend = False, oneshot = False, verbose = True, nocolor = True, fetchonly = False, buildonly = False, nodeps = False, custom_use = '', ldflags = '', cflags = ''):
return self.Manager.do_cmd(
True,
"compile_atoms",
[atoms],
{
'pretend': pretend,
'oneshot': oneshot,
'verbose': verbose,
'nocolor': nocolor,
'fetchonly': fetchonly,
'buildonly': buildonly,
'nodeps': nodeps,
'custom_use': custom_use,
'ldflags': ldflags,
'cflags': cflags,
}
)
def spm_remove_atoms(self, atoms, pretend = True, verbose = True, nocolor = True):
return self.Manager.do_cmd(
True,
"spm_remove_atoms",
[atoms],
{
'pretend': pretend,
'verbose': verbose,
'nocolor': nocolor,
}
)
def get_spm_categories_updates(self, categories):
return self.Manager.do_cmd(True, "get_spm_categories_updates", [categories], {})
def get_spm_categories_installed(self, categories):
return self.Manager.do_cmd(True, "get_spm_categories_installed", [categories], {})
def enable_uses_for_atoms(self, atoms, useflags):
return self.Manager.do_cmd(True, "enable_uses_for_atoms", [atoms,useflags], {})
def disable_uses_for_atoms(self, atoms, useflags):
return self.Manager.do_cmd(True, "disable_uses_for_atoms", [atoms,useflags], {})
def get_spm_atoms_info(self, atoms):
return self.Manager.do_cmd(True, "get_spm_atoms_info", [atoms], {})
def run_spm_info(self):
return self.Manager.do_cmd(True, "run_spm_info", [], {})
def run_custom_shell_command(self, command):
return self.Manager.do_cmd(True, "run_custom_shell_command", [command], {})
def get_spm_glsa_data(self, list_type = "affected"):
return self.Manager.do_cmd(True, "get_spm_glsa_data", [list_type], {})
def get_available_repositories(self):
return self.Manager.do_cmd(True, "get_available_repositories", [], {})
def set_default_repository(self, repoid):
return self.Manager.do_cmd(True, "set_default_repository", [repoid], {})
def get_available_entropy_packages(self, repoid):
return self.Manager.do_cmd(True, "get_available_entropy_packages", [repoid], {})
def get_entropy_idpackage_information(self, idpackage, repoid):
return self.Manager.do_cmd(True, "get_entropy_idpackage_information", [idpackage,repoid], {})
def remove_entropy_packages(self, matched_atoms):
return self.Manager.do_cmd(True, "remove_entropy_packages", [matched_atoms], {})
def search_entropy_packages(self, search_type, search_string, repoid):
return self.Manager.do_cmd(True, "search_entropy_packages", [search_type,search_string,repoid], {})
def move_entropy_packages_to_repository(self, idpackages, from_repo, to_repo, do_copy = False):
return self.Manager.do_cmd(True, "move_entropy_packages_to_repository", [idpackages,from_repo,to_repo, do_copy], {})
def scan_entropy_packages_database_changes(self):
return self.Manager.do_cmd(True, "scan_entropy_packages_database_changes", [], {})
def run_entropy_database_updates(self, to_add, to_remove, to_inject):
return self.Manager.do_cmd(True, "run_entropy_database_updates", [to_add,to_remove,to_inject], {})
def run_entropy_dependency_test(self):
return self.Manager.do_cmd(True, "run_entropy_dependency_test", [], {})
def run_entropy_library_test(self):
return self.Manager.do_cmd(True, "run_entropy_library_test", [], {})
def run_entropy_treeupdates(self, repoid):
return self.Manager.do_cmd(True, "run_entropy_treeupdates", [repoid], {})
def scan_entropy_mirror_updates(self, repositories):
return self.Manager.do_cmd(True, "scan_entropy_mirror_updates", [repositories], {})
def run_entropy_mirror_updates(self, repository_data):
return self.Manager.do_cmd(True, "run_entropy_mirror_updates", [repository_data], {})
def run_entropy_checksum_test(self, repoid, mode = "local"):
return self.Manager.do_cmd(True, "run_entropy_checksum_test", [repoid, mode], {})
def get_notice_board(self, repoid):
return self.Manager.do_cmd(True, "get_notice_board", [repoid], {})
def remove_notice_board_entries(self, repoid, entry_ids):
return self.Manager.do_cmd(True, "remove_notice_board_entries", [repoid,entry_ids], {})
def add_notice_board_entry(self, repoid, title, notice_text, link):
return self.Manager.do_cmd(True, "add_notice_board_entry", [repoid,title,notice_text,link], {})
class SystemManagerClientInterface:
ssl_connection = True
def __init__(self, EntropyInstance, MethodsInterface = None, ClientCommandsInterface = None, quiet = True, show_progress = False, do_cache_connection = False):
# FIXME: if you enable cache connection, you should also consider to clear the socket buffer
if not isinstance(EntropyInstance, (EquoInterface, ServerInterface)) and \
not issubclass(EntropyInstance, (EquoInterface, ServerInterface)):
mytxt = _("A valid EquoInterface/ServerInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (EntropyInstance,mytxt,))
if ClientCommandsInterface != None:
if not issubclass(ClientCommandsInterface, SystemManagerClientCommands):
mytxt = _("A valid SystemManagerClientCommands class/subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.ClientCommandsInterface = ClientCommandsInterface
else:
self.ClientCommandsInterface = SystemManagerClientCommands
if MethodsInterface != None:
if not issubclass(MethodsInterface, SystemManagerMethodsInterface):
mytxt = _("A valid SystemManagerMethodsInterface class/subclass is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.MethodsInterface = MethodsInterface
else:
self.MethodsInterface = SystemManagerMethodsInterface
import socket, struct, entropyTools
self.socket, self.struct, self.entropyTools = socket, struct, entropyTools
from datetime import datetime
self.datetime = datetime
self.Entropy = EntropyInstance
self.hostname = None
self.hostport = None
self.username = None
self.password = None
self.quiet = quiet
self.do_cache_connection = do_cache_connection
self.show_progress = show_progress
self.ClientCommandsInterface = ClientCommandsInterface
self.Methods = self.MethodsInterface(self)
self.connection_cache = {}
self.CacheLock = thread.allocate_lock()
self.shutdown = False
self.connection_killer = None
if self.do_cache_connection:
self.connection_killer = self.entropyTools.TimeScheduled(self.connection_killer_handler, 2)
self.connection_killer.start()
def __del__(self):
if hasattr(self,'shutdown'):
self.shutdown = True
if hasattr(self,'connection_killer'):
if self.connection_killer != None:
self.connection_killer.kill()
def _validate_credentials(self):
if not isinstance(self.hostname,basestring):
raise exceptionTools.IncorrectParameter("IncorrectParameter: hostname: %s. %s" % (_('not a string'),_('Please use setup_connection() properly'),))
if not isinstance(self.username,basestring):
raise exceptionTools.IncorrectParameter("IncorrectParameter: username: %s. %s" % (_('not a string'),_('Please use setup_connection() properly'),))
if not isinstance(self.password,basestring):
raise exceptionTools.IncorrectParameter("IncorrectParameter: password: %s. %s" % (_('not a string'),_('Please use setup_connection() properly'),))
if not isinstance(self.hostport,int):
raise exceptionTools.IncorrectParameter("IncorrectParameter: port: %s. %s" % (_('not an int'),_('Please use setup_connection() properly'),))
if not isinstance(self.ssl_connection,bool):
raise exceptionTools.IncorrectParameter("IncorrectParameter: ssl_connection: %s. %s" % (_('not a bool'),_('Please use setup_connection() properly'),))
def get_connection_cache_key(self):
return hash((self.hostname, self.hostport, self.username, self.password, self.ssl_connection,))
def get_connection_cache(self):
if self.do_cache_connection:
key = self.get_connection_cache_key()
srv = self.connection_cache.get(key)
# FIXME: if you enable cache connection, you should also consider to clear the socket buffer
# srv.sock_conn
# srv.real_sock_conn
return srv
def cache_connection(self, srv):
if self.do_cache_connection:
key = self.get_connection_cache_key()
self.connection_cache[key] = {
'conn': srv,
'ts': self.get_ts(),
}
def update_connection_ts(self):
if self.do_cache_connection:
key = self.get_connection_cache_key()
if key not in self.connection_cache:
return
self.connection_cache[key]['ts'] = self.get_ts()
def kill_all_connections(self):
if self.do_cache_connection:
self.CacheLock.acquire()
try:
keys = self.connection_cache.keys()
for key in keys:
data = self.connection_cache.pop(key)
data['conn'].disconnect()
finally:
self.CacheLock.release()
def connection_killer_handler(self):
if not self.do_cache_connection: return
if self.shutdown: return
if not self.connection_cache: return
keys = self.connection_cache.keys()
for key in keys:
curr_ts = self.get_ts()
ts = self.connection_cache[key]['ts']
delta = curr_ts - ts
if delta.seconds < 60:
continue
self.CacheLock.acquire()
try:
data = self.connection_cache.pop(key)
finally:
self.CacheLock.release()
srv = data['conn']
srv.disconnect()
def get_ts(self):
return self.datetime.fromtimestamp(time.time())
def setup_connection(self, hostname, port, username, password, ssl):
self.hostname = hostname
self.hostport = port
self.username = username
self.password = password
self.ssl_connection = ssl
self._validate_credentials()
def connect_to_service(self, timeout = None):
self._validate_credentials()
args = [self.Entropy, self.ClientCommandsInterface]
kwargs = {
'ssl': self.ssl_connection,
'quiet': self.quiet,
'show_progress': self.show_progress
}
if timeout != None: kwargs['socket_timeout'] = timeout
srv = SystemSocketClientInterface(*args,**kwargs)
srv.connect(self.hostname, self.hostport)
return srv
def get_service_connection(self, timeout = None):
try:
srv = self.connect_to_service(timeout = timeout)
except (exceptionTools.ConnectionError,self.socket.error,self.struct.error,):
return None
return srv
def logout(self, srv, session_id):
self._validate_credentials()
return srv.CmdInterface.service_logout(self.username, session_id)
def login(self, srv, session_id):
self._validate_credentials()
return srv.CmdInterface.service_login(self.username, self.password, session_id)
# eval(func) must have session as first param
def do_cmd(self, login_required, func, args, kwargs):
self.CacheLock.acquire()
try:
srv = self.get_connection_cache()
if srv == None:
srv = self.get_service_connection(timeout = 10)
if srv != None: self.cache_connection(srv)
else:
srv = srv['conn']
if srv == None:
return False, 'no connection'
session = srv.open_session()
if session == None:
return False, 'no session'
self.update_connection_ts()
args.insert(0,session)
if login_required:
logged, error = self.login(srv, session)
if not logged:
srv.close_session(session)
if not self.do_cache_connection:
srv.disconnect()
return False, error
rslt = eval("srv.CmdInterface.%s" % (func,))(*args,**kwargs)
if login_required:
self.logout(srv, session)
srv.close_session(session)
if not self.do_cache_connection:
srv.disconnect()
return rslt
finally:
self.CacheLock.release()
def get_available_client_commands(self):
return self.Methods.available_commands.copy()
class UGCClientAuthStore:
access_file = etpConst['ugc_accessfile']
def __init__(self):
from xml.dom import minidom
from xml.parsers import expat
self.expat = expat
self.minidom = minidom
self.setup_store_paths()
try:
self.setup_permissions()
except IOError:
pass
self.store = {}
try:
self.xmldoc = self.minidom.parse(self.access_file)
except (self.expat.ExpatError,IOError,):
self.xmldoc = None
if self.xmldoc != None:
try:
self.parse_document()
except self.expat.ExpatError:
self.xmldoc = None
self.store = {}
def setup_store_paths(self):
myhome = os.getenv("HOME")
if myhome != None:
if os.path.isdir(myhome) and os.access(myhome,os.W_OK):
self.access_file = os.path.join(myhome,".config/entropy",os.path.basename(self.access_file))
self.access_dir = os.path.dirname(self.access_file)
def setup_permissions(self):
if not os.path.isdir(self.access_dir):
os.makedirs(self.access_dir)
if not os.path.isfile(self.access_file):
f = open(self.access_file,"w")
f.close()
if etpConst['entropygid'] != None:
try:
const_setup_file(self.access_dir, etpConst['entropygid'], 0600)
except OSError:
pass
def parse_document(self):
self.store.clear()
store = self.xmldoc.getElementsByTagName("store")[0]
repositories = store.getElementsByTagName("repository")
for repository in repositories:
repoid = repository.getAttribute("id")
if not repoid: continue
username = repository.getElementsByTagName("username")[0].firstChild.data.strip()
password = repository.getElementsByTagName("password")[0].firstChild.data.strip()
self.store[repoid] = {'username': username, 'password': password}
def store_login(self, username, password, repository, save = True):
self.store[repository] = {'username': username, 'password': password}
if save:
self.save_store()
def save_store(self):
self.xmldoc = self.minidom.Document()
store = self.xmldoc.createElement("store")
for repository in self.store:
repo = self.xmldoc.createElement("repository")
repo.setAttribute('id',repository)
# username
username = self.xmldoc.createElement("username")
username_value = self.xmldoc.createTextNode(self.store[repository]['username'])
username.appendChild(username_value)
repo.appendChild(username)
# password
password = self.xmldoc.createElement("password")
password_value = self.xmldoc.createTextNode(self.store[repository]['password'])
password.appendChild(password_value)
repo.appendChild(password)
store.appendChild(repo)
self.xmldoc.appendChild(store)
f = open(self.access_file,"w")
f.writelines(self.xmldoc.toprettyxml(indent=" "))
f.flush()
f.close()
self.setup_permissions()
self.parse_document()
def remove_login(self, repository, save = True):
if repository in self.store:
del self.store[repository]
if save:
self.save_store()
def read_login(self, repository):
if repository in self.store:
return self.store[repository]['username'],self.store[repository]['password']
class UGCCacheInterface:
def __init__(self, UGCClientInstance):
if not isinstance(UGCClientInstance,UGCClientInterface):
mytxt = _("A valid UGCClientInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
import dumpTools
self.CacheLock = thread.allocate_lock()
self.dumpTools = dumpTools
self.Service = UGCClientInstance
self.xcache = {}
def acquire_lock(self):
self.CacheLock.acquire()
def release_lock(self):
self.CacheLock.release()
def _get_live_cache_item(self, repository, item):
if repository not in self.xcache:
return None
return self.xcache[repository].get(item)
def _set_live_cache_item(self, repository, item, obj):
if repository not in self.xcache:
self.xcache[repository] = {}
if type(obj) in (list,tuple,):
my_obj = obj[:]
elif type(obj) in (set,frozenset,dict,):
my_obj = obj.copy()
else:
my_obj = obj
self.xcache[repository][item] = my_obj
def _clear_live_cache_item(self, repository, item):
if not self.xcache.has_key(repository):
return
if not self.xcache[repository].has_key(item):
return
del self.xcache[repository][item]
def _get_store_cache_file(self, iddoc, repository, doc_url):
return "%s/%s/iddoc_%s/%s" % (etpCache['ugc_docs'], repository, iddoc, doc_url,)
def _get_vote_cache_file(self, repository):
return self._get_vote_cache_dir(repository)
def _get_downloads_cache_file(self, repository):
return self._get_downloads_cache_dir(repository)
def _get_alldocs_cache_file(self, pkgkey, repository):
return self._get_alldocs_cache_dir(repository)+"/"+pkgkey
def _get_alldocs_cache_dir(self, repository):
return etpCache['ugc_docs']+"/"+repository
def _get_downloads_cache_dir(self, repository):
return etpCache['ugc_downloads']+"/"+repository
def _get_vote_cache_dir(self, repository):
return etpCache['ugc_votes']+"/"+repository
def _get_vote_cache_key(self, repository):
return 'get_vote_cache_'+repository
def _get_downloads_cache_key(self, repository):
return 'get_downloads_cache_'+repository
def _get_alldocs_cache_key(self, repository):
return 'get_package_alldocs_cache_'+repository
def store_document(self, iddoc, repository, doc_url):
cache_file = os.path.join(etpConst['dumpstoragedir'],self._get_store_cache_file(iddoc, repository, doc_url))
cache_dir = os.path.dirname(cache_file)
try:
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir,0775)
if etpConst['entropygid'] != None:
const_setup_perms(cache_dir,etpConst['entropygid'])
except OSError:
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (_("Cannot setup cache directory"),cache_dir,))
if not os.access(cache_dir,os.W_OK):
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (_("Cannot write to cache directory"),cache_dir,))
if os.path.isfile(cache_file) or os.path.islink(cache_file):
try:
os.remove(cache_file)
except OSError:
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (_("Cannot remove cache file"),cache_file,))
fetcher = self.Service.Entropy.urlFetcher(doc_url, cache_file, resume = False)
rc = fetcher.download()
if rc in ("-1","-2","-3"): return None
if not os.path.isfile(cache_file): return None
try:
os.chmod(cache_file,0664)
if etpConst['entropygid'] != None:
os.chown(cache_file,-1,etpConst['entropygid'])
except OSError:
raise exceptionTools.PermissionDenied("PermissionDenied: %s %s" % (_("Cannot write to cache file"),cache_file,))
del fetcher
return cache_file
def get_stored_document(self, iddoc, repository, doc_url):
cache_file = os.path.join(etpConst['dumpstoragedir'],self._get_store_cache_file(iddoc, repository, doc_url))
if os.path.isfile(cache_file) and os.access(cache_file,os.R_OK):
return cache_file
def update_vote_cache(self, repository, vote_dict):
cached = self.get_vote_cache(repository)
if cached == None:
cached = vote_dict.copy()
else:
cached.update(vote_dict)
self.save_vote_cache(repository,cached)
def update_downloads_cache(self, repository, down_dict):
cached = self.get_downloads_cache(repository)
if cached == None:
cached = down_dict.copy()
else:
cached.update(down_dict)
self.save_downloads_cache(repository,cached)
def update_alldocs_cache(self, pkgkey, repository, alldocs_dict):
self.save_alldocs_cache(pkgkey, repository, alldocs_dict)
def clear_alldocs_cache(self, repository):
self.acquire_lock()
try:
self.Service.Entropy.clear_dump_cache(self._get_alldocs_cache_dir(repository))
self._clear_live_cache_item(repository, self._get_alldocs_cache_key(repository))
finally:
self.release_lock()
def clear_downloads_cache(self, repository):
self.acquire_lock()
try:
self.Service.Entropy.clear_dump_cache(self._get_alldocs_cache_dir(repository))
self._clear_live_cache_item(repository, self._get_downloads_cache_key(repository))
finally:
self.release_lock()
def clear_vote_cache(self, repository):
self.acquire_lock()
try:
self.Service.Entropy.clear_dump_cache(self._get_vote_cache_dir(repository))
self._clear_live_cache_item(repository, self._get_vote_cache_key(repository))
finally:
self.release_lock()
def clear_cache(self, repository):
self.clear_alldocs_cache(repository)
self.clear_downloads_cache(repository)
self.clear_vote_cache(repository)
self.xcache.clear()
def get_vote_cache(self, repository):
cache_key = self._get_vote_cache_key(repository)
cached = self._get_live_cache_item(repository, cache_key)
if cached != None:
return cached
self.acquire_lock()
cache_file = self._get_vote_cache_file(repository)
try:
data = self.dumpTools.loadobj(cache_file)
if data != None:
self._set_live_cache_item(repository, cache_key, data)
except (IOError,EOFError,OSError):
data = None
finally:
self.release_lock()
return data
def get_downloads_cache(self, repository):
cache_key = self._get_downloads_cache_key(repository)
cached = self._get_live_cache_item(repository, cache_key)
if cached != None:
return cached
self.acquire_lock()
cache_file = self._get_downloads_cache_file(repository)
try:
data = self.dumpTools.loadobj(cache_file)
if data != None:
self._set_live_cache_item(repository, cache_key, data)
except (IOError,EOFError,OSError):
data = None
finally:
self.release_lock()
return data
def get_alldocs_cache(self, pkgkey, repository):
cache_key = self._get_alldocs_cache_key(repository)
cached = self._get_live_cache_item(repository, cache_key)
if isinstance(cached,dict):
if cached.has_key(pkgkey): return cached[pkgkey]
else:
cached = {}
self.acquire_lock()
cache_file = self._get_alldocs_cache_file(pkgkey, repository)
try:
data = self.dumpTools.loadobj(cache_file)
if data != None:
cached[pkgkey] = data
self._set_live_cache_item(repository, cache_key, cached)
except (IOError,EOFError,OSError):
data = None
finally:
self.release_lock()
return data
def save_vote_cache(self, repository, vote_dict):
self.acquire_lock()
try:
self._clear_live_cache_item(repository, self._get_vote_cache_key(repository))
self.dumpTools.dumpobj(self._get_vote_cache_file(repository), vote_dict)
finally:
self.release_lock()
def save_downloads_cache(self, repository, down_dict):
self.acquire_lock()
try:
self._clear_live_cache_item(repository, self._get_downloads_cache_key(repository))
self.dumpTools.dumpobj(self._get_downloads_cache_file(repository), down_dict)
finally:
self.release_lock()
def save_alldocs_cache(self, pkgkey, repository, alldocs_dict):
self.acquire_lock()
try:
self._clear_live_cache_item(repository, self._get_alldocs_cache_key(repository))
self.dumpTools.dumpobj(self._get_alldocs_cache_file(pkgkey, repository), alldocs_dict)
finally:
self.release_lock()
def get_package_vote(self, repository, pkgkey):
cache = self.get_vote_cache(repository)
if not cache:
return 0.0
elif not isinstance(cache,dict):
return 0.0
elif not cache.has_key(pkgkey):
return 0.0
return cache[pkgkey]
def get_package_downloads(self, repository, pkgkey):
cache = self.get_downloads_cache(repository)
if not cache:
return 0
elif not isinstance(cache,dict):
return 0
elif not cache.has_key(pkgkey):
return 0
try:
return int(cache[pkgkey])
except ValueError:
return 0
class UGCClientInterface:
ssl_connection = True
def __init__(self, EquoInstance, quiet = True, show_progress = False):
if not isinstance(EquoInstance,EquoInterface):
mytxt = _("A valid EquoInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
import socket, threading
self.socket, self.threading = socket, threading
import struct
self.struct = struct
self.Entropy = EquoInstance
self.store = UGCClientAuthStore()
self.quiet = quiet
self.show_progress = show_progress
self.UGCCache = UGCCacheInterface(self)
self.TxLocks = {}
def connect_to_service(self, repository, timeout = None):
if repository not in etpRepositories:
raise exceptionTools.RepositoryError("RepositoryError: %s" % (_('repository is not available'),))
try:
url = etpRepositories[repository]['plain_database'].split("/")[2]
port = etpRepositories[repository]['service_port']
if self.ssl_connection: port = etpRepositories[repository]['ssl_service_port']
except (IndexError,KeyError,):
raise exceptionTools.RepositoryError("RepositoryError: %s" % (_('repository metadata is malformed'),))
args = [self.Entropy, RepositorySocketClientCommands]
kwargs = {
'ssl': self.ssl_connection,
'quiet': self.quiet,
'show_progress': self.show_progress
}
if timeout != None: kwargs['socket_timeout'] = timeout
srv = SystemSocketClientInterface(*args,**kwargs)
srv.connect(url, port)
return srv
def get_service_connection(self, repository, check = True, timeout = None):
if check:
if not self.is_repository_eapi3_aware(repository):
return None
try:
srv = self.connect_to_service(repository, timeout = timeout)
except (exceptionTools.RepositoryError,exceptionTools.ConnectionError,):
return None
except (self.socket.error,self.struct.error,):
return None
return srv
def is_repository_eapi3_aware(self, repository):
aware = self.UGCCache._get_live_cache_item(repository, 'is_repository_eapi3_aware')
if aware != None:
return aware
srv = self.get_service_connection(repository, check = False, timeout = 3)
if srv == None:
aware = False
else:
session = srv.open_session()
if session != None:
srv.close_session(session)
srv.disconnect()
aware = True
else:
aware = False
self.UGCCache._set_live_cache_item(repository, 'is_repository_eapi3_aware', aware)
return aware
def read_login(self, repository):
return self.store.read_login(repository)
def remove_login(self, repository):
return self.store.remove_login(repository)
def do_login(self, repository, force = False):
login_data = self.read_login(repository)
if (login_data != None) and not force:
return True,_('ok')
aware = self.is_repository_eapi3_aware(repository)
if not aware:
return False,_('repository does not support EAPI3')
def fake_callback(*args,**kwargs):
return True
attempts = 3
while attempts:
# use input box to read login
input_params = [
('username',_('Username'),fake_callback,False),
('password',_('Password'),fake_callback,True)
]
login_data = self.Entropy.inputBox(
"%s %s %s" % (_('Please login against'),repository,_('repository'),),
input_params,
cancel_button = True
)
if not login_data:
return False,_('login abort')
# now verify
srv = self.get_service_connection(repository)
if srv == None:
return False,_('connection issues')
session = srv.open_session()
login_status, login_msg = srv.CmdInterface.service_login(login_data['username'], login_data['password'], session)
if not login_status:
srv.close_session(session)
srv.disconnect()
self.Entropy.askQuestion("%s: %s" % (_("Access denied. Login failed"),login_msg,), responses = ["Ok"])
attempts -= 1
continue
# login accepted, store it?
srv.close_session(session)
srv.disconnect()
rc = self.Entropy.askQuestion(_("Login successful. Do you want to save these credentials ?"))
save = False
if rc == "Yes": save = True
self.store.store_login(login_data['username'], login_data['password'], repository, save = save)
return True,_('ok')
def login(self, repository, force = False):
if not self.TxLocks.has_key(repository):
self.TxLocks[repository] = self.threading.Lock()
with self.TxLocks[repository]:
return self.do_login(repository, force = force)
def logout(self, repository):
return self.store.remove_login(repository)
# eval(func) must have session as first param
def do_cmd(self, repository, login_required, func, args, kwargs):
if not self.TxLocks.has_key(repository):
self.TxLocks[repository] = self.threading.Lock()
with self.TxLocks[repository]:
if login_required:
status, err_msg = self.do_login(repository)
if not status:
return False,err_msg
srv = self.get_service_connection(repository)
if srv == None:
return False,'no connection'
session = srv.open_session()
if session == None:
return False,'no session'
args.insert(0,session)
if login_required:
stored_pass = False
while 1:
# login
login_data = self.read_login(repository)
if login_data == None:
status, msg = self.login(repository)
if not status: return status, msg
username, password = self.read_login(repository)
else:
stored_pass = True
username, password = login_data
logged, error = srv.CmdInterface.service_login(username, password, session)
if not logged:
if stored_pass:
stored_pass = False
self.remove_login(repository)
continue
srv.close_session(session)
srv.disconnect()
return logged, error
break
rslt = eval("srv.CmdInterface.%s" % (func,))(*args,**kwargs)
srv.close_session(session)
srv.disconnect()
return rslt
def get_comments(self, repository, pkgkey):
return self.do_cmd(repository, False, "ugc_get_textdocs", [pkgkey], {})
def get_comments_by_identifiers(self, repository, identifiers):
return self.do_cmd(repository, False, "ugc_get_textdocs_by_identifiers", [identifiers], {})
def get_documents_by_identifiers(self, repository, identifiers):
return self.do_cmd(repository, False, "ugc_get_documents_by_identifiers", [identifiers], {})
def add_comment(self, repository, pkgkey, comment, title, keywords):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_add_comment", [pkgkey, comment, title, keywords], {})
def edit_comment(self, repository, iddoc, new_comment, new_title, new_keywords):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_edit_comment", [iddoc, new_comment, new_title, new_keywords], {})
def remove_comment(self, repository, iddoc):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_remove_comment", [iddoc], {})
def add_vote(self, repository, pkgkey, vote):
data = self.do_cmd(repository, True, "ugc_do_vote", [pkgkey, vote], {})
if isinstance(data,tuple): voted, add_err_msg = data
else: return False,'wrong server answer'
if voted: self.get_vote(repository, pkgkey)
return voted, add_err_msg
def get_vote(self, repository, pkgkey):
vote, err_msg = self.do_cmd(repository, False, "ugc_get_vote", [pkgkey], {})
if isinstance(vote,float):
mydict = {pkgkey: vote}
self.UGCCache.update_vote_cache(repository, mydict)
return vote, err_msg
def get_all_votes(self, repository):
votes_dict, err_msg = self.do_cmd(repository, False, "ugc_get_allvotes", [], {})
if isinstance(votes_dict,dict):
self.UGCCache.update_vote_cache(repository, votes_dict)
return votes_dict, err_msg
def add_download(self, repository, pkgkey):
return self.do_cmd(repository, False, "ugc_do_download", [pkgkey], {})
def add_downloads(self, repository, pkgkeys):
return self.do_cmd(repository, False, "ugc_do_downloads", [pkgkeys], {})
def get_downloads(self, repository, pkgkey):
data = self.do_cmd(repository, False, "ugc_get_downloads", [pkgkey], {})
if isinstance(data,tuple): downloads, err_msg = data
else: return False,'wrong server answer'
if downloads:
mydict = {pkgkey: downloads}
self.UGCCache.update_downloads_cache(repository, mydict)
return downloads, err_msg
def get_all_downloads(self, repository):
down_dict, err_msg = self.do_cmd(repository, False, "ugc_get_alldownloads", [], {})
if isinstance(down_dict,dict):
self.UGCCache.update_downloads_cache(repository, down_dict)
return down_dict, err_msg
def send_file(self, repository, pkgkey, file_path, title, description, keywords):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_send_file", [pkgkey, file_path, etpConst['ugc_doctypes']['generic_file'], title, description, keywords], {})
def remove_file(self, repository, iddoc):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_remove_file", [iddoc], {})
def send_image(self, repository, pkgkey, image_path, title, description, keywords):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_send_file", [pkgkey, image_path, etpConst['ugc_doctypes']['image'], title, description, keywords], {})
def remove_image(self, repository, iddoc):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_remove_image", [iddoc], {})
def send_youtube_video(self, repository, pkgkey, video_path, title, description, keywords):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_send_file", [pkgkey, video_path, etpConst['ugc_doctypes']['youtube_video'], title, description, keywords], {})
def remove_youtube_video(self, repository, iddoc):
self.UGCCache.clear_alldocs_cache(repository)
return self.do_cmd(repository, True, "ugc_remove_youtube_video", [iddoc], {})
def get_docs(self, repository, pkgkey):
data = self.do_cmd(repository, False, "ugc_get_docs", [pkgkey], {})
if isinstance(data,tuple): docs_data, err_msg = data
else: return False,'wrong server answer'
if err_msg == 'ok':
self.UGCCache.update_alldocs_cache(pkgkey, repository, docs_data)
return docs_data, err_msg
def send_document_autosense(self, repository, pkgkey, ugc_type, data, title, description, keywords):
if ugc_type == etpConst['ugc_doctypes']['generic_file']:
return self.send_file(repository, pkgkey, data, title, description, keywords)
elif ugc_type == etpConst['ugc_doctypes']['image']:
return self.send_image(repository, pkgkey, data, title, description, keywords)
elif ugc_type == etpConst['ugc_doctypes']['youtube_video']:
return self.send_youtube_video(repository, pkgkey, data, title, description, keywords)
elif ugc_type == etpConst['ugc_doctypes']['comments']:
return self.add_comment(repository, pkgkey, description, title, keywords)
return None,'type not supported locally'
def remove_document_autosense(self, repository, iddoc, ugc_type):
if ugc_type == etpConst['ugc_doctypes']['generic_file']:
return self.remove_file(repository, iddoc)
elif ugc_type == etpConst['ugc_doctypes']['image']:
return self.remove_image(repository, iddoc)
elif ugc_type == etpConst['ugc_doctypes']['youtube_video']:
return self.remove_youtube_video(repository, iddoc)
elif ugc_type == etpConst['ugc_doctypes']['comments']:
return self.remove_comment(repository, iddoc)
return None,'type not supported locally'
class ServerMirrorsInterface:
import entropyTools, dumpTools, socket
def __init__(self, ServerInstance, repo = None):
if not isinstance(ServerInstance,ServerInterface):
mytxt = _("A valid ServerInterface based instance is needed")
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = ServerInstance
self.FtpInterface = self.Entropy.FtpInterface
self.rssFeed = self.Entropy.rssFeed
mytxt = blue("%s:") % (_("Entropy Server Mirrors Interface loaded"),)
self.Entropy.updateProgress(
mytxt,
importance = 2,
type = "info",
header = red(" @@ ")
)
mytxt = _("mirror")
for mirror in self.Entropy.get_remote_mirrors(repo):
mirror = self.entropyTools.hideFTPpassword(mirror)
self.Entropy.updateProgress(
blue("%s: %s") % (mytxt,darkgreen(mirror),),
importance = 0,
type = "info",
header = brown(" # ")
)
def lock_mirrors(self, lock = True, mirrors = [], repo = None):
if repo == None:
repo = self.Entropy.default_repository
if not mirrors:
mirrors = self.Entropy.get_remote_mirrors(repo)
issues = False
for uri in mirrors:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
lock_text = _("unlocking")
if lock: lock_text = _("locking")
self.Entropy.updateProgress(
"[repo:%s|%s] %s %s" % (
brown(repo),
darkgreen(crippled_uri),
bold(lock_text),
blue("%s...") % (_("mirror"),),
),
importance = 1,
type = "info",
header = brown(" * "),
back = True
)
ftp = self.FtpInterface(uri, self.Entropy)
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
ftp.setCWD(my_path, dodir = True)
if lock and ftp.isFileAvailable(etpConst['etpdatabaselockfile']):
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
blue(_("mirror already locked")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
ftp.closeConnection()
continue
elif not lock and not ftp.isFileAvailable(etpConst['etpdatabaselockfile']):
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
blue(_("mirror already unlocked")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
ftp.closeConnection()
continue
if lock:
rc = self.do_mirror_lock(uri, ftp, repo = repo)
else:
rc = self.do_mirror_unlock(uri, ftp, repo = repo)
ftp.closeConnection()
if not rc: issues = True
if not issues:
database_taint_file = self.Entropy.get_local_database_taint_file(repo)
if os.path.isfile(database_taint_file):
os.remove(database_taint_file)
return issues
# this functions makes entropy clients to not download anything from the chosen
# mirrors. it is used to avoid clients to download databases while we're uploading
# a new one.
def lock_mirrors_for_download(self, lock = True, mirrors = [], repo = None):
if repo == None:
repo = self.Entropy.default_repository
if not mirrors:
mirrors = self.Entropy.get_remote_mirrors(repo)
issues = False
for uri in mirrors:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
lock_text = _("unlocking")
if lock: lock_text = _("locking")
self.Entropy.updateProgress(
"[repo:%s|%s] %s %s..." % (
blue(repo),
red(crippled_uri),
bold(lock_text),
blue(_("mirror for download")),
),
importance = 1,
type = "info",
header = red(" @@ "),
back = True
)
ftp = self.FtpInterface(uri, self.Entropy)
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
ftp.setCWD(my_path, dodir = True)
if lock and ftp.isFileAvailable(etpConst['etpdatabasedownloadlockfile']):
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
blue(repo),
red(crippled_uri),
blue(_("mirror already locked for download")),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
ftp.closeConnection()
continue
elif not lock and not ftp.isFileAvailable(etpConst['etpdatabasedownloadlockfile']):
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
blue(repo),
red(crippled_uri),
blue(_("mirror already unlocked for download")),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
ftp.closeConnection()
continue
if lock:
rc = self.do_mirror_lock(uri, ftp, dblock = False, repo = repo)
else:
rc = self.do_mirror_unlock(uri, ftp, dblock = False, repo = repo)
ftp.closeConnection()
if not rc: issues = True
return issues
def do_mirror_lock(self, uri, ftp_connection = None, dblock = True, repo = None):
if repo == None:
repo = self.Entropy.default_repository
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
if not ftp_connection:
ftp_connection = self.FtpInterface(uri, self.Entropy)
ftp_connection.setCWD(my_path, dodir = True)
else:
mycwd = ftp_connection.getCWD()
if mycwd != my_path:
ftp_connection.setBasedir()
ftp_connection.setCWD(my_path, dodir = True)
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
lock_string = ''
if dblock:
self.create_local_database_lockfile(repo)
lock_file = self.get_database_lockfile(repo)
else:
lock_string = _('for download') # locking/unlocking mirror1 for download
self.create_local_database_download_lockfile(repo)
lock_file = self.get_database_download_lockfile(repo)
rc = ftp_connection.uploadFile(lock_file, ascii = True)
if rc:
self.Entropy.updateProgress(
"[repo:%s|%s] %s %s" % (
blue(repo),
red(crippled_uri),
blue(_("mirror successfully locked")),
blue(lock_string),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
else:
self.Entropy.updateProgress(
"[repo:%s|%s] %s: %s - %s %s" % (
blue(repo),
red(crippled_uri),
blue("lock error"),
rc,
blue(_("mirror not locked")),
blue(lock_string),
),
importance = 1,
type = "error",
header = darkred(" * ")
)
self.remove_local_database_lockfile(repo)
return rc
def do_mirror_unlock(self, uri, ftp_connection, dblock = True, repo = None):
if repo == None:
repo = self.Entropy.default_repository
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
if not ftp_connection:
ftp_connection = self.FtpInterface(uri, self.Entropy)
ftp_connection.setCWD(my_path)
else:
mycwd = ftp_connection.getCWD()
if mycwd != my_path:
ftp_connection.setBasedir()
ftp_connection.setCWD(my_path)
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
if dblock:
dbfile = etpConst['etpdatabaselockfile']
else:
dbfile = etpConst['etpdatabasedownloadlockfile']
rc = ftp_connection.deleteFile(dbfile)
if rc:
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
blue(repo),
red(crippled_uri),
blue(_("mirror successfully unlocked")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
if dblock:
self.remove_local_database_lockfile(repo)
else:
self.remove_local_database_download_lockfile(repo)
else:
self.Entropy.updateProgress(
"[repo:%s|%s] %s: %s - %s" % (
blue(repo),
red(crippled_uri),
blue(_("unlock error")),
rc,
blue(_("mirror not unlocked")),
),
importance = 1,
type = "error",
header = darkred(" * ")
)
return rc
def get_database_lockfile(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
return os.path.join(self.Entropy.get_local_database_dir(repo),etpConst['etpdatabaselockfile'])
def get_database_download_lockfile(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
return os.path.join(self.Entropy.get_local_database_dir(repo),etpConst['etpdatabasedownloadlockfile'])
def create_local_database_download_lockfile(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
lock_file = self.get_database_download_lockfile(repo)
f = open(lock_file,"w")
f.write("download locked")
f.flush()
f.close()
def create_local_database_lockfile(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
lock_file = self.get_database_lockfile(repo)
f = open(lock_file,"w")
f.write("database locked")
f.flush()
f.close()
def remove_local_database_lockfile(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
lock_file = self.get_database_lockfile(repo)
if os.path.isfile(lock_file):
os.remove(lock_file)
def remove_local_database_download_lockfile(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
lock_file = self.get_database_download_lockfile(repo)
if os.path.isfile(lock_file):
os.remove(lock_file)
def download_package(self, uri, pkg_relative_path, repo = None):
if repo == None:
repo = self.Entropy.default_repository
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
tries = 0
while tries < 5:
tries += 1
pkg_to_join_path = '/'.join(pkg_relative_path.split('/')[2:])
pkg_to_join_dirpath = os.path.dirname(pkg_to_join_path)
pkgfile = os.path.basename(pkg_relative_path)
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("connecting to download package")), # connecting to download package xyz
darkgreen(pkg_to_join_path),
),
importance = 1,
type = "info",
header = darkgreen(" * "),
back = True
)
ftp = self.FtpInterface(uri, self.Entropy)
dirpath = os.path.join(self.Entropy.get_remote_packages_relative_path(repo),pkg_to_join_dirpath)
ftp.setCWD(dirpath, dodir = True)
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("downloading package")),
darkgreen(pkg_to_join_path),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
download_path = os.path.join(self.Entropy.get_local_packages_directory(repo),pkg_to_join_dirpath)
if (not os.path.isdir(download_path)) and (not os.access(download_path,os.R_OK)):
os.makedirs(download_path)
rc = ftp.downloadFile(pkgfile,download_path)
if not rc:
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("package")),
darkgreen(pkg_to_join_path),
blue(_("does not exist")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
ftp.closeConnection()
return rc
dbconn = self.Entropy.openServerDatabase(read_only = True, no_upload = True, repo = repo)
idpackage = dbconn.getIDPackageFromDownload(pkg_relative_path)
if idpackage == -1:
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("package")),
darkgreen(pkgfile),
blue(_("is not listed in the current repository database!!")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
ftp.closeConnection()
return 0
storedmd5 = dbconn.retrieveDigest(idpackage)
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("verifying checksum of package")),
darkgreen(pkgfile),
),
importance = 1,
type = "info",
header = darkgreen(" * "),
back = True
)
pkg_path = os.path.join(download_path,pkgfile)
md5check = self.entropyTools.compareMd5(pkg_path,storedmd5)
if md5check:
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("package")),
darkgreen(pkgfile),
blue(_("downloaded successfully")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
return True
else:
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("package")),
darkgreen(pkgfile),
blue(_("checksum does not match. re-downloading...")),
),
importance = 1,
type = "warning",
header = darkred(" * ")
)
if os.path.isfile(pkg_path):
os.remove(pkg_path)
continue
# if we get here it means the files hasn't been downloaded properly
self.Entropy.updateProgress(
"[repo:%s|%s|#%s] %s: %s %s" % (
brown(repo),
darkgreen(crippled_uri),
brown(tries),
blue(_("package")),
darkgreen(pkgfile),
blue(_("seems broken. Consider to re-package it. Giving up!")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
return False
def get_remote_databases_status(self, repo = None, mirrors = []):
if repo == None:
repo = self.Entropy.default_repository
if not mirrors:
mirrors = self.Entropy.get_remote_mirrors(repo)
data = []
for uri in mirrors:
ftp = self.FtpInterface(uri, self.Entropy)
try:
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
ftp.setCWD(my_path)
except ftp.ftplib.error_perm:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
self.Entropy.updateProgress(
"[repo:%s|%s] %s !" % (
brown(repo),
darkgreen(crippled_uri),
blue(_("mirror doesn't have a valid directory structure")),
),
importance = 1,
type = "warning",
header = darkred(" !!! ")
)
ftp.closeConnection()
continue
cmethod = etpConst['etpdatabasecompressclasses'].get(etpConst['etpdatabasefileformat'])
if cmethod == None:
raise exceptionTools.InvalidDataType("InvalidDataType: %s." % (
_("Wrong database compression method passed"),
)
)
compressedfile = etpConst[cmethod[2]]
revision = 0
rc1 = ftp.isFileAvailable(compressedfile)
revfilename = os.path.basename(self.Entropy.get_local_database_revision_file(repo))
rc2 = ftp.isFileAvailable(revfilename)
if rc1 and rc2:
revision_localtmppath = os.path.join(etpConst['packagestmpdir'],revfilename)
ftp.downloadFile(revfilename,etpConst['packagestmpdir'],True)
f = open(revision_localtmppath,"r")
try:
revision = int(f.readline().strip())
except ValueError:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
self.Entropy.updateProgress(
"[repo:%s|%s] %s: %s" % (
brown(repo),
darkgreen(crippled_uri),
blue(_("mirror doesn't have a valid database revision file")),
bold(revision),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
revision = 0
f.close()
if os.path.isfile(revision_localtmppath):
os.remove(revision_localtmppath)
info = [uri,revision]
data.append(info)
ftp.closeConnection()
return data
def is_local_database_locked(self, repo = None):
x = repo
if x == None:
x = self.Entropy.default_repository
lock_file = self.get_database_lockfile(x)
return os.path.isfile(lock_file)
def get_mirrors_lock(self, repo = None):
dbstatus = []
for uri in self.Entropy.get_remote_mirrors(repo):
data = [uri,False,False]
ftp = FtpInterface(uri, self.Entropy)
try:
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
ftp.setCWD(my_path)
except ftp.ftplib.error_perm:
ftp.closeConnection()
continue
if ftp.isFileAvailable(etpConst['etpdatabaselockfile']):
# upload locked
data[1] = True
if ftp.isFileAvailable(etpConst['etpdatabasedownloadlockfile']):
# download locked
data[2] = True
ftp.closeConnection()
dbstatus.append(data)
return dbstatus
def download_notice_board(self, repo = None):
if repo == None: repo = self.Entropy.default_repository
mirrors = self.Entropy.get_remote_mirrors(repo)
rss_path = self.Entropy.get_local_database_notice_board_file(repo)
mytmpdir = self.entropyTools.getRandomTempFile()
os.makedirs(mytmpdir)
self.Entropy.updateProgress(
"[repo:%s] %s %s" % (
brown(repo),
blue(_("downloading notice board from mirrors to")),
red(rss_path),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
downloaded = False
for uri in mirrors:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
downloader = self.FileTransceiver(
self.FtpInterface, self.Entropy, [uri],
[rss_path], download = True,
local_basedir = mytmpdir, critical_files = [rss_path], repo = repo
)
errors, m_fine_uris, m_broken_uris = downloader.go()
if not errors:
self.Entropy.updateProgress(
"[repo:%s] %s: %s" % (
brown(repo),
blue(_("notice board downloaded successfully from")),
red(crippled_uri),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
downloaded = True
break
if downloaded:
shutil.move(os.path.join(mytmpdir,os.path.basename(rss_path)),rss_path)
return downloaded
def upload_notice_board(self, repo = None):
if repo == None: repo = self.Entropy.default_repository
mirrors = self.Entropy.get_remote_mirrors(repo)
rss_path = self.Entropy.get_local_database_notice_board_file(repo)
self.Entropy.updateProgress(
"[repo:%s] %s %s" % (
brown(repo),
blue(_("uploading notice board from")),
red(rss_path),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
uploader = self.FileTransceiver(
self.FtpInterface,
self.Entropy,
mirrors,
[rss_path],
critical_files = [rss_path],
repo = repo
)
errors, m_fine_uris, m_broken_uris = uploader.go()
if errors:
m_broken_uris = sorted(list(m_broken_uris))
m_broken_uris = [self.entropyTools.extractFTPHostFromUri(x) for x in m_broken_uris]
self.Entropy.updateProgress(
"[repo:%s] %s %s" % (
brown(repo),
blue(_("notice board upload failed on")),
red(', '.join(m_broken_uris)),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return False
self.Entropy.updateProgress(
"[repo:%s] %s" % (
brown(repo),
blue(_("notice board upload success")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return True
def update_notice_board(self, title, notice_text, link = None, repo = None):
rss_title = "%s Notice Board" % (etpConst['systemname'],)
rss_description = "Inform about important distribution activities."
rss_path = self.Entropy.get_local_database_notice_board_file(repo)
if not link: link = etpConst['rss-website-url']
self.download_notice_board(repo)
Rss = self.rssFeed(rss_path, rss_title, rss_description, maxentries = 20)
Rss.addItem(title, link, description = notice_text)
Rss.writeChanges()
status = self.upload_notice_board(repo)
return status
def read_notice_board(self, do_download = True, repo = None):
rss_path = self.Entropy.get_local_database_notice_board_file(repo)
if do_download: self.download_notice_board(repo)
if not (os.path.isfile(rss_path) and os.access(rss_path,os.R_OK)):
return None
Rss = self.rssFeed(rss_path, '', '')
return Rss.getEntries()
def remove_from_notice_board(self, identifier, repo = None):
rss_path = self.Entropy.get_local_database_notice_board_file(repo)
rss_title = "%s Notice Board" % (etpConst['systemname'],)
rss_description = "Inform about important distribution activities."
if not (os.path.isfile(rss_path) and os.access(rss_path,os.R_OK)):
return 0
Rss = self.rssFeed(rss_path, rss_title, rss_description)
data = Rss.removeEntry(identifier)
Rss.writeChanges()
return data
def update_rss_feed(self, repo = None):
#db_dir = self.Entropy.get_local_database_dir(repo)
rss_path = self.Entropy.get_local_database_rss_file(repo)
rss_light_path = self.Entropy.get_local_database_rsslight_file(repo)
rss_dump_name = etpConst['rss-dump-name']
db_revision_path = self.Entropy.get_local_database_revision_file(repo)
rss_title = "%s Online Repository Status" % (etpConst['systemname'],)
rss_description = "Keep you updated on what's going on in the %s Repository." % (etpConst['systemname'],)
Rss = self.rssFeed(rss_path, rss_title, rss_description, maxentries = etpConst['rss-max-entries'])
# load dump
db_actions = self.Entropy.Cacher.pop(rss_dump_name)
if db_actions:
try:
f = open(db_revision_path)
revision = f.readline().strip()
f.close()
except (IOError, OSError):
revision = "N/A"
commitmessage = ''
if self.Entropy.rssMessages['commitmessage']:
commitmessage = ' :: '+self.Entropy.rssMessages['commitmessage']
title = ": "+etpConst['systemname']+" "+etpConst['product'][0].upper()+etpConst['product'][1:]+" "+etpConst['branch']+" :: Revision: "+revision+commitmessage
link = etpConst['rss-base-url']
# create description
added_items = db_actions.get("added")
if added_items:
for atom in added_items:
mylink = link+"?search="+atom.split("~")[0]+"&arch="+etpConst['currentarch']+"&product="+etpConst['product']
description = atom+": "+added_items[atom]['description']
Rss.addItem(title = "Added/Updated"+title, link = mylink, description = description)
removed_items = db_actions.get("removed")
if removed_items:
for atom in removed_items:
description = atom+": "+removed_items[atom]['description']
Rss.addItem(title = "Removed"+title, link = link, description = description)
light_items = db_actions.get('light')
if light_items:
rssLight = self.rssFeed(rss_light_path, rss_title, rss_description, maxentries = etpConst['rss-light-max-entries'])
for atom in light_items:
mylink = link+"?search="+atom.split("~")[0]+"&arch="+etpConst['currentarch']+"&product="+etpConst['product']
description = light_items[atom]['description']
rssLight.addItem(title = "["+revision+"] "+atom, link = mylink, description = description)
rssLight.writeChanges()
Rss.writeChanges()
self.Entropy.rssMessages.clear()
self.dumpTools.removeobj(rss_dump_name)
# f_out is a file instance
def dump_database_to_file(self, db_path, destination_path, opener, repo = None):
f_out = opener(destination_path, "wb")
dbconn = self.Entropy.openServerDatabase(db_path, just_reading = True, repo = repo)
dbconn.doDatabaseExport(f_out)
self.Entropy.close_server_database(dbconn)
f_out.close()
def create_file_checksum(self, file_path, checksum_path):
mydigest = self.entropyTools.md5sum(file_path)
f = open(checksum_path,"w")
mystring = "%s %s\n" % (mydigest,os.path.basename(file_path),)
f.write(mystring)
f.flush()
f.close()
def compress_file(self, file_path, destination_path, opener):
f_out = opener(destination_path, "wb")
f_in = open(file_path,"rb")
data = f_in.read(8192)
while data:
f_out.write(data)
data = f_in.read(8192)
f_in.close()
try:
f_out.flush()
except:
pass
f_out.close()
def get_files_to_sync(self, cmethod, download = False, repo = None):
critical = []
data = {}
data['database_revision_file'] = self.Entropy.get_local_database_revision_file(repo)
critical.append(data['database_revision_file'])
database_package_mask_file = self.Entropy.get_local_database_mask_file(repo)
if os.path.isfile(database_package_mask_file) or download:
data['database_package_mask_file'] = database_package_mask_file
if not download:
critical.append(data['database_package_mask_file'])
database_package_system_mask_file = self.Entropy.get_local_database_system_mask_file(repo)
if os.path.isfile(database_package_system_mask_file) or download:
data['database_package_system_mask_file'] = database_package_mask_file
if not download:
critical.append(data['database_package_system_mask_file'])
database_package_confl_tagged_file = self.Entropy.get_local_database_confl_tagged_file(repo)
if os.path.isfile(database_package_confl_tagged_file) or download:
data['database_package_confl_tagged_file'] = database_package_confl_tagged_file
if not download:
critical.append(data['database_package_confl_tagged_file'])
database_license_whitelist_file = self.Entropy.get_local_database_licensewhitelist_file(repo)
if os.path.isfile(database_license_whitelist_file) or download:
data['database_license_whitelist_file'] = database_license_whitelist_file
if not download:
critical.append(data['database_license_whitelist_file'])
database_rss_file = self.Entropy.get_local_database_rss_file(repo)
if os.path.isfile(database_rss_file) or download:
data['database_rss_file'] = database_rss_file
if not download:
critical.append(data['database_rss_file'])
database_rss_light_file = self.Entropy.get_local_database_rsslight_file(repo)
if os.path.isfile(database_rss_light_file) or download:
data['database_rss_light_file'] = database_rss_light_file
if not download:
critical.append(data['database_rss_light_file'])
# EAPI 2,3
if not download: # we don't need to get the dump
data['dump_path'] = os.path.join(self.Entropy.get_local_database_dir(repo),etpConst[cmethod[3]])
critical.append(data['dump_path'])
data['dump_path_digest'] = os.path.join(self.Entropy.get_local_database_dir(repo),etpConst[cmethod[4]])
critical.append(data['dump_path_digest'])
data['database_path'] = self.Entropy.get_local_database_file(repo)
critical.append(data['database_path'])
# EAPI 1
data['compressed_database_path'] = os.path.join(self.Entropy.get_local_database_dir(repo),etpConst[cmethod[2]])
critical.append(data['compressed_database_path'])
data['compressed_database_path_digest'] = os.path.join(
self.Entropy.get_local_database_dir(repo),etpConst['etpdatabasehashfile']
)
critical.append(data['compressed_database_path_digest'])
# SSL cert file, just for reference
ssl_ca_cert = self.Entropy.get_local_database_ca_cert_file()
if os.path.isfile(ssl_ca_cert):
data['ssl_ca_cert_file'] = ssl_ca_cert
if not download:
critical.append(ssl_ca_cert)
ssl_server_cert = self.Entropy.get_local_database_server_cert_file()
if os.path.isfile(ssl_server_cert):
data['ssl_server_cert_file'] = ssl_server_cert
if not download:
critical.append(ssl_server_cert)
# Some information regarding how packages are built
spm_files = [
(etpConst['spm']['global_make_conf'],"global_make_conf"),
(etpConst['spm']['global_package_keywords'],"global_package_keywords"),
(etpConst['spm']['global_package_use'],"global_package_use"),
(etpConst['spm']['global_package_mask'],"global_package_mask"),
(etpConst['spm']['global_package_unmask'],"global_package_unmask"),
]
for myfile,myname in spm_files:
if os.path.isfile(myfile) and os.access(myfile,os.R_OK):
data[myname] = myfile
make_profile = etpConst['spm']['global_make_profile']
if os.path.islink(make_profile):
mylink = os.readlink(make_profile)
mytmpdir = os.path.dirname(self.Entropy.entropyTools.getRandomTempFile())
mytmpfile = os.path.join(mytmpdir,'profile.link')
f = open(mytmpfile,"w")
f.write(mylink)
f.flush()
f.close()
data['global_make_profile'] = mytmpfile
return data, critical
class FileTransceiver:
import entropyTools
def __init__( self,
ftp_interface,
entropy_interface,
uris,
files_to_upload,
download = False,
remove = False,
ftp_basedir = None,
local_basedir = None,
critical_files = [],
use_handlers = False,
handlers_data = {},
repo = None
):
self.FtpInterface = ftp_interface
self.Entropy = entropy_interface
if not isinstance(uris,list):
raise exceptionTools.InvalidDataType("InvalidDataType: %s" % (_("uris must be a list instance"),))
if not isinstance(files_to_upload,(list,dict)):
raise exceptionTools.InvalidDataType("InvalidDataType: %s" % (
_("files_to_upload must be a list or dict instance"),
)
)
self.uris = uris
if isinstance(files_to_upload,list):
self.myfiles = files_to_upload[:]
else:
self.myfiles = sorted([x for x in files_to_upload])
self.download = download
self.remove = remove
self.repo = repo
if self.repo == None:
self.repo = self.Entropy.default_repository
self.use_handlers = use_handlers
if self.remove:
self.download = False
self.use_handlers = False
if not ftp_basedir:
# default to database directory
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
self.ftp_basedir = unicode(my_path)
else:
self.ftp_basedir = unicode(ftp_basedir)
if not local_basedir:
# default to database directory
self.local_basedir = os.path.dirname(self.Entropy.get_local_database_file(self.repo))
else:
self.local_basedir = unicode(local_basedir)
self.critical_files = critical_files
self.handlers_data = handlers_data.copy()
def handler_verify_upload(self, local_filepath, uri, ftp_connection, counter, maxcount, action, tries):
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
darkgreen(_("verifying upload (if supported)")),
blue(os.path.basename(local_filepath)),
),
importance = 0,
type = "info",
header = red(" @@ "),
back = True
)
checksum = self.Entropy.get_remote_package_checksum(
self.repo,
os.path.basename(local_filepath),
self.handlers_data['branch']
)
if checksum == None:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(_("digest verification")),
os.path.basename(local_filepath),
darkred(_("not supported")),
),
importance = 0,
type = "info",
header = red(" @@ ")
)
return True
elif checksum == False:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(_("digest verification")),
os.path.basename(local_filepath),
bold(_("file not found")),
),
importance = 0,
type = "warning",
header = brown(" @@ ")
)
return False
elif len(checksum) == 32:
# valid? checking
ckres = self.entropyTools.compareMd5(local_filepath,checksum)
if ckres:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(_("digest verification")),
os.path.basename(local_filepath),
darkgreen(_("so far, so good!")),
),
importance = 0,
type = "info",
header = red(" @@ ")
)
return True
else:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(_("digest verification")),
os.path.basename(local_filepath),
darkred(_("invalid checksum")),
),
importance = 0,
type = "warning",
header = brown(" @@ ")
)
return False
else:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(_("digest verification")),
os.path.basename(local_filepath),
darkred(_("unknown data returned")),
),
importance = 0,
type = "warning",
header = brown(" @@ ")
)
return True
def go(self):
broken_uris = set()
fine_uris = set()
errors = False
action = 'upload'
if self.download:
action = 'download'
elif self.remove:
action = 'remove'
for uri in self.uris:
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
self.Entropy.updateProgress(
"[%s|%s] %s..." % (
blue(crippled_uri),
brown(action),
blue(_("connecting to mirror")),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
ftp = self.FtpInterface(uri, self.Entropy)
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(self.repo),etpConst['branch'])
self.Entropy.updateProgress(
"[%s|%s] %s %s..." % (
blue(crippled_uri),
brown(action),
blue(_("changing directory to")),
darkgreen(my_path),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
ftp.setCWD(self.ftp_basedir, dodir = True)
maxcount = len(self.myfiles)
counter = 0
for mypath in self.myfiles:
ftp.setBasedir()
ftp.setCWD(self.ftp_basedir, dodir = True)
mycwd = None
if isinstance(mypath,tuple):
if len(mypath) < 2: continue
mycwd = mypath[0]
mypath = mypath[1]
ftp.setCWD(mycwd, dodir = True)
syncer = ftp.uploadFile
myargs = [mypath]
if self.download:
syncer = ftp.downloadFile
myargs = [os.path.basename(mypath),self.local_basedir]
elif self.remove:
syncer = ftp.deleteFile
counter += 1
tries = 0
done = False
lastrc = None
while tries < 5:
tries += 1
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(action+"ing"),
red(os.path.basename(mypath)),
),
importance = 0,
type = "info",
header = red(" @@ ")
)
rc = syncer(*myargs)
if rc and self.use_handlers and not self.download:
rc = self.handler_verify_upload(mypath, uri, ftp, counter, maxcount, action, tries)
if rc:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(action),
_("successful"),
red(os.path.basename(mypath)),
),
importance = 0,
type = "info",
header = darkgreen(" @@ ")
)
done = True
break
else:
self.Entropy.updateProgress(
"[%s|#%s|(%s/%s)] %s %s: %s" % (
blue(crippled_uri),
darkgreen(str(tries)),
blue(str(counter)),
bold(str(maxcount)),
blue(action),
brown(_("failed, retrying")),
red(os.path.basename(mypath)),
),
importance = 0,
type = "warning",
header = brown(" @@ ")
)
lastrc = rc
continue
if not done:
self.Entropy.updateProgress(
"[%s|(%s/%s)] %s %s: %s - %s: %s" % (
blue(crippled_uri),
blue(str(counter)),
bold(str(maxcount)),
blue(action),
darkred("failed, giving up"),
red(os.path.basename(mypath)),
_("error"),
lastrc,
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
if mypath not in self.critical_files:
self.Entropy.updateProgress(
"[%s|(%s/%s)] %s: %s, %s..." % (
blue(crippled_uri),
blue(str(counter)),
bold(str(maxcount)),
blue(_("not critical")),
os.path.basename(mypath),
blue(_("continuing")),
),
importance = 1,
type = "warning",
header = brown(" @@ ")
)
continue
ftp.closeConnection()
errors = True
broken_uris.add((uri,lastrc))
# next mirror
break
# close connection
ftp.closeConnection()
fine_uris.add(uri)
return errors,fine_uris,broken_uris
def _show_eapi3_upload_messages(self, crippled_uri, database_path, repo):
self.Entropy.updateProgress(
"[repo:%s|%s|%s:%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
red("EAPI"),
bold("3"),
blue(_("preparing uncompressed database for the upload")),
),
importance = 0,
type = "info",
header = darkgreen(" * ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("database path"),blue(database_path),),
importance = 0,
type = "info",
header = brown(" # ")
)
def _show_eapi2_upload_messages(self, crippled_uri, database_path, upload_data, cmethod, repo):
if repo == None:
repo = self.Entropy.default_repository
self.Entropy.updateProgress(
"[repo:%s|%s|%s:%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
red("EAPI"),
bold("2"),
blue(_("creating compressed database dump + checksum")),
),
importance = 0,
type = "info",
header = darkgreen(" * ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("database path"),blue(database_path),),
importance = 0,
type = "info",
header = brown(" # ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("dump"),blue(upload_data['dump_path']),),
importance = 0,
type = "info",
header = brown(" # ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("dump checksum"),blue(upload_data['dump_path_digest']),),
importance = 0,
type = "info",
header = brown(" # ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("opener"),blue(cmethod[0]),),
importance = 0,
type = "info",
header = brown(" # ")
)
def _show_eapi1_upload_messages(self, crippled_uri, database_path, upload_data, cmethod, repo):
self.Entropy.updateProgress(
"[repo:%s|%s|%s:%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
red("EAPI"),
bold("1"),
blue(_("compressing database + checksum")),
),
importance = 0,
type = "info",
header = darkgreen(" * "),
back = True
)
self.Entropy.updateProgress(
"%s: %s" % (_("database path"),blue(database_path),),
importance = 0,
type = "info",
header = brown(" # ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("compressed database path"),blue(upload_data['compressed_database_path']),),
importance = 0,
type = "info",
header = brown(" # ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("compressed checksum"),blue(upload_data['compressed_database_path_digest']),),
importance = 0,
type = "info",
header = brown(" # ")
)
self.Entropy.updateProgress(
"%s: %s" % (_("opener"),blue(cmethod[0]),),
importance = 0,
type = "info",
header = brown(" # ")
)
def create_mirror_directories(self, ftp_connection, path_to_create):
bdir = ""
for mydir in path_to_create.split("/"):
bdir += "/"+mydir
if not ftp_connection.isFileAvailable(bdir):
try:
ftp_connection.mkdir(bdir)
except Exception, e:
error = unicode(e)
if (error.find("550") == -1) and (error.find("File exist") == -1):
mytxt = "%s %s, %s: %s" % (
_("cannot create mirror directory"),
bdir,
_("error"),
e,
)
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: %s" % (mytxt,))
def mirror_lock_check(self, uri, repo = None):
if repo == None:
repo = self.Entropy.default_repository
gave_up = False
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
ftp = self.FtpInterface(uri, self.Entropy)
my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo),etpConst['branch'])
ftp.setCWD(my_path, dodir = True)
lock_file = self.get_database_lockfile(repo)
if not os.path.isfile(lock_file) and ftp.isFileAvailable(os.path.basename(lock_file)):
self.Entropy.updateProgress(
red("[repo:%s|%s|%s] %s, %s" % (
repo,
crippled_uri,
_("locking"),
_("mirror already locked"),
_("waiting up to 2 minutes before giving up"),
)
),
importance = 1,
type = "warning",
header = brown(" * "),
back = True
)
unlocked = False
count = 0
while count < 120:
count += 1
time.sleep(1)
if not ftp.isFileAvailable(os.path.basename(lock_file)):
self.Entropy.updateProgress(
red("[repo:%s|%s|%s] %s !" % (
repo,
crippled_uri,
_("locking"),
_("mirror unlocked"),
)
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
unlocked = True
break
if not unlocked:
gave_up = True
ftp.closeConnection()
return gave_up
def shrink_database_and_close(self, repo = None):
dbconn = self.Entropy.openServerDatabase(read_only = False, no_upload = True, repo = repo, indexing = False)
dbconn.dropAllIndexes()
dbconn.vacuum()
dbconn.vacuum()
dbconn.commitChanges()
self.Entropy.close_server_database(dbconn)
def sync_database_treeupdates(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
dbconn = self.Entropy.openServerDatabase(read_only = False, no_upload = True, repo = repo)
# grab treeupdates from other databases and inject
server_repos = etpConst['server_repositories'].keys()
all_actions = set()
for myrepo in server_repos:
# avoid __default__
if myrepo == etpConst['clientserverrepoid']:
continue
mydbc = self.Entropy.openServerDatabase(just_reading = True, repo = myrepo)
actions = mydbc.listAllTreeUpdatesActions(no_ids_repos = True)
for data in actions:
all_actions.add(data)
if not actions:
continue
backed_up_entries = dbconn.listAllTreeUpdatesActions()
try:
# clear first
dbconn.removeTreeUpdatesActions(repo)
dbconn.insertTreeUpdatesActions(all_actions,repo)
except Exception, e:
self.entropyTools.printTraceback()
mytxt = "%s, %s: %s. %s" % (
_("Troubles with treeupdates"),
_("error"),
e,
_("Bumping old data back"),
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning"
)
# restore previous data
dbconn.bumpTreeUpdatesActions(backed_up_entries)
dbconn.commitChanges()
self.Entropy.close_server_database(dbconn)
def upload_database(self, uris, lock_check = False, pretend = False, repo = None):
if repo == None:
repo = self.Entropy.default_repository
# doing some tests
import gzip
myt = type(gzip)
del myt
import bz2
myt = type(bz2)
del myt
if etpConst['rss-feed']:
self.update_rss_feed(repo = repo)
upload_errors = False
broken_uris = set()
fine_uris = set()
for uri in uris:
cmethod = etpConst['etpdatabasecompressclasses'].get(etpConst['etpdatabasefileformat'])
if cmethod == None:
raise exceptionTools.InvalidDataType("InvalidDataType: %s." % (
_("wrong database compression method passed"),
)
)
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
database_path = self.Entropy.get_local_database_file(repo)
upload_data, critical = self.get_files_to_sync(cmethod, repo = repo)
if lock_check:
given_up = self.mirror_lock_check(uri, repo = repo)
if given_up:
upload_errors = True
broken_uris.add(uri)
continue
self.lock_mirrors_for_download(True, [uri], repo = repo)
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s" % (
repo,
crippled_uri,
_("upload"),
_("preparing to upload database to mirror"),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
self.sync_database_treeupdates(repo)
self.Entropy.update_database_package_sets(repo)
self.Entropy.close_server_databases()
# backup current database to avoid re-indexing
old_dbpath = self.Entropy.get_local_database_file(repo)
backup_dbpath = old_dbpath+".up_backup"
copy_back = False
if not pretend:
try:
if os.path.isfile(backup_dbpath):
os.remove(backup_dbpath)
shutil.copy2(old_dbpath,backup_dbpath)
copy_back = True
except:
pass
self.shrink_database_and_close(repo)
# EAPI 3
self._show_eapi3_upload_messages(crippled_uri, database_path, repo)
# EAPI 2
self._show_eapi2_upload_messages(crippled_uri, database_path, upload_data, cmethod, repo)
# create compressed dump + checksum
self.dump_database_to_file(database_path, upload_data['dump_path'], eval(cmethod[0]), repo = repo)
self.create_file_checksum(upload_data['dump_path'], upload_data['dump_path_digest'])
# EAPI 1
self._show_eapi1_upload_messages(crippled_uri, database_path, upload_data, cmethod, repo)
# compress the database
self.compress_file(database_path, upload_data['compressed_database_path'], eval(cmethod[0]))
self.create_file_checksum(database_path, upload_data['compressed_database_path_digest'])
if not pretend:
# upload
uploader = self.FileTransceiver(
self.FtpInterface,
self.Entropy,
[uri],
[upload_data[x] for x in upload_data],
critical_files = critical,
repo = repo
)
errors, m_fine_uris, m_broken_uris = uploader.go()
if errors:
#my_fine_uris = sorted([self.entropyTools.extractFTPHostFromUri(x) for x in m_fine_uris])
my_broken_uris = sorted([(self.entropyTools.extractFTPHostFromUri(x[0]),x[1]) for x in m_broken_uris])
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s" % (
repo,
crippled_uri,
_("errors"),
_("failed to upload to mirror, not unlocking and continuing"),
),
importance = 0,
type = "error",
header = darkred(" !!! ")
)
# get reason
reason = my_broken_uris[0][1]
self.Entropy.updateProgress(
blue("%s: %s" % (_("reason"),reason,)),
importance = 0,
type = "error",
header = blue(" # ")
)
upload_errors = True
broken_uris |= m_broken_uris
continue
# copy db back
if copy_back and os.path.isfile(backup_dbpath):
self.Entropy.close_server_databases()
further_backup_dbpath = old_dbpath+".security_backup"
if os.path.isfile(further_backup_dbpath):
os.remove(further_backup_dbpath)
shutil.copy2(old_dbpath,further_backup_dbpath)
shutil.move(backup_dbpath,old_dbpath)
# unlock
self.lock_mirrors_for_download(False,[uri], repo = repo)
fine_uris |= m_fine_uris
if not fine_uris:
upload_errors = True
return upload_errors, broken_uris, fine_uris
def download_database(self, uris, lock_check = False, pretend = False, repo = None):
if repo == None:
repo = self.Entropy.default_repository
# doing some tests
import gzip
myt = type(gzip)
del myt
import bz2
myt = type(bz2)
del myt
download_errors = False
broken_uris = set()
fine_uris = set()
for uri in uris:
cmethod = etpConst['etpdatabasecompressclasses'].get(etpConst['etpdatabasefileformat'])
if cmethod == None:
raise exceptionTools.InvalidDataType("InvalidDataType: %s." % (
_("wrong database compression method passed"),
)
)
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
database_path = self.Entropy.get_local_database_file(repo)
database_dir_path = os.path.dirname(self.Entropy.get_local_database_file(repo))
download_data, critical = self.get_files_to_sync(cmethod, download = True, repo = repo)
mytmpdir = self.entropyTools.getRandomTempFile()
os.makedirs(mytmpdir)
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
red(_("download")),
blue(_("preparing to download database from mirror")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
files_to_sync = sorted(download_data.keys())
for myfile in files_to_sync:
self.Entropy.updateProgress(
"%s: %s" % (blue(_("download path")),brown(unicode(download_data[myfile])),),
importance = 0,
type = "info",
header = brown(" # ")
)
if lock_check:
given_up = self.mirror_lock_check(uri, repo = repo)
if given_up:
download_errors = True
broken_uris.add(uri)
continue
# avoid having others messing while we're downloading
self.lock_mirrors(True,[uri], repo = repo)
if not pretend:
# download
downloader = self.FileTransceiver(
self.FtpInterface, self.Entropy, [uri],
[download_data[x] for x in download_data], download = True,
local_basedir = mytmpdir, critical_files = critical, repo = repo
)
errors, m_fine_uris, m_broken_uris = downloader.go()
if errors:
#my_fine_uris = sorted([self.entropyTools.extractFTPHostFromUri(x) for x in m_fine_uris])
my_broken_uris = sorted([(self.entropyTools.extractFTPHostFromUri(x[0]),x[1]) for x in m_broken_uris])
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s" % (
brown(repo),
darkgreen(crippled_uri),
red(_("errors")),
blue(_("failed to download from mirror")),
),
importance = 0,
type = "error",
header = darkred(" !!! ")
)
# get reason
reason = my_broken_uris[0][1]
self.Entropy.updateProgress(
blue("%s: %s" % (_("reason"),reason,)),
importance = 0,
type = "error",
header = blue(" # ")
)
download_errors = True
broken_uris |= m_broken_uris
self.lock_mirrors(False,[uri], repo = repo)
continue
# all fine then, we need to move data from mytmpdir to database_dir_path
# EAPI 1
# unpack database
compressed_db_filename = os.path.basename(download_data['compressed_database_path'])
uncompressed_db_filename = os.path.basename(database_path)
compressed_file = os.path.join(mytmpdir,compressed_db_filename)
uncompressed_file = os.path.join(mytmpdir,uncompressed_db_filename)
self.entropyTools.uncompress_file(compressed_file, uncompressed_file, eval(cmethod[0]))
# now move
for myfile in os.listdir(mytmpdir):
fromfile = os.path.join(mytmpdir,myfile)
tofile = os.path.join(database_dir_path,myfile)
shutil.move(fromfile,tofile)
self.Entropy.ClientService.setup_default_file_perms(tofile)
if os.path.isdir(mytmpdir):
shutil.rmtree(mytmpdir)
if os.path.isdir(mytmpdir):
os.rmdir(mytmpdir)
fine_uris.add(uri)
self.lock_mirrors(False,[uri], repo = repo)
return download_errors, fine_uris, broken_uris
def calculate_database_sync_queues(self, repo = None):
if repo == None:
repo = self.Entropy.default_repository
remote_status = self.get_remote_databases_status(repo)
local_revision = self.Entropy.get_local_database_revision(repo)
upload_queue = []
download_latest = ()
# all mirrors are empty ? I rule
if not [x for x in remote_status if x[1]]:
upload_queue = remote_status[:]
else:
highest_remote_revision = max([x[1] for x in remote_status])
if local_revision < highest_remote_revision:
for x in remote_status:
if x[1] == highest_remote_revision:
download_latest = x
break
if download_latest:
upload_queue = [x for x in remote_status if (x[1] < highest_remote_revision)]
else:
upload_queue = [x for x in remote_status if (x[1] < local_revision)]
return download_latest, upload_queue
def sync_databases(self, no_upload = False, unlock_mirrors = False, repo = None):
if repo == None:
repo = self.Entropy.default_repository
while 1:
db_locked = False
if self.is_local_database_locked(repo):
db_locked = True
lock_data = self.get_mirrors_lock(repo)
mirrors_locked = [x for x in lock_data if x[1]]
if not mirrors_locked and db_locked:
# mirrors not locked remotely but only locally
mylock_file = self.get_database_lockfile(repo)
if os.path.isfile(mylock_file) and os.access(mylock_file,os.W_OK):
os.remove(mylock_file)
continue
break
if mirrors_locked and not db_locked:
mytxt = "%s, %s %s" % (
_("At the moment, mirrors are locked, someone is working on their databases"),
_("try again later"),
"...",
)
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: %s" % (mytxt,))
download_latest, upload_queue = self.calculate_database_sync_queues(repo)
if not download_latest and not upload_queue:
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
brown(repo),
red(_("sync")), # something short please
blue(_("database already in sync")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return 0, set(), set()
if download_latest:
download_uri = download_latest[0]
download_errors, fine_uris, broken_uris = self.download_database([download_uri], repo = repo)
if download_errors:
self.Entropy.updateProgress(
"[repo:%s|%s] %s: %s" % (
brown(repo),
red(_("sync")),
blue(_("database sync failed")),
red(_("download issues")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
return 1,fine_uris,broken_uris
# XXX: reload revision settings?
if upload_queue and not no_upload:
deps_not_found = self.Entropy.dependencies_test()
if deps_not_found and not self.Entropy.community_repo:
self.Entropy.updateProgress(
"[repo:%s|%s] %s: %s" % (
brown(repo),
red(_("sync")),
blue(_("database sync forbidden")),
red(_("dependencies_test() reported errors")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
return 3,set(),set()
problems = self.Entropy.check_config_file_updates()
if problems:
return 4,set(),set()
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
brown(repo),
red(_("config files")), # something short please
blue(_("no configuration files to commit. All fine.")),
),
importance = 1,
type = "info",
header = blue(" @@ "),
back = True
)
# for x in scandata:
#
uris = [x[0] for x in upload_queue]
errors, fine_uris, broken_uris = self.upload_database(uris, repo = repo)
if errors:
self.Entropy.updateProgress(
"[repo:%s|%s] %s: %s" % (
brown(repo),
red(_("sync")),
blue(_("database sync failed")),
red(_("upload issues")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
return 2,fine_uris,broken_uris
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
brown(repo),
red(_("sync")),
blue(_("database sync completed successfully")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
if unlock_mirrors:
self.lock_mirrors(False, repo = repo)
return 0, set(), set()
def calculate_local_upload_files(self, branch, repo = None):
upload_files = 0
upload_packages = set()
upload_dir = os.path.join(self.Entropy.get_local_upload_directory(repo),branch)
for package in os.listdir(upload_dir):
if package.endswith(etpConst['packagesext']) or package.endswith(etpConst['packageshashfileext']):
upload_packages.add(package)
if package.endswith(etpConst['packagesext']):
upload_files += 1
return upload_files, upload_packages
def calculate_local_package_files(self, branch, repo = None):
local_files = 0
local_packages = set()
packages_dir = os.path.join(self.Entropy.get_local_packages_directory(repo),branch)
if not os.path.isdir(packages_dir):
os.makedirs(packages_dir)
for package in os.listdir(packages_dir):
if package.endswith(etpConst['packagesext']) or package.endswith(etpConst['packageshashfileext']):
local_packages.add(package)
if package.endswith(etpConst['packagesext']):
local_files += 1
return local_files, local_packages
def _show_local_sync_stats(self, upload_files, local_files):
self.Entropy.updateProgress(
"%s:" % ( blue(_("Local statistics")),),
importance = 1,
type = "info",
header = red(" @@ ")
)
self.Entropy.updateProgress(
red("%s:\t\t%s %s" % (
blue(_("upload directory")),
bold(str(upload_files)),
red(_("files ready")),
)
),
importance = 0,
type = "info",
header = red(" @@ ")
)
self.Entropy.updateProgress(
red("%s:\t\t%s %s" % (
blue(_("packages directory")),
bold(str(local_files)),
red(_("files ready")),
)
),
importance = 0,
type = "info",
header = red(" @@ ")
)
def _show_sync_queues(self, upload, download, removal, copy, metainfo, branch):
# show stats
for itemdata in upload:
package = darkgreen(os.path.basename(itemdata[0]))
size = blue(self.entropyTools.bytesIntoHuman(itemdata[1]))
self.Entropy.updateProgress(
"[branch:%s|%s] %s [%s]" % (
brown(branch),
blue(_("upload")),
darkgreen(package),
size,
),
importance = 0,
type = "info",
header = red(" # ")
)
for itemdata in download:
package = darkred(os.path.basename(itemdata[0]))
size = blue(self.entropyTools.bytesIntoHuman(itemdata[1]))
self.Entropy.updateProgress(
"[branch:%s|%s] %s [%s]" % (
brown(branch),
darkred(_("download")),
blue(package),
size,
),
importance = 0,
type = "info",
header = red(" # ")
)
for itemdata in copy:
package = darkblue(os.path.basename(itemdata[0]))
size = blue(self.entropyTools.bytesIntoHuman(itemdata[1]))
self.Entropy.updateProgress(
"[branch:%s|%s] %s [%s]" % (
brown(branch),
darkgreen(_("copy")),
brown(package),
size,
),
importance = 0,
type = "info",
header = red(" # ")
)
for itemdata in removal:
package = brown(os.path.basename(itemdata[0]))
size = blue(self.entropyTools.bytesIntoHuman(itemdata[1]))
self.Entropy.updateProgress(
"[branch:%s|%s] %s [%s]" % (
brown(branch),
red(_("remove")),
red(package),
size,
),
importance = 0,
type = "info",
header = red(" # ")
)
self.Entropy.updateProgress(
"%s:\t\t\t%s" % (
blue(_("Packages to be removed")),
darkred(str(len(removal))),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
self.Entropy.updateProgress(
"%s:\t\t%s" % (
darkgreen(_("Packages to be moved locally")),
darkgreen(str(len(copy))),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
self.Entropy.updateProgress(
"%s:\t\t\t%s" % (
bold(_("Packages to be uploaded")),
bold(str(len(upload))),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
self.Entropy.updateProgress(
"%s:\t\t\t%s" % (
darkred(_("Total removal size")),
darkred(self.entropyTools.bytesIntoHuman(metainfo['removal'])),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
self.Entropy.updateProgress(
"%s:\t\t\t%s" % (
blue(_("Total upload size")),
blue(self.entropyTools.bytesIntoHuman(metainfo['upload'])),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
self.Entropy.updateProgress(
"%s:\t\t\t%s" % (
brown(_("Total download size")),
brown(self.entropyTools.bytesIntoHuman(metainfo['download'])),
),
importance = 0,
type = "info",
header = blue(" @@ ")
)
def calculate_remote_package_files(self, uri, branch, ftp_connection = None, repo = None):
remote_files = 0
close_conn = False
remote_packages_data = {}
my_path = os.path.join(self.Entropy.get_remote_packages_relative_path(repo),branch)
if ftp_connection == None:
close_conn = True
ftp_connection = self.FtpInterface(uri, self.Entropy)
ftp_connection.setCWD(my_path, dodir = True)
remote_packages = ftp_connection.listDir()
remote_packages_info = ftp_connection.getRoughList()
if close_conn:
ftp_connection.closeConnection()
for tbz2 in remote_packages:
if tbz2.endswith(etpConst['packagesext']):
remote_files += 1
for remote_package in remote_packages_info:
remote_packages_data[remote_package.split()[8]] = int(remote_package.split()[4])
return remote_files, remote_packages, remote_packages_data
def calculate_packages_to_sync(self, uri, branch, repo = None):
if repo == None:
repo = self.Entropy.default_repository
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
upload_files, upload_packages = self.calculate_local_upload_files(branch, repo)
local_files, local_packages = self.calculate_local_package_files(branch, repo)
self._show_local_sync_stats(upload_files, local_files)
self.Entropy.updateProgress(
"%s: %s" % (blue(_("Remote statistics for")),red(crippled_uri),),
importance = 1,
type = "info",
header = red(" @@ ")
)
remote_files, remote_packages, remote_packages_data = self.calculate_remote_package_files(
uri,
branch,
repo = repo
)
self.Entropy.updateProgress(
"%s:\t\t\t%s %s" % (
blue(_("remote packages")),
bold(str(remote_files)),
red(_("files stored")),
),
importance = 0,
type = "info",
header = red(" @@ ")
)
mytxt = blue("%s ...") % (_("Calculating queues"),)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "info",
header = red(" @@ ")
)
uploadQueue, downloadQueue, removalQueue, fineQueue = self.calculate_sync_queues(
upload_packages,
local_packages,
remote_packages,
remote_packages_data,
branch,
repo
)
return uploadQueue, downloadQueue, removalQueue, fineQueue, remote_packages_data
def calculate_sync_queues(
self,
upload_packages,
local_packages,
remote_packages,
remote_packages_data,
branch,
repo = None
):
uploadQueue = set()
downloadQueue = set()
removalQueue = set()
fineQueue = set()
for local_package in upload_packages:
if local_package in remote_packages:
local_filepath = os.path.join(self.Entropy.get_local_upload_directory(repo),branch,local_package)
local_size = self.entropyTools.get_file_size(local_filepath)
remote_size = remote_packages_data.get(local_package)
if remote_size == None:
remote_size = 0
if (local_size != remote_size):
# size does not match, adding to the upload queue
uploadQueue.add(local_package)
else:
fineQueue.add(local_package) # just move from upload to packages
else:
# always force upload of packages in uploaddir
uploadQueue.add(local_package)
# if a package is in the packages directory but not online, we have to upload it
# we have local_packages and remotePackages
for local_package in local_packages:
if local_package in remote_packages:
local_filepath = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,local_package)
local_size = self.entropyTools.get_file_size(local_filepath)
remote_size = remote_packages_data.get(local_package)
if remote_size == None:
remote_size = 0
if (local_size != remote_size) and (local_size != 0):
# size does not match, adding to the upload queue
if local_package not in fineQueue:
uploadQueue.add(local_package)
else:
# this means that the local package does not exist
# so, we need to download it
uploadQueue.add(local_package)
# Fill downloadQueue and removalQueue
for remote_package in remote_packages:
if remote_package in local_packages:
local_filepath = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,remote_package)
local_size = self.entropyTools.get_file_size(local_filepath)
remote_size = remote_packages_data.get(remote_package)
if remote_size == None:
remote_size = 0
if (local_size != remote_size) and (local_size != 0):
# size does not match, remove first
if remote_package not in uploadQueue: # do it only if the package has not been added to the uploadQueue
removalQueue.add(remote_package) # remotePackage == localPackage # just remove something that differs from the content of the mirror
# then add to the download queue
downloadQueue.add(remote_package)
else:
# this means that the local package does not exist
# so, we need to download it
if not remote_package.endswith(".tmp"): # ignore .tmp files
downloadQueue.add(remote_package)
# Collect packages that don't exist anymore in the database
# so we can filter them out from the download queue
dbconn = self.Entropy.openServerDatabase(just_reading = True, repo = repo)
db_files = dbconn.listBranchPackagesTbz2(branch, do_sort = False, full_path = True)
db_files = set([os.path.basename(x) for x in db_files if (self.Entropy.get_branch_from_download_relative_uri(x) == branch)])
exclude = set()
for myfile in downloadQueue:
if myfile.endswith(etpConst['packagesext']):
if myfile not in db_files:
exclude.add(myfile)
downloadQueue -= exclude
exclude = set()
for myfile in uploadQueue:
if myfile.endswith(etpConst['packagesext']):
if myfile not in db_files:
exclude.add(myfile)
uploadQueue -= exclude
exclude = set()
for myfile in downloadQueue:
if myfile in uploadQueue:
exclude.add(myfile)
downloadQueue -= exclude
return uploadQueue, downloadQueue, removalQueue, fineQueue
def expand_queues(self, uploadQueue, downloadQueue, removalQueue, remote_packages_data, branch, repo):
metainfo = {
'removal': 0,
'download': 0,
'upload': 0,
}
removal = []
download = []
copy = []
upload = []
for item in removalQueue:
if not item.endswith(etpConst['packagesext']):
continue
local_filepath = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,item)
size = self.entropyTools.get_file_size(local_filepath)
metainfo['removal'] += size
removal.append((local_filepath,size))
for item in downloadQueue:
if not item.endswith(etpConst['packagesext']):
continue
local_filepath = os.path.join(self.Entropy.get_local_upload_directory(repo),branch,item)
if not os.path.isfile(local_filepath):
size = remote_packages_data.get(item)
if size == None:
size = 0
size = int(size)
metainfo['removal'] += size
download.append((local_filepath,size))
else:
size = self.entropyTools.get_file_size(local_filepath)
copy.append((local_filepath,size))
for item in uploadQueue:
if not item.endswith(etpConst['packagesext']):
continue
local_filepath = os.path.join(self.Entropy.get_local_upload_directory(repo),branch,item)
local_filepath_pkgs = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,item)
if os.path.isfile(local_filepath):
size = self.entropyTools.get_file_size(local_filepath)
upload.append((local_filepath,size))
else:
size = self.entropyTools.get_file_size(local_filepath_pkgs)
upload.append((local_filepath_pkgs,size))
metainfo['upload'] += size
return upload, download, removal, copy, metainfo
def _sync_run_removal_queue(self, removal_queue, branch, repo = None):
if repo == None:
repo = self.Entropy.default_repository
for itemdata in removal_queue:
remove_filename = itemdata[0]
remove_filepath = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,remove_filename)
remove_filepath_hash = remove_filepath+etpConst['packageshashfileext']
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s: %s [%s]" % (
brown(repo),
red("sync"),
brown(branch),
blue(_("removing package+hash")),
darkgreen(remove_filename),
blue(self.entropyTools.bytesIntoHuman(itemdata[1])),
),
importance = 0,
type = "info",
header = darkred(" * ")
)
if os.path.isfile(remove_filepath):
os.remove(remove_filepath)
if os.path.isfile(remove_filepath_hash):
os.remove(remove_filepath_hash)
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s" % (
brown(repo),
red(_("sync")),
brown(branch),
blue(_("removal complete")),
),
importance = 0,
type = "info",
header = darkred(" * ")
)
def _sync_run_copy_queue(self, copy_queue, branch, repo = None):
if repo == None:
repo = self.Entropy.default_repository
for itemdata in copy_queue:
from_file = itemdata[0]
from_file_hash = from_file+etpConst['packageshashfileext']
to_file = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,os.path.basename(from_file))
to_file_hash = to_file+etpConst['packageshashfileext']
expiration_file = to_file+etpConst['packagesexpirationfileext']
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s: %s" % (
brown(repo),
red("sync"),
brown(branch),
blue(_("copying file+hash to repository")),
darkgreen(from_file),
),
importance = 0,
type = "info",
header = darkred(" * ")
)
if not os.path.isdir(os.path.dirname(to_file)):
os.makedirs(os.path.dirname(to_file))
shutil.copy2(from_file,to_file)
if not os.path.isfile(from_file_hash):
self.create_file_checksum(from_file, from_file_hash)
shutil.copy2(from_file_hash,to_file_hash)
# clear expiration file
if os.path.isfile(expiration_file):
os.remove(expiration_file)
def _sync_run_upload_queue(self, uri, upload_queue, branch, repo = None):
if repo == None:
repo = self.Entropy.default_repository
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
myqueue = []
for itemdata in upload_queue:
x = itemdata[0]
hash_file = x+etpConst['packageshashfileext']
if not os.path.isfile(hash_file):
self.entropyTools.createHashFile(x)
myqueue.append(hash_file)
myqueue.append(x)
ftp_basedir = os.path.join(self.Entropy.get_remote_packages_relative_path(repo),branch)
uploader = self.FileTransceiver( self.FtpInterface,
self.Entropy,
[uri],
myqueue,
critical_files = myqueue,
use_handlers = True,
ftp_basedir = ftp_basedir,
handlers_data = {'branch': branch },
repo = repo
)
errors, m_fine_uris, m_broken_uris = uploader.go()
if errors:
my_broken_uris = [(self.entropyTools.extractFTPHostFromUri(x[0]),x[1]) for x in m_broken_uris]
reason = my_broken_uris[0][1]
self.Entropy.updateProgress(
"[branch:%s] %s: %s, %s: %s" % (
brown(branch),
blue(_("upload errors")),
red(crippled_uri),
blue(_("reason")),
darkgreen(unicode(reason)),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
return errors, m_fine_uris, m_broken_uris
self.Entropy.updateProgress(
"[branch:%s] %s: %s" % (
brown(branch),
blue(_("upload completed successfully")),
red(crippled_uri),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return errors, m_fine_uris, m_broken_uris
def _sync_run_download_queue(self, uri, download_queue, branch, repo = None):
if repo == None:
repo = self.Entropy.default_repository
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
myqueue = []
for itemdata in download_queue:
x = itemdata[0]
hash_file = x+etpConst['packageshashfileext']
myqueue.append(x)
myqueue.append(hash_file)
ftp_basedir = os.path.join(self.Entropy.get_remote_packages_relative_path(repo),branch)
local_basedir = os.path.join(self.Entropy.get_local_packages_directory(repo),branch)
downloader = self.FileTransceiver(
self.FtpInterface,
self.Entropy,
[uri],
myqueue,
critical_files = myqueue,
use_handlers = True,
ftp_basedir = ftp_basedir,
local_basedir = local_basedir,
handlers_data = {'branch': branch },
download = True,
repo = repo
)
errors, m_fine_uris, m_broken_uris = downloader.go()
if errors:
my_broken_uris = [(self.entropyTools.extractFTPHostFromUri(x[0]),x[1]) for x in m_broken_uris]
reason = my_broken_uris[0][1]
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s: %s, %s: %s" % (
brown(repo),
red(_("sync")),
brown(branch),
blue(_("download errors")),
darkgreen(crippled_uri),
blue(_("reason")),
reason,
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
return errors, m_fine_uris, m_broken_uris
self.Entropy.updateProgress(
"[repo:%s|%s|%s] %s: %s" % (
brown(repo),
red(_("sync")),
brown(branch),
blue(_("download completed successfully")),
darkgreen(crippled_uri),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
return errors, m_fine_uris, m_broken_uris
def sync_packages(self, ask = True, pretend = False, packages_check = False, repo = None):
if repo == None:
repo = self.Entropy.default_repository
self.Entropy.updateProgress(
"[repo:%s|%s] %s" % (
repo,
red(_("sync")),
darkgreen(_("starting packages sync")),
),
importance = 1,
type = "info",
header = red(" @@ "),
back = True
)
successfull_mirrors = set()
broken_mirrors = set()
check_data = ()
mirrors_tainted = False
mirror_errors = False
mirrors_errors = False
for uri in self.Entropy.get_remote_mirrors(repo):
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
mirror_errors = False
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s: %s" % (
repo,
red(_("sync")),
brown(etpConst['branch']),
blue(_("packages sync")),
bold(crippled_uri),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
try:
uploadQueue, downloadQueue, removalQueue, fineQueue, remote_packages_data = self.calculate_packages_to_sync(uri, etpConst['branch'], repo)
del fineQueue
except self.socket.error, e:
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s: %s, %s %s" % (
repo,
red(_("sync")),
etpConst['branch'],
darkred(_("socket error")),
e,
darkred(_("on")),
crippled_uri,
),
importance = 1,
type = "error",
header = darkgreen(" * ")
)
continue
if (not uploadQueue) and (not downloadQueue) and (not removalQueue):
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s: %s" % (
repo,
red(_("sync")),
etpConst['branch'],
darkgreen(_("nothing to do on")),
crippled_uri,
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
successfull_mirrors.add(uri)
continue
self.Entropy.updateProgress(
"%s:" % (blue(_("Expanding queues")),),
importance = 1,
type = "info",
header = red(" ** ")
)
upload, download, removal, copy, metainfo = self.expand_queues(
uploadQueue,
downloadQueue,
removalQueue,
remote_packages_data,
etpConst['branch'],
repo
)
del uploadQueue, downloadQueue, removalQueue, remote_packages_data
self._show_sync_queues(upload, download, removal, copy, metainfo, etpConst['branch'])
if not len(upload)+len(download)+len(removal)+len(copy):
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s %s" % (
self.Entropy.default_repository,
red(_("sync")),
etpConst['branch'],
blue(_("nothing to sync for")),
crippled_uri,
),
importance = 1,
type = "info",
header = darkgreen(" @@ ")
)
successfull_mirrors.add(uri)
continue
if pretend:
successfull_mirrors.add(uri)
continue
if ask:
rc = self.Entropy.askQuestion(_("Would you like to run the steps above ?"))
if rc == "No":
continue
try:
if removal:
self._sync_run_removal_queue(removal, etpConst['branch'], repo)
if copy:
self._sync_run_copy_queue(copy, etpConst['branch'], repo)
if upload or download:
mirrors_tainted = True
if upload:
d_errors, m_fine_uris, m_broken_uris = self._sync_run_upload_queue(uri, upload, etpConst['branch'], repo)
if d_errors: mirror_errors = True
if download:
d_errors, m_fine_uris, m_broken_uris = self._sync_run_download_queue(uri, download, etpConst['branch'], repo)
if d_errors: mirror_errors = True
if not mirror_errors:
successfull_mirrors.add(uri)
else:
mirrors_errors = True
except KeyboardInterrupt:
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s" % (
repo,
red(_("sync")),
etpConst['branch'],
darkgreen(_("keyboard interrupt !")),
),
importance = 1,
type = "info",
header = darkgreen(" * ")
)
continue
except Exception, e:
self.entropyTools.printTraceback()
mirrors_errors = True
broken_mirrors.add(uri)
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s: %s, %s: %s" % (
repo,
red(_("sync")),
etpConst['branch'],
darkred(_("exception caught")),
Exception,
_("error"),
e,
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
exc_txt = self.Entropy.entropyTools.printException(returndata = True)
for line in exc_txt:
self.Entropy.updateProgress(
unicode(line),
importance = 1,
type = "error",
header = darkred(": ")
)
if len(successfull_mirrors) > 0:
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s" % (
repo,
red(_("sync")),
etpConst['branch'],
darkred(_("at least one mirror has been sync'd properly, hooray!")),
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
continue
# if at least one server has been synced successfully, move files
if (len(successfull_mirrors) > 0) and not pretend:
self.remove_expiration_files(etpConst['branch'], repo)
if packages_check:
check_data = self.Entropy.verify_local_packages([], ask = ask, repo = repo)
return mirrors_tainted, mirrors_errors, successfull_mirrors, broken_mirrors, check_data
def remove_expiration_files(self, branch, repo = None):
if repo == None:
repo = self.Entropy.default_repository
branch_dir = os.path.join(self.Entropy.get_local_upload_directory(repo),branch)
branchcontent = os.listdir(branch_dir)
for xfile in branchcontent:
source = os.path.join(self.Entropy.get_local_upload_directory(repo),branch,xfile)
destdir = os.path.join(self.Entropy.get_local_packages_directory(repo),branch)
if not os.path.isdir(destdir):
os.makedirs(destdir)
dest = os.path.join(destdir,xfile)
shutil.move(source,dest)
# clear expiration file
dest_expiration = dest+etpConst['packagesexpirationfileext']
if os.path.isfile(dest_expiration):
os.remove(dest_expiration)
def is_package_expired(self, package_file, branch, repo = None):
pkg_path = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,package_file)
pkg_path += etpConst['packagesexpirationfileext']
if not os.path.isfile(pkg_path):
return False
mtime = self.entropyTools.getFileUnixMtime(pkg_path)
delta = int(etpConst['packagesexpirationdays'])*24*3600
currmtime = time.time()
file_delta = currmtime - mtime
if file_delta > delta:
return True
return False
def create_expiration_file(self, package_file, branch, repo = None, gentle = False):
pkg_path = os.path.join(self.Entropy.get_local_packages_directory(repo),branch,package_file)
pkg_path += etpConst['packagesexpirationfileext']
if gentle and os.path.isfile(pkg_path):
return
f = open(pkg_path,"w")
f.flush()
f.close()
def collect_expiring_packages(self, branch, repo = None):
dbconn = self.Entropy.openServerDatabase(just_reading = True, repo = repo)
database_bins = dbconn.listBranchPackagesTbz2(branch, do_sort = False, full_path = True)
bins_dir = os.path.join(self.Entropy.get_local_packages_directory(repo),branch)
repo_bins = set()
if os.path.isdir(bins_dir):
repo_bins = os.listdir(bins_dir)
repo_bins = set([os.path.join('packages',etpSys['arch'],branch,x) for x in repo_bins if x.endswith(etpConst['packagesext'])])
repo_bins -= database_bins
return set([os.path.basename(x) for x in repo_bins])
def tidy_mirrors(self, ask = True, pretend = False, repo = None):
if repo == None:
repo = self.Entropy.default_repository
self.Entropy.updateProgress(
"[repo:%s|%s|branch:%s] %s" % (
brown(repo),
red(_("tidy")),
blue(etpConst['branch']),
blue(_("collecting expired packages")),
),
importance = 1,
type = "info",
header = red(" @@ ")
)
branch_data = {}
errors = False
branch_data['errors'] = False
if etpConst['branch'] != etpConst['branches'][-1]:
self.Entropy.updateProgress(
"[branch:%s] %s" % (
brown(etpConst['branch']),
blue(_("not the latest branch, skipping tidy for consistence. This branch entered the maintenance mode.")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
branch_data['errors'] = True
return True, branch_data
self.Entropy.updateProgress(
"[branch:%s] %s" % (
brown(etpConst['branch']),
blue(_("collecting expired packages in the selected branches")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# collect removed packages
expiring_packages = self.collect_expiring_packages(etpConst['branch'], repo)
removal = []
for package in expiring_packages:
expired = self.is_package_expired(package, etpConst['branch'], repo)
if expired:
removal.append(package)
else:
self.create_expiration_file(package, etpConst['branch'], repo, gentle = True)
# fill returning data
branch_data['removal'] = removal[:]
if not removal:
self.Entropy.updateProgress(
"[branch:%s] %s" % (
brown(etpConst['branch']),
blue(_("nothing to remove on this branch")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return errors, branch_data
else:
self.Entropy.updateProgress(
"[branch:%s] %s:" % (
brown(etpConst['branch']),
blue(_("these are the expired packages")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
for package in removal:
self.Entropy.updateProgress(
"[branch:%s] %s: %s" % (
brown(etpConst['branch']),
blue(_("remove")),
darkgreen(package),
),
importance = 1,
type = "info",
header = brown(" # ")
)
if pretend:
return errors,branch_data
if ask:
rc = self.Entropy.askQuestion(_("Would you like to continue ?"))
if rc == "No":
return errors, branch_data
myqueue = []
for package in removal:
myqueue.append(package+etpConst['packageshashfileext'])
myqueue.append(package)
ftp_basedir = os.path.join(self.Entropy.get_remote_packages_relative_path(repo),etpConst['branch'])
for uri in self.Entropy.get_remote_mirrors(repo):
self.Entropy.updateProgress(
"[branch:%s] %s..." % (
brown(etpConst['branch']),
blue(_("removing packages remotely")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
crippled_uri = self.entropyTools.extractFTPHostFromUri(uri)
destroyer = self.FileTransceiver(
self.FtpInterface,
self.Entropy,
[uri],
myqueue,
critical_files = [],
ftp_basedir = ftp_basedir,
remove = True,
repo = repo
)
errors, m_fine_uris, m_broken_uris = destroyer.go()
if errors:
my_broken_uris = [(self.entropyTools.extractFTPHostFromUri(x[0]),x[1]) for x in m_broken_uris]
reason = my_broken_uris[0][1]
self.Entropy.updateProgress(
"[branch:%s] %s: %s, %s: %s" % (
brown(etpConst['branch']),
blue(_("remove errors")),
red(crippled_uri),
blue(_("reason")),
reason,
),
importance = 1,
type = "warning",
header = brown(" !!! ")
)
branch_data['errors'] = True
errors = True
self.Entropy.updateProgress(
"[branch:%s] %s..." % (
brown(etpConst['branch']),
blue(_("removing packages locally")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
branch_data['removed'] = set()
for package in removal:
package_path = os.path.join(self.Entropy.get_local_packages_directory(repo),etpConst['branch'],package)
package_path_hash = package_path+etpConst['packageshashfileext']
package_path_expired = package_path+etpConst['packagesexpirationfileext']
for myfile in [package_path_hash,package_path,package_path_expired]:
if os.path.isfile(myfile):
self.Entropy.updateProgress(
"[branch:%s] %s: %s" % (
brown(etpConst['branch']),
blue(_("removing")),
darkgreen(myfile),
),
importance = 1,
type = "info",
header = brown(" @@ ")
)
os.remove(myfile)
branch_data['removed'].add(myfile)
return errors, branch_data
class EntropyDatabaseInterface:
import entropyTools, dumpTools, threading
def __init__(
self,
readOnly = False,
noUpload = False,
dbFile = None,
clientDatabase = False,
xcache = False,
dbname = etpConst['serverdbid'],
indexing = True,
OutputInterface = None,
ServiceInterface = None,
skipChecks = False, # dangerous!
useBranch = None,
lockRemote = True
):
self.dbMatchCacheKey = etpCache['dbMatch']
self.dbSearchCacheKey = etpCache['dbSearch']
self.dbname = dbname
self.lockRemote = lockRemote
self.db_branch = etpConst['branch']
if self.dbname == etpConst['clientdbid']:
self.db_branch = None
if useBranch != None: self.db_branch = useBranch
if OutputInterface == None:
OutputInterface = TextInterface()
if dbFile == None:
raise exceptionTools.IncorrectParameter("IncorrectParameter: %s" % (_("valid database path needed"),) )
self.WriteLock = self.threading.Lock()
self.dbapi2 = dbapi2
# setup output interface
self.OutputInterface = OutputInterface
self.updateProgress = self.OutputInterface.updateProgress
self.askQuestion = self.OutputInterface.askQuestion
# setup service interface
self.ServiceInterface = ServiceInterface
self.readOnly = readOnly
self.noUpload = noUpload
self.clientDatabase = clientDatabase
self.xcache = xcache
self.indexing = indexing
self.skipChecks = skipChecks
if not self.skipChecks:
if not self.entropyTools.is_user_in_entropy_group():
# forcing since we won't have write access to db
self.indexing = False
# live systems don't like wasting RAM
if self.entropyTools.islive():
self.indexing = False
self.dbFile = dbFile
self.dbclosed = True
self.server_repo = None
if not self.clientDatabase:
self.server_repo = self.dbname[len(etpConst['serverdbid']):]
self.create_dbstatus_data()
if not self.skipChecks:
# no caching for non root and server connections
if (self.dbname.startswith(etpConst['serverdbid'])) or (not self.entropyTools.is_user_in_entropy_group()):
self.xcache = False
self.live_cache = {}
# create connection
self.connection = self.dbapi2.connect(dbFile,timeout=300.0, check_same_thread = False)
self.cursor = self.connection.cursor()
if not self.skipChecks:
if os.access(self.dbFile,os.W_OK) and self.doesTableExist('baseinfo') and self.doesTableExist('extrainfo'):
if self.entropyTools.islive():
# check where's the file
if etpConst['systemroot']:
self.databaseStructureUpdates()
else:
self.databaseStructureUpdates()
# now we can set this to False
self.dbclosed = False
def setCacheSize(self, size):
self.cursor.execute('PRAGMA cache_size = '+str(size))
def setDefaultCacheSize(self, size):
self.cursor.execute('PRAGMA default_cache_size = '+str(size))
def __del__(self):
if not self.dbclosed:
self.closeDB()
def create_dbstatus_data(self):
taint_file = self.ServiceInterface.get_local_database_taint_file(self.server_repo)
if not etpDbStatus.has_key(self.dbFile):
etpDbStatus[self.dbFile] = {}
etpDbStatus[self.dbFile]['tainted'] = False
etpDbStatus[self.dbFile]['bumped'] = False
if os.path.isfile(taint_file):
etpDbStatus[self.dbFile]['tainted'] = True
etpDbStatus[self.dbFile]['bumped'] = True
def closeDB(self):
self.dbclosed = True
# if the class is opened readOnly, close and forget
if self.readOnly:
self.cursor.close()
self.connection.close()
return
if self.clientDatabase:
self.commitChanges()
self.cursor.close()
self.connection.close()
return
if not etpDbStatus[self.dbFile]['tainted']:
# we can unlock it, no changes were made
self.ServiceInterface.MirrorsService.lock_mirrors(False, repo = self.server_repo)
else:
self.updateProgress(
darkgreen(_("Mirrors have not been unlocked. Remember to sync them.")),
importance = 1,
type = "info",
header = brown(" * ")
)
self.commitChanges()
#self.vacuum()
self.cursor.close()
self.connection.close()
def vacuum(self):
self.cursor.execute("vacuum")
def commitChanges(self):
if self.readOnly:
return
try:
self.connection.commit()
except:
pass
if not self.clientDatabase:
self.taintDatabase()
if (etpDbStatus[self.dbFile]['tainted']) and \
(not etpDbStatus[self.dbFile]['bumped']):
# bump revision, setting DatabaseBump causes the session to just bump once
etpDbStatus[self.dbFile]['bumped'] = True
self.revisionBump()
def taintDatabase(self):
# if it's equo to open it, this should be avoided
if self.clientDatabase:
return
# taint the database status
taint_file = self.ServiceInterface.get_local_database_taint_file(repo = self.server_repo)
f = open(taint_file,"w")
f.write(etpConst['currentarch']+" database tainted\n")
f.flush()
f.close()
etpDbStatus[self.dbFile]['tainted'] = True
def untaintDatabase(self):
if (self.clientDatabase): # if it's equo to open it, this should be avoided
return
etpDbStatus[self.dbFile]['tainted'] = False
# untaint the database status
taint_file = self.ServiceInterface.get_local_database_taint_file(repo = self.server_repo)
if os.path.isfile(taint_file):
os.remove(taint_file)
def revisionBump(self):
revision_file = self.ServiceInterface.get_local_database_revision_file(repo = self.server_repo)
if not os.path.isfile(revision_file):
revision = 1
else:
f = open(revision_file,"r")
revision = int(f.readline().strip())
revision += 1
f.close()
f = open(revision_file,"w")
f.write(str(revision)+"\n")
f.flush()
f.close()
def isDatabaseTainted(self):
taint_file = self.ServiceInterface.get_local_database_taint_file(repo = self.server_repo)
if os.path.isfile(taint_file):
return True
return False
# never use this unless you know what you're doing
def initializeDatabase(self):
self.checkReadOnly()
self.cursor.executescript(etpConst['sql_destroy'])
self.cursor.executescript(etpConst['sql_init'])
self.databaseStructureUpdates()
# set cache size
self.setCacheSize(6000)
self.setDefaultCacheSize(6000)
self.commitChanges()
def checkReadOnly(self):
if (self.readOnly):
raise exceptionTools.OperationNotPermitted("OperationNotPermitted: %s." % (
_("can't do that on a readonly database"),
)
)
# check for /usr/portage/profiles/updates changes
def serverUpdatePackagesData(self):
etpConst['server_treeupdatescalled'].add(self.server_repo)
repo_updates_file = self.ServiceInterface.get_local_database_treeupdates_file(self.server_repo)
doRescan = False
stored_digest = self.retrieveRepositoryUpdatesDigest(self.server_repo)
if stored_digest == -1:
doRescan = True
# check portage files for changes if doRescan is still false
portage_dirs_digest = "0"
if not doRescan:
treeupdates_dict = self.ServiceInterface.repository_treeupdate_digests
if treeupdates_dict.has_key(self.server_repo):
portage_dirs_digest = treeupdates_dict.get(self.server_repo)
else:
SpmIntf = SpmInterface(self.OutputInterface)
Spm = SpmIntf.intf
# grab portdir
updates_dir = etpConst['systemroot']+Spm.get_spm_setting("PORTDIR")+"/profiles/updates"
if os.path.isdir(updates_dir):
# get checksum
mdigest = self.entropyTools.md5sum_directory(updates_dir, get_obj = True)
# also checksum etpConst['etpdatabaseupdatefile']
if os.path.isfile(repo_updates_file):
f = open(repo_updates_file)
block = f.read(1024)
while block:
mdigest.update(block)
block = f.read(1024)
f.close()
portage_dirs_digest = mdigest.hexdigest()
treeupdates_dict[self.server_repo] = portage_dirs_digest
del updates_dir
if doRescan or (str(stored_digest) != str(portage_dirs_digest)):
# force parameters
self.readOnly = False
self.noUpload = True
# reset database tables
self.clearTreeupdatesEntries(self.server_repo)
SpmIntf = SpmInterface(self.OutputInterface)
Spm = SpmIntf.intf
updates_dir = etpConst['systemroot']+Spm.get_spm_setting("PORTDIR")+"/profiles/updates"
update_files = self.entropyTools.sortUpdateFiles(os.listdir(updates_dir))
update_files = [os.path.join(updates_dir,x) for x in update_files]
# now load actions from files
update_actions = []
for update_file in update_files:
f = open(update_file,"r")
mycontent = f.readlines()
f.close()
lines = [x.strip() for x in mycontent if x.strip()]
update_actions.extend(lines)
# add entropy packages.db.repo_updates content
if os.path.isfile(repo_updates_file):
f = open(repo_updates_file,"r")
mycontent = f.readlines()
f.close()
lines = [x.strip() for x in mycontent if x.strip() and not x.strip().startswith("#")]
update_actions.extend(lines)
# now filter the required actions
update_actions = self.filterTreeUpdatesActions(update_actions)
if update_actions:
mytxt = "%s: %s. %s %s" % (
bold(_("ATTENTION")),
red(_("forcing package updates")),
red(_("Syncing with")),
blue(updates_dir),
)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = brown(" * ")
)
# lock database
if self.lockRemote:
self.ServiceInterface.doServerDatabaseSyncLock(self.server_repo, self.noUpload)
# now run queue
try:
self.runTreeUpdatesActions(update_actions)
except:
# destroy digest
self.setRepositoryUpdatesDigest(self.server_repo, "-1")
raise
# store new actions
self.addRepositoryUpdatesActions(self.server_repo, update_actions, self.db_branch)
# store new digest into database
self.setRepositoryUpdatesDigest(self.server_repo, portage_dirs_digest)
def clientUpdatePackagesData(self, clientDbconn, force = False):
if clientDbconn == None:
return
repository = self.dbname[len(etpConst['dbnamerepoprefix']):]
etpConst['client_treeupdatescalled'].add(repository)
doRescan = False
shell_rescan = os.getenv("ETP_TREEUPDATES_RESCAN")
if shell_rescan: doRescan = True
# check database digest
stored_digest = self.retrieveRepositoryUpdatesDigest(repository)
if stored_digest == -1:
doRescan = True
# check stored value in client database
client_digest = "0"
if not doRescan:
client_digest = clientDbconn.retrieveRepositoryUpdatesDigest(repository)
if doRescan or (str(stored_digest) != str(client_digest)) or force:
# reset database tables
clientDbconn.clearTreeupdatesEntries(repository)
# load updates
update_actions = self.retrieveTreeUpdatesActions(repository)
# now filter the required actions
update_actions = clientDbconn.filterTreeUpdatesActions(update_actions)
if update_actions:
mytxt = "%s: %s. %s %s" % (
bold(_("ATTENTION")),
red(_("forcing packages metadata update")),
red(_("Updating system database using repository id")),
blue(repository),
)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = darkred(" * ")
)
# run stuff
clientDbconn.runTreeUpdatesActions(update_actions)
# store new digest into database
clientDbconn.setRepositoryUpdatesDigest(repository, stored_digest)
# store new actions
clientDbconn.addRepositoryUpdatesActions(etpConst['clientdbid'], update_actions, etpConst['branch'])
# clear client cache
clientDbconn.clearCache()
return True
# this functions will filter either data from /usr/portage/profiles/updates/*
# or repository database returning only the needed actions
def filterTreeUpdatesActions(self, actions):
new_actions = []
for action in actions:
if action in new_actions: # skip dupies
continue
doaction = action.split()
if doaction[0] == "slotmove":
# slot move
atom = doaction[1]
from_slot = doaction[2]
to_slot = doaction[3]
atom_key = self.entropyTools.dep_getkey(atom)
category = atom_key.split("/")[0]
matches = self.atomMatch(atom, matchSlot = from_slot, multiMatch = True)
found = False
if matches[1] == 0:
# found atoms, check category
for idpackage in matches[0]:
myslot = self.retrieveSlot(idpackage)
mycategory = self.retrieveCategory(idpackage)
if mycategory == category:
if (myslot != to_slot) and (action not in new_actions):
new_actions.append(action)
found = True
break
if found:
continue
# if we get here it means found == False
# search into dependencies
dep_atoms = self.searchDependency(atom_key, like = True, multi = True, strings = True)
dep_atoms = [x for x in dep_atoms if x.endswith(":"+from_slot) and self.entropyTools.dep_getkey(x) == atom_key]
if dep_atoms:
new_actions.append(action)
elif doaction[0] == "move":
atom = doaction[1] # usually a key
atom_key = self.entropyTools.dep_getkey(atom)
category = atom_key.split("/")[0]
matches = self.atomMatch(atom, multiMatch = True)
found = False
if matches[1] == 0:
for idpackage in matches[0]:
mycategory = self.retrieveCategory(idpackage)
if (mycategory == category) and (action not in new_actions):
new_actions.append(action)
found = True
break
if found:
continue
# if we get here it means found == False
# search into dependencies
dep_atoms = self.searchDependency(atom_key, like = True, multi = True, strings = True)
dep_atoms = [x for x in dep_atoms if self.entropyTools.dep_getkey(x) == atom_key]
if dep_atoms:
new_actions.append(action)
return new_actions
# this is the place to add extra actions support
def runTreeUpdatesActions(self, actions):
# just run fixpackages if gentoo-compat is enabled
if etpConst['gentoo-compat']:
mytxt = "%s: %s, %s." % (
bold(_("SPM")),
blue(_("Running fixpackages")),
red(_("it could take a while")),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
if self.clientDatabase:
try:
Spm = self.ServiceInterface.Spm()
Spm.run_fixpackages()
except:
pass
else:
self.ServiceInterface.SpmService.run_fixpackages()
spm_moves = set()
quickpkg_atoms = set()
for action in actions:
command = action.split()
mytxt = "%s: %s: %s." % (
bold(_("ENTROPY")),
red(_("action")),
blue(action),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
if command[0] == "move":
spm_moves.add(action)
quickpkg_atoms |= self.runTreeUpdatesMoveAction(command[1:], quickpkg_atoms)
elif command[0] == "slotmove":
quickpkg_atoms |= self.runTreeUpdatesSlotmoveAction(command[1:], quickpkg_atoms)
if quickpkg_atoms and not self.clientDatabase:
# quickpkg package and packages owning it as a dependency
try:
self.runTreeUpdatesQuickpkgAction(quickpkg_atoms)
except:
self.entropyTools.printTraceback()
mytxt = "%s: %s: %s, %s." % (
bold(_("WARNING")),
red(_("Cannot complete quickpkg for atoms")),
blue(str(list(quickpkg_atoms))),
_("do it manually"),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
self.commitChanges()
if spm_moves:
try:
self.doTreeupdatesSpmCleanup(spm_moves)
except Exception, e:
mytxt = "%s: %s: %s, %s." % (
bold(_("WARNING")),
red(_("Cannot run SPM cleanup, error")),
Exception,
e,
)
self.entropyTools.printTraceback()
# discard cache
self.clearCache()
# -- move action:
# 1) move package key to the new name: category + name + atom
# 2) update all the dependencies in dependenciesreference to the new key
# 3) run fixpackages which will update /var/db/pkg files
# 4) automatically run quickpkg() to build the new binary and
# tainted binaries owning tainted iddependency and taint database
def runTreeUpdatesMoveAction(self, move_command, quickpkg_queue):
key_from = move_command[0]
key_to = move_command[1]
cat_to = key_to.split("/")[0]
name_to = key_to.split("/")[1]
matches = self.atomMatch(key_from, multiMatch = True)
iddependencies_idpackages = set()
if matches[1] == 0:
for idpackage in matches[0]:
slot = self.retrieveSlot(idpackage)
old_atom = self.retrieveAtom(idpackage)
new_atom = old_atom.replace(key_from,key_to)
### UPDATE DATABASE
# update category
self.setCategory(idpackage, cat_to)
# update name
self.setName(idpackage, name_to)
# update atom
self.setAtom(idpackage, new_atom)
# look for packages we need to quickpkg again
# note: quickpkg_queue is simply ignored if self.clientDatabase
quickpkg_queue.add(key_to+":"+str(slot))
if not self.clientDatabase:
# check for injection and warn the developer
injected = self.isInjected(idpackage)
if injected:
mytxt = "%s: %s %s. %s !!! %s." % (
bold(_("INJECT")),
blue(str(new_atom)),
red(_("has been injected")),
red(_("You need to quickpkg it manually to update the embedded database")),
red(_("Repository database will be updated anyway")),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
iddeps = self.searchDependency(key_from, like = True, multi = True)
for iddep in iddeps:
# update string
mydep = self.retrieveDependencyFromIddependency(iddep)
mydep_key = self.entropyTools.dep_getkey(mydep)
if mydep_key != key_from: # avoid changing wrong atoms -> dev-python/qscintilla-python would
continue # become x11-libs/qscintilla if we don't do this check
mydep = mydep.replace(key_from,key_to)
# now update
# dependstable on server is always re-generated
self.setDependency(iddep, mydep)
# we have to repackage also package owning this iddep
iddependencies_idpackages |= self.searchIdpackageFromIddependency(iddep)
self.commitChanges()
quickpkg_queue = list(quickpkg_queue)
for x in range(len(quickpkg_queue)):
myatom = quickpkg_queue[x]
myatom = myatom.replace(key_from,key_to)
quickpkg_queue[x] = myatom
quickpkg_queue = set(quickpkg_queue)
for idpackage_owner in iddependencies_idpackages:
myatom = self.retrieveAtom(idpackage_owner)
myatom = myatom.replace(key_from,key_to)
quickpkg_queue.add(myatom)
return quickpkg_queue
# -- slotmove action:
# 1) move package slot
# 2) update all the dependencies in dependenciesreference owning same matched atom + slot
# 3) run fixpackages which will update /var/db/pkg files
# 4) automatically run quickpkg() to build the new binary and tainted binaries owning tainted iddependency and taint database
def runTreeUpdatesSlotmoveAction(self, slotmove_command, quickpkg_queue):
atom = slotmove_command[0]
atomkey = self.entropyTools.dep_getkey(atom)
slot_from = slotmove_command[1]
slot_to = slotmove_command[2]
matches = self.atomMatch(atom, multiMatch = True)
iddependencies_idpackages = set()
if matches[1] == 0:
matched_idpackages = matches[0]
for idpackage in matched_idpackages:
### UPDATE DATABASE
# update slot
self.setSlot(idpackage, slot_to)
# look for packages we need to quickpkg again
# note: quickpkg_queue is simply ignored if self.clientDatabase
quickpkg_queue.add(atom+":"+str(slot_to))
if not self.clientDatabase:
# check for injection and warn the developer
injected = self.isInjected(idpackage)
if injected:
mytxt = "%s: %s %s. %s !!! %s." % (
bold(_("INJECT")),
blue(str(atom)),
red(_("has been injected")),
red(_("You need to quickpkg it manually to update the embedded database")),
red(_("Repository database will be updated anyway")),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
# only if we've found VALID matches !
iddeps = self.searchDependency(atomkey, like = True, multi = True)
for iddep in iddeps:
# update string
mydep = self.retrieveDependencyFromIddependency(iddep)
mydep_key = self.entropyTools.dep_getkey(mydep)
if mydep_key != atomkey:
continue
if not mydep.endswith(":"+slot_from): # probably slotted dep
continue
mydep_match = self.atomMatch(mydep)
if mydep_match not in matched_idpackages:
continue
mydep = mydep.replace(":"+slot_from,":"+slot_to)
# now update
# dependstable on server is always re-generated
self.setDependency(iddep, mydep)
# we have to repackage also package owning this iddep
iddependencies_idpackages |= self.searchIdpackageFromIddependency(iddep)
self.commitChanges()
for idpackage_owner in iddependencies_idpackages:
myatom = self.retrieveAtom(idpackage_owner)
quickpkg_queue.add(myatom)
return quickpkg_queue
def runTreeUpdatesQuickpkgAction(self, atoms):
self.commitChanges()
package_paths = set()
runatoms = set()
for myatom in atoms:
mymatch = self.atomMatch(myatom)
if mymatch[0] == -1:
continue
myatom = self.retrieveAtom(mymatch[0])
runatoms.add(myatom)
for myatom in runatoms:
self.updateProgress(
red("%s: " % (_("repackaging"),) )+blue(myatom),
importance = 1,
type = "warning",
header = blue(" # ")
)
mydest = self.ServiceInterface.get_local_store_directory(self.server_repo)
try:
mypath = self.ServiceInterface.quickpkg(myatom,mydest)
except:
# remove broken bin before raising
mypath = os.path.join(mydest,os.path.basename(myatom)+etpConst['packagesext'])
if os.path.isfile(mypath):
os.remove(mypath)
self.entropyTools.printTraceback()
mytxt = "%s: %s: %s, %s." % (
bold(_("WARNING")),
red(_("Cannot complete quickpkg for atom")),
blue(myatom),
_("do it manually"),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
continue
package_paths.add(mypath)
packages_data = [(x,False) for x in package_paths]
idpackages = self.ServiceInterface.add_packages_to_repository(packages_data, repo = self.server_repo)
if not idpackages:
mytxt = "%s: %s. %s." % (
bold(_("ATTENTION")),
red(_("runTreeUpdatesQuickpkgAction did not run properly")),
red(_("Please update packages manually")),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
def doTreeupdatesSpmCleanup(self, spm_moves):
# now erase Spm entries if necessary
for action in spm_moves:
command = action.split()
if len(command) < 2:
continue
key = command[1]
name = key.split("/")[1]
if self.clientDatabase:
try:
Spm = self.ServiceInterface.Spm()
except:
continue
else:
Spm = self.ServiceInterface.SpmService
vdb_path = Spm.get_vdb_path()
pkg_path = os.path.join(vdb_path,key.split("/")[0])
try:
mydirs = [os.path.join(pkg_path,x) for x in os.listdir(pkg_path) if x.startswith(name)]
except OSError: # no dir, no party!
continue
mydirs = [x for x in mydirs if os.path.isdir(x)]
# now move these dirs
for mydir in mydirs:
to_path = os.path.join(etpConst['packagestmpdir'],os.path.basename(mydir))
mytxt = "%s: %s '%s' %s '%s'" % (
bold(_("SPM")),
red(_("Moving old entry")),
blue(mydir),
red(_("to")),
blue(to_path),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning",
header = darkred(" * ")
)
if os.path.isdir(to_path):
shutil.rmtree(to_path,True)
try:
os.rmdir(to_path)
except OSError:
pass
shutil.move(mydir,to_path)
def handlePackage(self, etpData, forcedRevision = -1, formattedContent = False):
self.checkReadOnly()
if self.clientDatabase:
return self.addPackage(etpData, revision = forcedRevision, formatted_content = formattedContent)
# build atom string, server side
pkgatom = self.entropyTools.create_package_atom_string(etpData['category'], etpData['name'], etpData['version'], etpData['versiontag'])
foundid = self.isPackageAvailable(pkgatom)
if foundid < 0: # same atom doesn't exist in any branch
return self.addPackage(etpData, revision = forcedRevision, formatted_content = formattedContent)
idpackage = self.getIDPackage(pkgatom)
curRevision = forcedRevision
if forcedRevision == -1:
curRevision = 0
if idpackage != -1:
curRevision = self.retrieveRevision(idpackage)
if idpackage != -1: # remove old package atom, we do it here because othersie
# injected packages wouldn't be removed by addPackages
self.removePackage(idpackage)
if forcedRevision == -1: curRevision += 1
# add the new one
return self.addPackage(etpData, revision = curRevision, formatted_content = formattedContent)
def retrieve_packages_to_remove(self, name, category, slot, injected):
removelist = set()
searchsimilar = self.searchPackagesByNameAndCategory(
name = name,
category = category,
sensitive = True
)
if not injected:
# read: if package has been injected, we'll skip
# the removal of packages in the same slot, usually used server side btw
for oldpkg in searchsimilar:
# get the package slot
idpackage = oldpkg[1]
myslot = self.retrieveSlot(idpackage)
# we merely ignore packages with
# negative counters, since they're the injected ones
if self.isInjected(idpackage): continue
if slot == myslot:
# remove!
removelist.add(idpackage)
return removelist
def addPackage(self, etpData, revision = -1, idpackage = None, do_remove = True, do_commit = True, formatted_content = False):
self.checkReadOnly()
self.live_cache.clear()
if revision == -1:
try:
revision = int(etpData['revision'])
except (KeyError, ValueError):
etpData['revision'] = 0 # revision not specified
revision = 0
elif not etpData.has_key('revision'):
etpData['revision'] = revision
if do_remove:
removelist = self.retrieve_packages_to_remove(
etpData['name'],
etpData['category'],
etpData['slot'],
etpData['injected']
)
for pkg in removelist:
self.removePackage(pkg)
### create new ids
# create new category if it doesn't exist
catid = self.isCategoryAvailable(etpData['category'])
if catid == -1: catid = self.addCategory(etpData['category'])
# create new license if it doesn't exist
licid = self.isLicenseAvailable(etpData['license'])
if licid == -1: licid = self.addLicense(etpData['license'])
idprotect = self.isProtectAvailable(etpData['config_protect'])
if idprotect == -1: idprotect = self.addProtect(etpData['config_protect'])
idprotect_mask = self.isProtectAvailable(etpData['config_protect_mask'])
if idprotect_mask == -1: idprotect_mask = self.addProtect(etpData['config_protect_mask'])
idflags = self.areCompileFlagsAvailable(etpData['chost'],etpData['cflags'],etpData['cxxflags'])
if idflags == -1: idflags = self.addCompileFlags(etpData['chost'],etpData['cflags'],etpData['cxxflags'])
trigger = 0
if etpData['trigger']:
trigger = 1
# baseinfo
pkgatom = self.entropyTools.create_package_atom_string(etpData['category'], etpData['name'], etpData['version'], etpData['versiontag'])
mybaseinfo_data = [
pkgatom,
catid,
etpData['name'],
etpData['version'],
etpData['versiontag'],
revision,
etpData['branch'],
etpData['slot'],
licid,
etpData['etpapi'],
trigger
]
myidpackage_string = 'NULL'
if isinstance(idpackage,int):
# does it exist?
self.removePackage(idpackage, do_cleanup = False, do_commit = False, do_rss = False)
myidpackage_string = '?'
mybaseinfo_data.insert(0,idpackage)
else:
idpackage = None
with self.WriteLock:
self.cursor.execute(
'INSERT into baseinfo VALUES '
'('+myidpackage_string+',?,?,?,?,?,?,?,?,?,?,?)'
, mybaseinfo_data
)
if idpackage == None:
idpackage = self.cursor.lastrowid
# extrainfo
self.cursor.execute(
'INSERT into extrainfo VALUES '
'(?,?,?,?,?,?,?,?)'
, ( idpackage,
etpData['description'],
etpData['homepage'],
etpData['download'],
etpData['size'],
idflags,
etpData['digest'],
etpData['datecreation'],
)
)
### other information iserted below are not as critical as these above
# tables using a select
self.insertEclasses(idpackage, etpData['eclasses'])
self.insertNeeded(idpackage, etpData['needed'])
self.insertDependencies(idpackage, etpData['dependencies'])
self.insertSources(idpackage, etpData['sources'])
self.insertUseflags(idpackage, etpData['useflags'])
self.insertKeywords(idpackage, etpData['keywords'])
self.insertLicenses(etpData['licensedata'])
self.insertMirrors(etpData['mirrorlinks'])
# not depending on other tables == no select done
self.insertContent(idpackage, etpData['content'], already_formatted = formatted_content)
etpData['counter'] = int(etpData['counter']) # cast to integer
etpData['counter'] = self.insertPortageCounter(
idpackage,
etpData['counter'],
etpData['branch'],
etpData['injected']
)
self.insertOnDiskSize(idpackage, etpData['disksize'])
self.insertTrigger(idpackage, etpData['trigger'])
self.insertConflicts(idpackage, etpData['conflicts'])
self.insertProvide(idpackage, etpData['provide'])
self.insertMessages(idpackage, etpData['messages'])
self.insertConfigProtect(idpackage, idprotect)
self.insertConfigProtect(idpackage, idprotect_mask, mask = True)
# injected?
if etpData.get('injected'):
self.setInjected(idpackage, do_commit = False)
# is it a system package?
if etpData.get('systempackage'):
self.setSystemPackage(idpackage, do_commit = False)
self.clearCache()
if do_commit:
self.commitChanges()
### RSS Atom support
### dictionary will be elaborated by activator
if etpConst['rss-feed'] and not self.clientDatabase:
self._write_rss_for_added_package(pkgatom, revision, etpData['description'], etpData['homepage'])
# Update category description
if not self.clientDatabase:
mycategory = etpData['category']
descdata = {}
try:
descdata = self.get_category_description_from_disk(mycategory)
except (IOError,OSError,EOFError):
pass
if descdata:
self.setCategoryDescription(mycategory,descdata)
return idpackage, revision, etpData
def _write_rss_for_added_package(self, pkgatom, revision, description, homepage):
rssAtom = pkgatom+"~"+str(revision)
rssObj = self.dumpTools.loadobj(etpConst['rss-dump-name'])
if rssObj: self.ServiceInterface.rssMessages = rssObj.copy()
if not isinstance(self.ServiceInterface.rssMessages,dict):
self.ServiceInterface.rssMessages = {}
if not self.ServiceInterface.rssMessages.has_key('added'):
self.ServiceInterface.rssMessages['added'] = {}
if not self.ServiceInterface.rssMessages.has_key('removed'):
self.ServiceInterface.rssMessages['removed'] = {}
if rssAtom in self.ServiceInterface.rssMessages['removed']:
del self.ServiceInterface.rssMessages['removed'][rssAtom]
self.ServiceInterface.rssMessages['added'][rssAtom] = {}
self.ServiceInterface.rssMessages['added'][rssAtom]['description'] = description
self.ServiceInterface.rssMessages['added'][rssAtom]['homepage'] = homepage
self.ServiceInterface.rssMessages['light'][rssAtom] = {}
self.ServiceInterface.rssMessages['light'][rssAtom]['description'] = description
self.dumpTools.dumpobj(etpConst['rss-dump-name'],self.ServiceInterface.rssMessages)
def _write_rss_for_removed_package(self, idpackage):
rssObj = self.dumpTools.loadobj(etpConst['rss-dump-name'])
if rssObj: self.ServiceInterface.rssMessages = rssObj.copy()
rssAtom = self.retrieveAtom(idpackage)
rssRevision = self.retrieveRevision(idpackage)
rssAtom += "~"+str(rssRevision)
if not isinstance(self.ServiceInterface.rssMessages,dict):
self.ServiceInterface.rssMessages = {}
if not self.ServiceInterface.rssMessages.has_key('added'):
self.ServiceInterface.rssMessages['added'] = {}
if not self.ServiceInterface.rssMessages.has_key('removed'):
self.ServiceInterface.rssMessages['removed'] = {}
if rssAtom in self.ServiceInterface.rssMessages['added']:
del self.ServiceInterface.rssMessages['added'][rssAtom]
self.ServiceInterface.rssMessages['removed'][rssAtom] = {}
try:
self.ServiceInterface.rssMessages['removed'][rssAtom]['description'] = self.retrieveDescription(idpackage)
except:
self.ServiceInterface.rssMessages['removed'][rssAtom]['description'] = "N/A"
try:
self.ServiceInterface.rssMessages['removed'][rssAtom]['homepage'] = self.retrieveHomepage(idpackage)
except:
self.ServiceInterface.rssMessages['removed'][rssAtom]['homepage'] = ""
self.dumpTools.dumpobj(etpConst['rss-dump-name'],self.ServiceInterface.rssMessages)
def removePackage(self, idpackage, do_cleanup = True, do_commit = True, do_rss = True):
self.checkReadOnly()
self.live_cache.clear()
### RSS Atom support
### dictionary will be elaborated by activator
if etpConst['rss-feed'] and (not self.clientDatabase) and do_rss:
# store addPackage action
self._write_rss_for_removed_package(idpackage)
with self.WriteLock:
r_tup = (idpackage,)*20
self.cursor.executescript("""
DELETE FROM baseinfo WHERE idpackage = %d;
DELETE FROM extrainfo WHERE idpackage = %d;
DELETE FROM dependencies WHERE idpackage = %d;
DELETE FROM provide WHERE idpackage = %d;
DELETE FROM conflicts WHERE idpackage = %d;
DELETE FROM configprotect WHERE idpackage = %d;
DELETE FROM configprotectmask WHERE idpackage = %d;
DELETE FROM sources WHERE idpackage = %d;
DELETE FROM useflags WHERE idpackage = %d;
DELETE FROM keywords WHERE idpackage = %d;
DELETE FROM content WHERE idpackage = %d;
DELETE FROM messages WHERE idpackage = %d;
DELETE FROM counters WHERE idpackage = %d;
DELETE FROM sizes WHERE idpackage = %d;
DELETE FROM eclasses WHERE idpackage = %d;
DELETE FROM needed WHERE idpackage = %d;
DELETE FROM triggers WHERE idpackage = %d;
DELETE FROM systempackages WHERE idpackage = %d;
DELETE FROM injected WHERE idpackage = %d;
DELETE FROM installedtable WHERE idpackage = %d;
""" % r_tup)
# Remove from dependstable if exists
self.removePackageFromDependsTable(idpackage)
if do_cleanup:
# Cleanups if at least one package has been removed
self.doCleanups()
# clear caches
self.clearCache()
if do_commit:
self.commitChanges()
def removeMirrorEntries(self,mirrorname):
with self.WriteLock:
self.cursor.execute('DELETE FROM mirrorlinks WHERE mirrorname = (?)',(mirrorname,))
def addMirrors(self,mirrorname,mirrorlist):
with self.WriteLock:
for x in mirrorlist:
self.cursor.execute(
'INSERT into mirrorlinks VALUES '
'(?,?)', (mirrorname,x,)
)
def addCategory(self,category):
with self.WriteLock:
self.cursor.execute(
'INSERT into categories VALUES '
'(NULL,?)', (category,)
)
return self.cursor.lastrowid
def addProtect(self,protect):
with self.WriteLock:
self.cursor.execute(
'INSERT into configprotectreference VALUES '
'(NULL,?)', (protect,)
)
return self.cursor.lastrowid
def addSource(self,source):
with self.WriteLock:
self.cursor.execute(
'INSERT into sourcesreference VALUES '
'(NULL,?)', (source,)
)
return self.cursor.lastrowid
def addDependency(self,dependency):
with self.WriteLock:
self.cursor.execute(
'INSERT into dependenciesreference VALUES '
'(NULL,?)', (dependency,)
)
return self.cursor.lastrowid
def addKeyword(self,keyword):
with self.WriteLock:
self.cursor.execute(
'INSERT into keywordsreference VALUES '
'(NULL,?)', (keyword,)
)
return self.cursor.lastrowid
def addUseflag(self,useflag):
with self.WriteLock:
self.cursor.execute(
'INSERT into useflagsreference VALUES '
'(NULL,?)', (useflag,)
)
return self.cursor.lastrowid
def addEclass(self,eclass):
with self.WriteLock:
self.cursor.execute(
'INSERT into eclassesreference VALUES '
'(NULL,?)', (eclass,)
)
return self.cursor.lastrowid
def addNeeded(self,needed):
with self.WriteLock:
self.cursor.execute(
'INSERT into neededreference VALUES '
'(NULL,?)', (needed,)
)
return self.cursor.lastrowid
def addLicense(self,pkglicense):
if not self.entropyTools.is_valid_string(pkglicense):
pkglicense = ' ' # workaround for broken license entries
with self.WriteLock:
self.cursor.execute(
'INSERT into licenses VALUES '
'(NULL,?)', (pkglicense,)
)
return self.cursor.lastrowid
def addCompileFlags(self,chost,cflags,cxxflags):
with self.WriteLock:
self.cursor.execute(
'INSERT into flags VALUES '
'(NULL,?,?,?)', (chost,cflags,cxxflags,)
)
return self.cursor.lastrowid
def setSystemPackage(self, idpackage, do_commit = True):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('INSERT into systempackages VALUES (?)', (idpackage,))
if do_commit:
self.commitChanges()
def setInjected(self, idpackage, do_commit = True):
self.checkReadOnly()
with self.WriteLock:
if not self.isInjected(idpackage):
self.cursor.execute('INSERT into injected VALUES (?)', (idpackage,))
if do_commit:
self.commitChanges()
# date expressed the unix way
def setDateCreation(self, idpackage, date):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE extrainfo SET datecreation = (?) WHERE idpackage = (?)', (str(date),idpackage,))
self.commitChanges()
def setDigest(self, idpackage, digest):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE extrainfo SET digest = (?) WHERE idpackage = (?)', (digest,idpackage,))
self.commitChanges()
def setDownloadURL(self, idpackage, url):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE extrainfo SET download = (?) WHERE idpackage = (?)', (url,idpackage,))
self.commitChanges()
def setCategory(self, idpackage, category):
self.checkReadOnly()
# create new category if it doesn't exist
catid = self.isCategoryAvailable(category)
if (catid == -1):
# create category
catid = self.addCategory(category)
with self.WriteLock:
self.cursor.execute('UPDATE baseinfo SET idcategory = (?) WHERE idpackage = (?)', (catid,idpackage,))
self.commitChanges()
def setCategoryDescription(self, category, description_data):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM categoriesdescription WHERE category = (?)', (category,))
for locale in description_data:
mydesc = description_data[locale]
#if type(mydesc) is unicode:
# mydesc = mydesc.encode('raw_unicode_escape')
self.cursor.execute('INSERT INTO categoriesdescription VALUES (?,?,?)', (category,locale,mydesc,))
self.commitChanges()
def setName(self, idpackage, name):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE baseinfo SET name = (?) WHERE idpackage = (?)', (name,idpackage,))
self.commitChanges()
def setDependency(self, iddependency, dependency):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE dependenciesreference SET dependency = (?) WHERE iddependency = (?)', (dependency,iddependency,))
self.commitChanges()
def setAtom(self, idpackage, atom):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE baseinfo SET atom = (?) WHERE idpackage = (?)', (atom,idpackage,))
self.commitChanges()
def setSlot(self, idpackage, slot):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE baseinfo SET slot = (?) WHERE idpackage = (?)', (slot,idpackage,))
self.commitChanges()
def removeLicensedata(self, license_name):
if not self.doesTableExist("licensedata"):
return
with self.WriteLock:
self.cursor.execute('DELETE FROM licensedata WHERE licensename = (?)', (license_name,))
def removeDependencies(self, idpackage):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute("DELETE FROM dependencies WHERE idpackage = (?)", (idpackage,))
self.commitChanges()
def insertDependencies(self, idpackage, depdata):
dcache = set()
add_dep = self.addDependency
is_dep_avail = self.isDependencyAvailable
def mymf(dep):
if dep in dcache: return 0
iddep = is_dep_avail(dep)
if iddep == -1: iddep = add_dep(dep)
deptype = 0
if isinstance(depdata,dict):
deptype = depdata[dep]
dcache.add(dep)
return (idpackage,iddep,deptype,)
# do not place inside the with statement, otherwise there'll be an obvious lockup
deps = [x for x in map(mymf,depdata) if type(x) != int]
with self.WriteLock:
self.cursor.executemany('INSERT into dependencies VALUES (?,?,?)', deps)
def removeContent(self, idpackage):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute("DELETE FROM content WHERE idpackage = (?)", (idpackage,))
self.commitChanges()
def insertContent(self, idpackage, content, already_formatted = False):
with self.WriteLock:
if already_formatted:
self.cursor.executemany('INSERT INTO content VALUES (?,?,?)',[(idpackage,b,c,) for a,b,c in content])
return
do_encode = [1 for x in content if type(x) is unicode]
def my_cmap(xfile):
contenttype = content[xfile]
if do_encode: xfile = xfile.encode('raw_unicode_escape')
return (idpackage,xfile,contenttype,)
self.cursor.executemany('INSERT INTO content VALUES (?,?,?)',map(my_cmap,content))
def insertLicenses(self, licenses_data):
mylicenses = licenses_data.keys()
is_lic_avail = self.isLicensedataKeyAvailable
def my_mf(mylicense):
return not is_lic_avail(mylicense)
def my_mm(mylicense):
return (mylicense,buffer(licenses_data.get(mylicense)),0,)
with self.WriteLock:
self.cursor.executemany('INSERT into licensedata VALUES (?,?,?)',map(my_mm,list(set(filter(my_mf,mylicenses)))))
def insertConfigProtect(self, idpackage, idprotect, mask = False):
mytable = 'configprotect'
if mask: mytable += 'mask'
with self.WriteLock:
self.cursor.execute('INSERT into %s VALUES (?,?)' % (mytable,), (idpackage,idprotect,))
def insertMirrors(self, mirrors):
for mirrorname,mirrorlist in mirrors:
# remove old
self.removeMirrorEntries(mirrorname)
# add new
self.addMirrors(mirrorname,mirrorlist)
def insertKeywords(self, idpackage, keywords):
mydata = set()
for key in keywords:
idkeyword = self.isKeywordAvailable(key)
if (idkeyword == -1):
# create category
idkeyword = self.addKeyword(key)
mydata.add((idpackage,idkeyword,))
with self.WriteLock:
self.cursor.executemany('INSERT into keywords VALUES (?,?)',mydata)
def insertUseflags(self, idpackage, useflags):
mydata = set()
for flag in useflags:
iduseflag = self.isUseflagAvailable(flag)
if (iduseflag == -1):
# create category
iduseflag = self.addUseflag(flag)
mydata.add((idpackage,iduseflag,))
with self.WriteLock:
self.cursor.executemany('INSERT into useflags VALUES (?,?)',mydata)
def insertSources(self, idpackage, sources):
mydata = set()
for source in sources:
if (not source) or (source == "") or (not self.entropyTools.is_valid_string(source)):
continue
idsource = self.isSourceAvailable(source)
if (idsource == -1):
# create category
idsource = self.addSource(source)
mydata.add((idpackage,idsource,))
with self.WriteLock:
self.cursor.executemany('INSERT into sources VALUES (?,?)',mydata)
def insertConflicts(self, idpackage, conflicts):
def myiter():
for conflict in conflicts:
yield (idpackage,conflict,)
with self.WriteLock:
self.cursor.executemany('INSERT into conflicts VALUES (?,?)',myiter())
def insertMessages(self, idpackage, messages):
def myiter():
for message in messages:
yield (idpackage,message,)
with self.WriteLock:
self.cursor.executemany('INSERT into messages VALUES (?,?)',myiter())
def insertProvide(self, idpackage, provides):
def myiter():
for atom in provides:
yield (idpackage,atom,)
with self.WriteLock:
self.cursor.executemany('INSERT into provide VALUES (?,?)',myiter())
def insertNeeded(self, idpackage, neededs):
mydata = set()
for needed,elfclass in neededs:
idneeded = self.isNeededAvailable(needed)
if idneeded == -1:
# create eclass
idneeded = self.addNeeded(needed)
mydata.add((idpackage,idneeded,elfclass))
with self.WriteLock:
self.cursor.executemany('INSERT into needed VALUES (?,?,?)',mydata)
def insertEclasses(self, idpackage, eclasses):
mydata = set()
for eclass in eclasses:
idclass = self.isEclassAvailable(eclass)
if (idclass == -1):
# create eclass
idclass = self.addEclass(eclass)
mydata.add((idpackage,idclass))
with self.WriteLock:
self.cursor.executemany('INSERT into eclasses VALUES (?,?)',mydata)
def insertOnDiskSize(self, idpackage, mysize):
with self.WriteLock:
self.cursor.execute('INSERT into sizes VALUES (?,?)', (idpackage,mysize,))
def insertTrigger(self, idpackage, trigger):
with self.WriteLock:
self.cursor.execute('INSERT into triggers VALUES (?,?)', (idpackage,buffer(trigger),))
def insertPortageCounter(self, idpackage, counter, branch, injected):
if (counter != -1) and not injected:
if counter <= -2:
# special cases
counter = self.getNewNegativeCounter()
with self.WriteLock:
try:
self.cursor.execute(
'INSERT into counters VALUES '
'(?,?,?)'
, ( counter,
idpackage,
branch,
)
)
except self.dbapi2.IntegrityError: # we have a PRIMARY KEY we need to remove
self._migrateCountersTable()
self.cursor.execute(
'INSERT into counters VALUES '
'(?,?,?)'
, ( counter,
idpackage,
branch,
)
)
except:
if self.dbname == etpConst['clientdbid']: # force only for client database
if self.doesTableExist("counters"):
raise
self.cursor.execute(
'INSERT into counters VALUES '
'(?,?,?)'
, ( counter,
idpackage,
branch,
)
)
elif self.dbname.startswith(etpConst['serverdbid']):
raise
return counter
def insertCounter(self, idpackage, counter, branch = None):
self.checkReadOnly()
if not branch: branch = self.db_branch
if not branch: branch = etpConst['branch']
with self.WriteLock:
self.cursor.execute('DELETE FROM counters WHERE (counter = (?) OR idpackage = (?)) AND branch = (?)', (counter,idpackage,branch,))
self.cursor.execute('INSERT INTO counters VALUES (?,?,?)', (counter,idpackage,branch,))
self.commitChanges()
def setTrashedCounter(self, counter):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM trashedcounters WHERE counter = (?)', (counter,))
self.cursor.execute('INSERT INTO trashedcounters VALUES (?)', (counter,))
self.commitChanges()
def setCounter(self, idpackage, counter, branch = None):
self.checkReadOnly()
branchstring = ''
insertdata = [counter,idpackage]
if branch:
branchstring = ', branch = (?)'
insertdata.insert(1,branch)
with self.WriteLock:
try:
self.cursor.execute('UPDATE counters SET counter = (?) '+branchstring+' WHERE idpackage = (?)', insertdata)
self.commitChanges()
except:
if self.dbname == etpConst['clientdbid']:
raise
def contentDiff(self, idpackage, dbconn, dbconn_idpackage):
self.checkReadOnly()
self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
# create a random table and fill
randomtable = "cdiff%s" % (self.entropyTools.getRandomNumber(),)
while self.doesTableExist(randomtable):
randomtable = "cdiff%s" % (self.entropyTools.getRandomNumber(),)
self.cursor.execute('CREATE TEMPORARY TABLE %s ( file VARCHAR )' % (randomtable,))
try:
dbconn.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
dbconn.cursor.execute('select file from content where idpackage = (?)', (dbconn_idpackage,))
xfile = dbconn.cursor.fetchone()
while xfile:
self.cursor.execute('INSERT INTO %s VALUES (?)' % (randomtable,), (xfile[0],))
xfile = dbconn.cursor.fetchone()
# now compare
self.cursor.execute('SELECT file FROM content WHERE content.idpackage = (?) AND content.file NOT IN (SELECT file from %s)' % (randomtable,), (idpackage,))
diff = self.fetchall2set(self.cursor.fetchall())
return diff
finally:
self.cursor.execute('DROP TABLE IF EXISTS %s' % (randomtable,))
def doCleanups(self):
self.cleanupUseflags()
self.cleanupSources()
self.cleanupEclasses()
self.cleanupNeeded()
self.cleanupDependencies()
def cleanupUseflags(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM useflagsreference WHERE idflag NOT IN (SELECT idflag FROM useflags)')
self.commitChanges()
def cleanupSources(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM sourcesreference WHERE idsource NOT IN (SELECT idsource FROM sources)')
self.commitChanges()
def cleanupEclasses(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM eclassesreference WHERE idclass NOT IN (SELECT idclass FROM eclasses)')
self.commitChanges()
def cleanupNeeded(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM neededreference WHERE idneeded NOT IN (SELECT idneeded FROM needed)')
self.commitChanges()
def cleanupDependencies(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM dependenciesreference WHERE iddependency NOT IN (SELECT iddependency FROM dependencies)')
self.commitChanges()
def getNewNegativeCounter(self):
counter = -2
try:
self.cursor.execute('SELECT min(counter) FROM counters')
dbcounter = self.cursor.fetchone()
mycounter = 0
if dbcounter:
mycounter = dbcounter[0]
if mycounter >= -1:
counter = -2
else:
counter = mycounter-1
except:
pass
return counter
def getApi(self):
self.cursor.execute('SELECT max(etpapi) FROM baseinfo')
api = self.cursor.fetchone()
if api: api = api[0]
else: api = -1
return api
def getCategory(self, idcategory):
self.cursor.execute('SELECT category from categories WHERE idcategory = (?)', (idcategory,))
cat = self.cursor.fetchone()
if cat: cat = cat[0]
return cat
def get_category_description_from_disk(self, category):
if not self.ServiceInterface:
return {}
return self.ServiceInterface.SpmService.get_category_description_data(category)
def getIDPackage(self, atom, branch = None):
branch_string = ''
params = [atom]
if branch:
branch_string = ' AND branch = (?)'
params.append(branch)
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE atom = (?)'+branch_string, params)
idpackage = self.cursor.fetchone()
if idpackage: return idpackage[0]
return -1
def getIDPackageFromDownload(self, download_relative_path, endswith = False):
if endswith:
self.cursor.execute('SELECT baseinfo.idpackage FROM baseinfo,extrainfo WHERE extrainfo.download LIKE (?)', ("%"+download_relative_path,))
else:
self.cursor.execute('SELECT baseinfo.idpackage FROM baseinfo,extrainfo WHERE extrainfo.download = (?)', (download_relative_path,))
idpackage = self.cursor.fetchone()
if idpackage: return idpackage[0]
return -1
def getIDPackagesFromFile(self, file):
self.cursor.execute('SELECT idpackage FROM content WHERE file = (?)', (file,))
return self.fetchall2list(self.cursor.fetchall())
def getIDCategory(self, category):
self.cursor.execute('SELECT "idcategory" FROM categories WHERE category = (?)', (category,))
idcat = self.cursor.fetchone()
if idcat: return idcat[0]
return -1
def getVersioningData(self, idpackage):
self.cursor.execute('SELECT version,versiontag,revision FROM baseinfo WHERE idpackage = (?)', (idpackage,))
return self.cursor.fetchone()
def getStrictData(self, idpackage):
self.cursor.execute('SELECT categories.category || "/" || baseinfo.name,baseinfo.slot,baseinfo.version,baseinfo.versiontag,baseinfo.revision,baseinfo.atom FROM baseinfo,categories WHERE baseinfo.idpackage = (?) and baseinfo.idcategory = categories.idcategory', (idpackage,))
return self.cursor.fetchone()
def getStrictScopeData(self, idpackage):
self.cursor.execute("""
SELECT
atom,
slot,
revision
FROM
baseinfo
WHERE
idpackage = (?)
""", (idpackage,))
rslt = self.cursor.fetchone()
return rslt
def getScopeData(self, idpackage):
self.cursor.execute("""
SELECT
baseinfo.atom,
categories.category,
baseinfo.name,
baseinfo.version,
baseinfo.slot,
baseinfo.versiontag,
baseinfo.revision,
baseinfo.branch
FROM
baseinfo,
categories
WHERE
baseinfo.idpackage = (?)
and baseinfo.idcategory = categories.idcategory
""", (idpackage,))
return self.cursor.fetchone()
def getBaseData(self,idpackage):
sql = """
SELECT
baseinfo.atom,
baseinfo.name,
baseinfo.version,
baseinfo.versiontag,
extrainfo.description,
categories.category,
flags.chost,
flags.cflags,
flags.cxxflags,
extrainfo.homepage,
licenses.license,
baseinfo.branch,
extrainfo.download,
extrainfo.digest,
baseinfo.slot,
baseinfo.etpapi,
extrainfo.datecreation,
extrainfo.size,
baseinfo.revision
FROM
baseinfo,
extrainfo,
categories,
flags,
licenses
WHERE
baseinfo.idpackage = (?)
and baseinfo.idpackage = extrainfo.idpackage
and baseinfo.idcategory = categories.idcategory
and extrainfo.idflags = flags.idflags
and baseinfo.idlicense = licenses.idlicense
"""
self.cursor.execute(sql, (idpackage,))
return self.cursor.fetchone()
def getTriggerInfo(self, idpackage):
data = {}
mydata = self.getScopeData(idpackage)
data['atom'] = mydata[0]
data['category'] = mydata[1]
data['name'] = mydata[2]
data['version'] = mydata[3]
data['versiontag'] = mydata[5]
flags = self.retrieveCompileFlags(idpackage)
data['chost'] = flags[0]
data['cflags'] = flags[1]
data['cxxflags'] = flags[2]
data['trigger'] = self.retrieveTrigger(idpackage)
data['eclasses'] = self.retrieveEclasses(idpackage)
data['content'] = self.retrieveContent(idpackage)
return data
def getPackageData(self, idpackage, get_content = True, content_insert_formatted = False, trigger_unicode = False):
data = {}
try:
data['atom'], data['name'], data['version'], data['versiontag'], \
data['description'], data['category'], data['chost'], \
data['cflags'], data['cxxflags'],data['homepage'], \
data['license'], data['branch'], data['download'], \
data['digest'], data['slot'], data['etpapi'], \
data['datecreation'], data['size'], data['revision'] = self.getBaseData(idpackage)
except TypeError:
return None
### risky to add to the sql above
data['counter'] = self.retrieveCounter(idpackage)
data['messages'] = self.retrieveMessages(idpackage)
data['trigger'] = self.retrieveTrigger(idpackage, get_unicode = trigger_unicode)
data['disksize'] = self.retrieveOnDiskSize(idpackage)
data['injected'] = self.isInjected(idpackage)
data['systempackage'] = False
if self.isSystemPackage(idpackage):
data['systempackage'] = True
data['config_protect'] = self.retrieveProtect(idpackage)
data['config_protect_mask'] = self.retrieveProtectMask(idpackage)
data['useflags'] = self.retrieveUseflags(idpackage)
data['keywords'] = self.retrieveKeywords(idpackage)
data['sources'] = self.retrieveSources(idpackage)
data['eclasses'] = self.retrieveEclasses(idpackage)
data['needed'] = self.retrieveNeeded(idpackage, extended = True)
data['provide'] = self.retrieveProvide(idpackage)
data['conflicts'] = self.retrieveConflicts(idpackage)
data['licensedata'] = self.retrieveLicensedata(idpackage)
mirrornames = set()
for x in data['sources']:
if x.startswith("mirror://"):
mirrornames.add(x.split("/")[2])
data['mirrorlinks'] = []
for mirror in mirrornames:
data['mirrorlinks'].append([mirror,self.retrieveMirrorInfo(mirror)])
data['content'] = {}
if get_content:
data['content'] = self.retrieveContent(
idpackage,
extended = True,
formatted = True,
insert_formatted = content_insert_formatted
)
mydeps = {}
depdata = self.retrieveDependencies(idpackage, extended = True)
for dep,deptype in depdata:
mydeps[dep] = deptype
data['dependencies'] = mydeps
return data
def fetchall2set(self, item):
mycontent = set()
for x in item:
mycontent |= set(x)
return mycontent
def fetchall2list(self, item):
content = []
for x in item:
content += list(x)
return content
def fetchone2list(self, item):
return list(item)
def fetchone2set(self, item):
return set(item)
def clearCache(self, depends = False):
self.live_cache.clear()
def do_clear(name):
dump_path = os.path.join(etpConst['dumpstoragedir'],name)
dump_dir = os.path.dirname(dump_path)
if os.path.isdir(dump_dir):
for item in os.listdir(dump_dir):
try: os.remove(os.path.join(dump_dir,item))
except OSError: pass
do_clear("%s/%s/" % (self.dbMatchCacheKey,self.dbname,))
do_clear("%s/%s/" % (self.dbSearchCacheKey,self.dbname,))
if depends:
do_clear(etpCache['depends_tree'])
do_clear(etpCache['dep_tree'])
do_clear(etpCache['filter_satisfied_deps'])
def fetchSearchCache(self, key, function, extra_hash = 0):
if self.xcache:
c_hash = "%s/%s/%s/%s" % (self.dbSearchCacheKey,self.dbname,key,"%s%s" % (hash(function),extra_hash,),)
if self.ServiceInterface: cached = self.ServiceInterface.Cacher.pop(c_hash)
else: cached = self.dumpTools.loadobj(c_hash)
if cached != None: return cached
def storeSearchCache(self, key, function, search_cache_data, extra_hash = 0):
if self.xcache:
c_hash = "%s/%s/%s/%s" % (self.dbSearchCacheKey,self.dbname,key,"%s%s" % (hash(function),extra_hash,),)
if self.ServiceInterface: self.ServiceInterface.Cacher.push(c_hash, search_cache_data.copy())
else: self.dumpTools.dumpobj(c_hash,search_cache_data)
def retrieveRepositoryUpdatesDigest(self, repository):
if not self.doesTableExist("treeupdates"):
return -1
self.cursor.execute('SELECT digest FROM treeupdates WHERE repository = (?)', (repository,))
mydigest = self.cursor.fetchone()
if mydigest:
return mydigest[0]
else:
return -1
def listAllTreeUpdatesActions(self, no_ids_repos = False):
if no_ids_repos:
self.cursor.execute('SELECT command,branch,date FROM treeupdatesactions')
else:
self.cursor.execute('SELECT * FROM treeupdatesactions')
return self.cursor.fetchall()
def retrieveTreeUpdatesActions(self, repository, forbranch = None):
if not self.doesTableExist("treeupdatesactions"): return []
if forbranch == None: forbranch = self.db_branch
params = [repository]
branch_string = ''
if forbranch:
branch_string = 'and branch = (?)'
params.append(forbranch)
self.cursor.execute('SELECT command FROM treeupdatesactions where repository = (?) '+branch_string+' order by date', params)
return self.fetchall2list(self.cursor.fetchall())
# mainly used to restore a previous table, used by reagent in --initialize
def bumpTreeUpdatesActions(self, updates):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM treeupdatesactions')
for update in updates:
self.cursor.execute('INSERT INTO treeupdatesactions VALUES (?,?,?,?,?)', update)
self.commitChanges()
def removeTreeUpdatesActions(self, repository):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM treeupdatesactions WHERE repository = (?)', (repository,))
self.commitChanges()
def insertTreeUpdatesActions(self, updates, repository):
self.checkReadOnly()
with self.WriteLock:
for update in updates:
update = list(update)
update.insert(0,repository)
self.cursor.execute('INSERT INTO treeupdatesactions VALUES (NULL,?,?,?,?)', update)
self.commitChanges()
def setRepositoryUpdatesDigest(self, repository, digest):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('DELETE FROM treeupdates where repository = (?)', (repository,)) # doing it for safety
self.cursor.execute('INSERT INTO treeupdates VALUES (?,?)', (repository,digest,))
self.commitChanges()
def addRepositoryUpdatesActions(self, repository, actions, branch):
self.checkReadOnly()
mytime = str(self.entropyTools.getCurrentUnixTime())
with self.WriteLock:
for command in actions:
if not self.doesTreeupdatesActionExist(repository, command, branch):
self.cursor.execute('INSERT INTO treeupdatesactions VALUES (NULL,?,?,?,?)', (repository,command,branch,mytime,))
self.commitChanges()
def doesTreeupdatesActionExist(self, repository, command, branch):
self.cursor.execute('SELECT * FROM treeupdatesactions WHERE repository = (?) and command = (?) and branch = (?)', (repository,command,branch,))
result = self.cursor.fetchone()
if result:
return True
return False
def clearPackageSets(self):
self.checkReadOnly()
self.cursor.execute('DELETE FROM packagesets')
def insertPackageSets(self, sets_data):
self.checkReadOnly()
mysets = []
for setname in sorted(sets_data.keys()):
for dependency in sorted(sets_data[setname]):
try:
mysets.append((unicode(setname),unicode(dependency),))
except (UnicodeDecodeError,UnicodeEncodeError,):
continue
with self.WriteLock:
self.cursor.executemany('INSERT INTO packagesets VALUES (?,?)', mysets)
def retrievePackageSets(self):
if not self.doesTableExist("packagesets"): return {}
self.cursor.execute('SELECT setname,dependency FROM packagesets')
data = self.cursor.fetchall()
sets = {}
for setname, dependency in data:
if not sets.has_key(setname):
sets[setname] = set()
sets[setname].add(dependency)
return sets
def retrievePackageSet(self, setname):
self.cursor.execute('SELECT dependency FROM packagesets WHERE setname = (?)', (setname,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveSystemPackages(self):
self.cursor.execute('SELECT idpackage FROM systempackages')
return self.fetchall2set(self.cursor.fetchall())
def retrieveAtom(self, idpackage):
self.cursor.execute('SELECT atom FROM baseinfo WHERE idpackage = (?)', (idpackage,))
atom = self.cursor.fetchone()
if atom: return atom[0]
def retrieveBranch(self, idpackage):
self.cursor.execute('SELECT branch FROM baseinfo WHERE idpackage = (?)', (idpackage,))
br = self.cursor.fetchone()
if br: return br[0]
def retrieveTrigger(self, idpackage, get_unicode = False):
self.cursor.execute('SELECT data FROM triggers WHERE idpackage = (?)', (idpackage,))
trigger = self.cursor.fetchone()
if trigger:
trigger = trigger[0]
else:
trigger = ''
if get_unicode:
trigger = unicode(trigger,'raw_unicode_escape')
return trigger
def retrieveDownloadURL(self, idpackage):
self.cursor.execute('SELECT download FROM extrainfo WHERE idpackage = (?)', (idpackage,))
download = self.cursor.fetchone()
if download: return download[0]
def retrieveDescription(self, idpackage):
self.cursor.execute('SELECT description FROM extrainfo WHERE idpackage = (?)', (idpackage,))
description = self.cursor.fetchone()
if description: return description[0]
def retrieveHomepage(self, idpackage):
self.cursor.execute('SELECT homepage FROM extrainfo WHERE idpackage = (?)', (idpackage,))
home = self.cursor.fetchone()
if home: return home[0]
def retrieveCounter(self, idpackage):
counter = -1
self.cursor.execute('SELECT counters.counter FROM counters,baseinfo WHERE counters.idpackage = (?) AND baseinfo.idpackage = counters.idpackage AND baseinfo.branch = counters.branch', (idpackage,))
mycounter = self.cursor.fetchone()
if mycounter: return mycounter[0]
return counter
def retrieveMessages(self, idpackage):
messages = []
try:
self.cursor.execute('SELECT message FROM messages WHERE idpackage = (?)', (idpackage,))
messages = self.fetchall2list(self.cursor.fetchall())
except:
pass
return messages
# in bytes
def retrieveSize(self, idpackage):
self.cursor.execute('SELECT size FROM extrainfo WHERE idpackage = (?)', (idpackage,))
size = self.cursor.fetchone()
if size: return size[0]
# in bytes
def retrieveOnDiskSize(self, idpackage):
self.cursor.execute('SELECT size FROM sizes WHERE idpackage = (?)', (idpackage,))
size = self.cursor.fetchone() # do not use [0]!
if not size: size = 0
else: size = size[0]
return size
def retrieveDigest(self, idpackage):
self.cursor.execute('SELECT digest FROM extrainfo WHERE idpackage = (?)', (idpackage,))
digest = self.cursor.fetchone()
if digest: return digest[0]
def retrieveName(self, idpackage):
self.cursor.execute('SELECT name FROM baseinfo WHERE idpackage = (?)', (idpackage,))
name = self.cursor.fetchone()
if name: return name[0]
def retrieveKeySlot(self, idpackage):
self.cursor.execute('SELECT categories.category || "/" || baseinfo.name,baseinfo.slot FROM baseinfo,categories WHERE baseinfo.idpackage = (?) and baseinfo.idcategory = categories.idcategory', (idpackage,))
data = self.cursor.fetchone()
return data
def retrieveKeySlotTag(self, idpackage):
self.cursor.execute('SELECT categories.category || "/" || baseinfo.name,baseinfo.slot,baseinfo.versiontag FROM baseinfo,categories WHERE baseinfo.idpackage = (?) and baseinfo.idcategory = categories.idcategory', (idpackage,))
data = self.cursor.fetchone()
return data
def retrieveVersion(self, idpackage):
self.cursor.execute('SELECT version FROM baseinfo WHERE idpackage = (?)', (idpackage,))
ver = self.cursor.fetchone()
if ver: return ver[0]
def retrieveRevision(self, idpackage):
self.cursor.execute('SELECT revision FROM baseinfo WHERE idpackage = (?)', (idpackage,))
rev = self.cursor.fetchone()
if rev: return rev[0]
def retrieveDateCreation(self, idpackage):
self.cursor.execute('SELECT datecreation FROM extrainfo WHERE idpackage = (?)', (idpackage,))
date = self.cursor.fetchone()
if date: return date[0]
def retrieveApi(self, idpackage):
self.cursor.execute('SELECT etpapi FROM baseinfo WHERE idpackage = (?)', (idpackage,))
api = self.cursor.fetchone()
if api: return api[0]
def retrieveUseflags(self, idpackage):
self.cursor.execute('SELECT flagname FROM useflags,useflagsreference WHERE useflags.idpackage = (?) and useflags.idflag = useflagsreference.idflag', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveEclasses(self, idpackage):
self.cursor.execute('SELECT classname FROM eclasses,eclassesreference WHERE eclasses.idpackage = (?) and eclasses.idclass = eclassesreference.idclass', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveNeededRaw(self, idpackage):
self.cursor.execute('SELECT library FROM needed,neededreference WHERE needed.idpackage = (?) and needed.idneeded = neededreference.idneeded', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveNeeded(self, idpackage, extended = False, format = False):
if extended:
self.cursor.execute('SELECT library,elfclass FROM needed,neededreference WHERE needed.idpackage = (?) and needed.idneeded = neededreference.idneeded order by library', (idpackage,))
needed = self.cursor.fetchall()
else:
self.cursor.execute('SELECT library FROM needed,neededreference WHERE needed.idpackage = (?) and needed.idneeded = neededreference.idneeded order by library', (idpackage,))
needed = self.fetchall2list(self.cursor.fetchall())
if extended and format:
data = {}
for lib,elfclass in needed:
data[lib] = elfclass
needed = data
return needed
def retrieveConflicts(self, idpackage):
self.cursor.execute('SELECT conflict FROM conflicts WHERE idpackage = (?)', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveProvide(self, idpackage):
self.cursor.execute('SELECT atom FROM provide WHERE idpackage = (?)', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveDependenciesList(self, idpackage):
self.cursor.execute('SELECT dependenciesreference.dependency FROM dependencies,dependenciesreference WHERE dependencies.idpackage = (?) AND dependencies.iddependency = dependenciesreference.iddependency UNION SELECT "!" || conflict FROM conflicts WHERE idpackage = (?)', (idpackage,idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrievePostDependencies(self, idpackage):
self.cursor.execute('SELECT dependenciesreference.dependency FROM dependencies,dependenciesreference WHERE dependencies.idpackage = (?) AND dependencies.iddependency = dependenciesreference.iddependency AND dependencies.type = (?)', (idpackage,etpConst['spm']['pdepend_id'],))
return self.fetchall2set(self.cursor.fetchall())
def retrieveDependencies(self, idpackage, extended = False, deptype = None):
searchdata = [idpackage]
depstring = ''
if deptype != None:
depstring = ' and dependencies.type = (?)'
searchdata.append(deptype)
if extended:
self.cursor.execute('SELECT dependenciesreference.dependency,dependencies.type FROM dependencies,dependenciesreference WHERE dependencies.idpackage = (?) and dependencies.iddependency = dependenciesreference.iddependency'+depstring, searchdata)
deps = self.cursor.fetchall()
else:
self.cursor.execute('SELECT dependenciesreference.dependency FROM dependencies,dependenciesreference WHERE dependencies.idpackage = (?) and dependencies.iddependency = dependenciesreference.iddependency'+depstring, searchdata)
deps = self.fetchall2set(self.cursor.fetchall())
return deps
def retrieveIdDependencies(self, idpackage):
self.cursor.execute('SELECT iddependency FROM dependencies WHERE idpackage = (?)', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveDependencyFromIddependency(self, iddependency):
self.cursor.execute('SELECT dependency FROM dependenciesreference WHERE iddependency = (?)', (iddependency,))
dep = self.cursor.fetchone()
if dep: dep = dep[0]
return dep
def retrieveKeywords(self, idpackage):
self.cursor.execute('SELECT keywordname FROM keywords,keywordsreference WHERE keywords.idpackage = (?) and keywords.idkeyword = keywordsreference.idkeyword', (idpackage,))
return self.fetchall2set(self.cursor.fetchall())
def retrieveProtect(self, idpackage):
self.cursor.execute('SELECT protect FROM configprotect,configprotectreference WHERE configprotect.idpackage = (?) and configprotect.idprotect = configprotectreference.idprotect', (idpackage,))
protect = self.cursor.fetchone()
if not protect: protect = ''
else: protect = protect[0]
return protect
def retrieveProtectMask(self, idpackage):
self.cursor.execute('SELECT protect FROM configprotectmask,configprotectreference WHERE idpackage = (?) and configprotectmask.idprotect= configprotectreference.idprotect', (idpackage,))
protect = self.cursor.fetchone()
if not protect: protect = ''
else: protect = protect[0]
return protect
def retrieveSources(self, idpackage, extended = False):
self.cursor.execute('SELECT sourcesreference.source FROM sources,sourcesreference WHERE idpackage = (?) and sources.idsource = sourcesreference.idsource', (idpackage,))
sources = self.fetchall2set(self.cursor.fetchall())
if not extended:
return sources
source_data = {}
mirror_str = "mirror://"
for source in sources:
source_data[source] = set()
if source.startswith(mirror_str):
mirrorname = source.split("/")[2]
mirror_url = source.split("/",3)[3:][0]
source_data[source] |= set([os.path.join(url,mirror_url) for url in self.retrieveMirrorInfo(mirrorname)])
else:
source_data[source].add(source)
return source_data
def retrieveContent(self, idpackage, extended = False, contentType = None, formatted = False, insert_formatted = False, order_by = ''):
# like portage does
self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
extstring = ''
if extended:
extstring = ",type"
extstring_idpackage = ''
if insert_formatted:
extstring_idpackage = 'idpackage,'
searchkeywords = [idpackage]
contentstring = ''
if contentType:
searchkeywords.append(contentType)
contentstring = ' and type = (?)'
order_by_string = ''
if order_by:
order_by_string = ' order by %s' % (order_by,)
self.cursor.execute('SELECT %s file%s FROM content WHERE idpackage = (?) %s%s' % (extstring_idpackage,extstring,contentstring,order_by_string,), searchkeywords)
if extended and insert_formatted:
fl = self.cursor.fetchall()
elif extended and formatted:
fl = {}
items = self.cursor.fetchone()
while items:
fl[items[0]] = items[1]
items = self.cursor.fetchone()
elif extended:
fl = self.cursor.fetchall()
else:
if order_by:
fl = self.fetchall2list(self.cursor.fetchall())
else:
fl = self.fetchall2set(self.cursor.fetchall())
return fl
def retrieveSlot(self, idpackage):
self.cursor.execute('SELECT slot FROM baseinfo WHERE idpackage = (?)', (idpackage,))
slot = self.cursor.fetchone()
if slot: return slot[0]
def retrieveVersionTag(self, idpackage):
self.cursor.execute('SELECT versiontag FROM baseinfo WHERE idpackage = (?)', (idpackage,))
vtag = self.cursor.fetchone()
if vtag: return vtag[0]
def retrieveMirrorInfo(self, mirrorname):
self.cursor.execute('SELECT mirrorlink FROM mirrorlinks WHERE mirrorname = (?)', (mirrorname,))
mirrorlist = self.fetchall2set(self.cursor.fetchall())
return mirrorlist
def retrieveCategory(self, idpackage):
self.cursor.execute('SELECT category FROM baseinfo,categories WHERE baseinfo.idpackage = (?) and baseinfo.idcategory = categories.idcategory', (idpackage,))
cat = self.cursor.fetchone()
if cat: return cat[0]
def retrieveCategoryDescription(self, category):
data = {}
if not self.doesTableExist("categoriesdescription"):
return data
#self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
self.cursor.execute('SELECT description,locale FROM categoriesdescription WHERE category = (?)', (category,))
description_data = self.cursor.fetchall()
for description,locale in description_data:
data[locale] = description
return data
def retrieveLicensedata(self, idpackage):
# insert license information
if not self.doesTableExist("licensedata"):
return {}
licenses = self.retrieveLicense(idpackage)
if licenses == None:
return {}
licenses = licenses.split()
licdata = {}
for licname in licenses:
licname = licname.strip()
if not self.entropyTools.is_valid_string(licname):
continue
self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
self.cursor.execute('SELECT text FROM licensedata WHERE licensename = (?)', (licname,))
lictext = self.cursor.fetchone()
if lictext != None:
licdata[licname] = str(lictext[0])
return licdata
def retrieveLicensedataKeys(self, idpackage):
if not self.doesTableExist("licensedata"):
return set()
licenses = self.retrieveLicense(idpackage)
if licenses == None:
return set()
licenses = licenses.split()
licdata = set()
for licname in licenses:
licname = licname.strip()
if not self.entropyTools.is_valid_string(licname):
continue
self.cursor.execute('SELECT licensename FROM licensedata WHERE licensename = (?)', (licname,))
licidentifier = self.cursor.fetchone()
if licidentifier:
licdata.add(licidentifier[0])
return licdata
def retrieveLicenseText(self, license_name):
if not self.doesTableExist("licensedata"):
return None
self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
self.cursor.execute('SELECT text FROM licensedata WHERE licensename = (?)', (license_name,))
text = self.cursor.fetchone()
if not text:
return None
return str(text[0])
def retrieveLicense(self, idpackage):
self.cursor.execute('SELECT license FROM baseinfo,licenses WHERE baseinfo.idpackage = (?) and baseinfo.idlicense = licenses.idlicense', (idpackage,))
licname = self.cursor.fetchone()
if licname: return licname[0]
def retrieveCompileFlags(self, idpackage):
self.cursor.execute('SELECT chost,cflags,cxxflags FROM flags,extrainfo WHERE extrainfo.idpackage = (?) and extrainfo.idflags = flags.idflags', (idpackage,))
flags = self.cursor.fetchone()
if not flags:
flags = ("N/A","N/A","N/A")
return flags
def retrieveDepends(self, idpackage, atoms = False, key_slot = False):
# XXX: never remove this, otherwise equo.db (client database) dependstable will be always broken (trust me)
# sanity check on the table
if not self.isDependsTableSane(): # is empty, need generation
self.regenerateDependsTable(output = False)
if atoms:
self.cursor.execute('SELECT baseinfo.atom FROM dependstable,dependencies,baseinfo WHERE dependstable.idpackage = (?) and dependstable.iddependency = dependencies.iddependency and baseinfo.idpackage = dependencies.idpackage', (idpackage,))
result = self.fetchall2set(self.cursor.fetchall())
elif key_slot:
self.cursor.execute('SELECT categories.category || "/" || baseinfo.name,baseinfo.slot FROM baseinfo,categories,dependstable,dependencies WHERE dependstable.idpackage = (?) and dependstable.iddependency = dependencies.iddependency and baseinfo.idpackage = dependencies.idpackage and categories.idcategory = baseinfo.idcategory', (idpackage,))
result = self.cursor.fetchall()
else:
self.cursor.execute('SELECT dependencies.idpackage FROM dependstable,dependencies WHERE dependstable.idpackage = (?) and dependstable.iddependency = dependencies.iddependency', (idpackage,))
result = self.fetchall2set(self.cursor.fetchall())
return result
def retrieveUnusedIdpackages(self):
# XXX: never remove this, otherwise equo.db (client database) dependstable will be always broken (trust me)
# sanity check on the table
if not self.isDependsTableSane(): # is empty, need generation
self.regenerateDependsTable(output = False)
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE idpackage NOT IN (SELECT idpackage FROM dependstable) ORDER BY atom')
return self.fetchall2list(self.cursor.fetchall())
# You must provide the full atom to this function
# WARNING: this function does not support branches
def isPackageAvailable(self,pkgatom):
pkgatom = self.entropyTools.removePackageOperators(pkgatom)
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE atom = (?)', (pkgatom,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isIDPackageAvailable(self,idpackage):
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE idpackage = (?)', (idpackage,))
result = self.cursor.fetchone()
if not result:
return False
return True
def isCategoryAvailable(self,category):
self.cursor.execute('SELECT idcategory FROM categories WHERE category = (?)', (category,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isProtectAvailable(self,protect):
self.cursor.execute('SELECT idprotect FROM configprotectreference WHERE protect = (?)', (protect,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isFileAvailable(self, myfile, get_id = False):
self.cursor.execute('SELECT idpackage FROM content WHERE file = (?)', (myfile,))
result = self.cursor.fetchall()
if get_id:
return self.fetchall2set(result)
elif result:
return True
return False
def resolveNeeded(self, needed, elfclass = -1):
cache = self.fetchSearchCache(needed,'resolveNeeded')
if cache != None: return cache
ldpaths = self.entropyTools.collectLinkerPaths()
mypaths = [os.path.join(x,needed) for x in ldpaths]
query = """
SELECT
idpackage,file
FROM
content
WHERE
content.file IN (%s)
""" % ( ('?,'*len(mypaths))[:-1], )
self.cursor.execute(query,mypaths)
results = self.cursor.fetchall()
if elfclass == -1:
mydata = set(results)
else:
mydata = set()
for data in results:
if not os.access(data[1],os.R_OK):
continue
myclass = self.entropyTools.read_elf_class(data[1])
if myclass == elfclass:
mydata.add(data)
self.storeSearchCache(needed,'resolveNeeded',mydata)
return mydata
def isSourceAvailable(self,source):
self.cursor.execute('SELECT idsource FROM sourcesreference WHERE source = (?)', (source,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isDependencyAvailable(self,dependency):
self.cursor.execute('SELECT iddependency FROM dependenciesreference WHERE dependency = (?)', (dependency,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isKeywordAvailable(self,keyword):
self.cursor.execute('SELECT idkeyword FROM keywordsreference WHERE keywordname = (?)', (keyword,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isUseflagAvailable(self,useflag):
self.cursor.execute('SELECT idflag FROM useflagsreference WHERE flagname = (?)', (useflag,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isEclassAvailable(self,eclass):
self.cursor.execute('SELECT idclass FROM eclassesreference WHERE classname = (?)', (eclass,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isNeededAvailable(self,needed):
self.cursor.execute('SELECT idneeded FROM neededreference WHERE library = (?)', (needed,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isCounterAvailable(self, counter, branch = None, branch_operator = "="):
params = [counter]
branch_string = ''
if branch:
branch_string = ' and branch '+branch_operator+' (?)'
params = [counter,branch]
self.cursor.execute('SELECT counter FROM counters WHERE counter = (?)'+branch_string, params)
result = self.cursor.fetchone()
if result: return True
return False
def isCounterTrashed(self, counter):
self.cursor.execute('SELECT counter FROM trashedcounters WHERE counter = (?)', (counter,))
result = self.cursor.fetchone()
if result: return True
return False
def isLicensedataKeyAvailable(self, license_name):
if not self.doesTableExist("licensedata"):
return True
self.cursor.execute('SELECT licensename FROM licensedata WHERE licensename = (?)', (license_name,))
result = self.cursor.fetchone()
if not result:
return False
return True
def isLicenseAccepted(self, license_name):
self.cursor.execute('SELECT licensename FROM licenses_accepted WHERE licensename = (?)', (license_name,))
result = self.cursor.fetchone()
if not result:
return False
return True
def acceptLicense(self, license_name):
if self.readOnly or (not self.entropyTools.is_user_in_entropy_group()):
return
if self.isLicenseAccepted(license_name):
return
with self.WriteLock:
self.cursor.execute('INSERT INTO licenses_accepted VALUES (?)', (license_name,))
self.commitChanges()
def isLicenseAvailable(self,pkglicense):
if not self.entropyTools.is_valid_string(pkglicense):
pkglicense = ' '
self.cursor.execute('SELECT idlicense FROM licenses WHERE license = (?)', (pkglicense,))
result = self.cursor.fetchone()
if result: return result[0]
return -1
def isSystemPackage(self,idpackage):
self.cursor.execute('SELECT idpackage FROM systempackages WHERE idpackage = (?)', (idpackage,))
result = self.cursor.fetchone()
if result:
return True
return False
def isInjected(self,idpackage):
self.cursor.execute('SELECT idpackage FROM injected WHERE idpackage = (?)', (idpackage,))
result = self.cursor.fetchone()
if result:
return True
return False
def areCompileFlagsAvailable(self,chost,cflags,cxxflags):
self.cursor.execute('SELECT idflags FROM flags WHERE chost = (?) AND cflags = (?) AND cxxflags = (?)',
(chost,cflags,cxxflags,)
)
result = self.cursor.fetchone()
if result: return result[0]
return -1
def searchBelongs(self, file, like = False, branch = None, branch_operator = "="):
branchstring = ''
searchkeywords = [file]
if branch:
searchkeywords.append(branch)
branchstring = ' and baseinfo.branch '+branch_operator+' (?)'
if like:
self.cursor.execute('SELECT content.idpackage FROM content,baseinfo WHERE file LIKE (?) and content.idpackage = baseinfo.idpackage '+branchstring, searchkeywords)
else:
self.cursor.execute('SELECT content.idpackage FROM content,baseinfo WHERE file = (?) and content.idpackage = baseinfo.idpackage '+branchstring, searchkeywords)
return self.fetchall2set(self.cursor.fetchall())
''' search packages that uses the eclass provided '''
def searchEclassedPackages(self, eclass, atoms = False): # atoms = return atoms directly
if atoms:
self.cursor.execute('SELECT baseinfo.atom,eclasses.idpackage FROM baseinfo,eclasses,eclassesreference WHERE eclassesreference.classname = (?) and eclassesreference.idclass = eclasses.idclass and eclasses.idpackage = baseinfo.idpackage', (eclass,))
return self.cursor.fetchall()
else:
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE versiontag = (?)', (eclass,))
return self.fetchall2set(self.cursor.fetchall())
''' search packages whose versiontag matches the one provided '''
def searchTaggedPackages(self, tag, atoms = False): # atoms = return atoms directly
if atoms:
self.cursor.execute('SELECT atom,idpackage FROM baseinfo WHERE versiontag = (?)', (tag,))
return self.cursor.fetchall()
else:
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE versiontag = (?)', (tag,))
return self.fetchall2set(self.cursor.fetchall())
def searchLicenses(self, mylicense, caseSensitive = False, atoms = False):
if not self.entropyTools.is_valid_string(mylicense):
return []
request = "baseinfo.idpackage"
if atoms:
request = "baseinfo.atom,baseinfo.idpackage"
if caseSensitive:
self.cursor.execute('SELECT '+request+' FROM baseinfo,licenses WHERE licenses.license LIKE (?) and licenses.idlicense = baseinfo.idlicense', ("%"+mylicense+"%",))
else:
self.cursor.execute('SELECT '+request+' FROM baseinfo,licenses WHERE LOWER(licenses.license) LIKE (?) and licenses.idlicense = baseinfo.idlicense', ("%"+mylicense+"%".lower(),))
if atoms:
return self.cursor.fetchall()
return self.fetchall2set(self.cursor.fetchall())
''' search packages whose slot matches the one provided '''
def searchSlottedPackages(self, slot, atoms = False): # atoms = return atoms directly
if atoms:
self.cursor.execute('SELECT atom,idpackage FROM baseinfo WHERE slot = (?)', (slot,))
return self.cursor.fetchall()
else:
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE slot = (?)', (slot,))
return self.fetchall2set(self.cursor.fetchall())
def searchKeySlot(self, key, slot, branch = None):
branchstring = ''
cat,name = key.split("/")
params = [cat,name,slot]
if branch:
params.append(branch)
branchstring = 'and baseinfo.branch = (?)'
self.cursor.execute('SELECT idpackage FROM baseinfo,categories WHERE baseinfo.idcategory = categories.idcategory AND categories.category = (?) AND baseinfo.name = (?) AND baseinfo.slot = (?) %s' % (branchstring,), params)
return self.cursor.fetchall()
''' search packages that need the specified library (in neededreference table) specified by keyword '''
def searchNeeded(self, keyword, like = False):
if like:
self.cursor.execute('SELECT needed.idpackage FROM needed,neededreference WHERE library LIKE (?) and needed.idneeded = neededreference.idneeded', (keyword,))
else:
self.cursor.execute('SELECT needed.idpackage FROM needed,neededreference WHERE library = (?) and needed.idneeded = neededreference.idneeded', (keyword,))
return self.fetchall2set(self.cursor.fetchall())
''' search dependency string inside dependenciesreference table and retrieve iddependency '''
def searchDependency(self, dep, like = False, multi = False, strings = False):
sign = "="
if like:
sign = "LIKE"
dep = "%"+dep+"%"
item = 'iddependency'
if strings:
item = 'dependency'
self.cursor.execute('SELECT %s FROM dependenciesreference WHERE dependency %s (?)' % (item,sign,), (dep,))
if multi:
return self.fetchall2set(self.cursor.fetchall())
else:
iddep = self.cursor.fetchone()
if iddep:
iddep = iddep[0]
else:
iddep = -1
return iddep
''' search iddependency inside dependencies table and retrieve idpackages '''
def searchIdpackageFromIddependency(self, iddep):
self.cursor.execute('SELECT idpackage FROM dependencies WHERE iddependency = (?)', (iddep,))
return self.fetchall2set(self.cursor.fetchall())
def searchSets(self, keyword):
self.cursor.execute('SELECT DISTINCT(setname) FROM packagesets WHERE setname LIKE (?)', ("%"+keyword+"%",))
return self.fetchall2set(self.cursor.fetchall())
def searchSimilarPackages(self, mystring, atom = False):
s_item = 'name'
if atom: s_item = 'atom'
self.cursor.execute('SELECT idpackage FROM baseinfo WHERE soundex(%s) = soundex((?)) ORDER BY %s' % (s_item,s_item,), (mystring,))
return self.fetchall2list(self.cursor.fetchall())
def searchPackages(self, keyword, sensitive = False, slot = None, tag = None, branch = None, order_by = 'atom', just_id = False):
searchkeywords = ["%"+keyword+"%"]
slotstring = ''
if slot:
searchkeywords.append(slot)
slotstring = ' and slot = (?)'
tagstring = ''
if tag:
searchkeywords.append(tag)
tagstring = ' and versiontag = (?)'
branchstring = ''
if branch:
searchkeywords.append(branch)
branchstring = ' and branch = (?)'
order_by_string = ''
if order_by in ("atom","idpackage","branch",):
order_by_string = ' order by %s' % (order_by,)
search_elements = 'atom,idpackage,branch'
if just_id: search_elements = 'idpackage'
if (sensitive):
self.cursor.execute('SELECT '+search_elements+' FROM baseinfo WHERE atom LIKE (?)'+slotstring+tagstring+branchstring+order_by_string, searchkeywords)
else:
self.cursor.execute('SELECT '+search_elements+' FROM baseinfo WHERE LOWER(atom) LIKE (?)'+slotstring+tagstring+branchstring+order_by_string, searchkeywords)
if just_id:
return self.fetchall2list(self.cursor.fetchall())
return self.cursor.fetchall()
def searchProvide(self, keyword, slot = None, tag = None, branch = None, justid = False):
slotstring = ''
searchkeywords = [keyword]
if slot:
searchkeywords.append(slot)
slotstring = ' and baseinfo.slot = (?)'
tagstring = ''
if tag:
searchkeywords.append(tag)
tagstring = ' and baseinfo.versiontag = (?)'
branchstring = ''
if branch:
searchkeywords.append(branch)
branchstring = ' and baseinfo.branch = (?)'
atomstring = ''
if not justid:
atomstring = 'baseinfo.atom,'
self.cursor.execute('SELECT '+atomstring+'baseinfo.idpackage FROM baseinfo,provide WHERE provide.atom = (?) and provide.idpackage = baseinfo.idpackage'+slotstring+tagstring+branchstring, searchkeywords)
if justid:
results = self.fetchall2list(self.cursor.fetchall())
else:
results = self.cursor.fetchall()
return results
def searchPackagesByDescription(self, keyword):
self.cursor.execute('SELECT baseinfo.atom,baseinfo.idpackage FROM extrainfo,baseinfo WHERE LOWER(extrainfo.description) LIKE (?) and baseinfo.idpackage = extrainfo.idpackage', ("%"+keyword.lower()+"%",))
return self.cursor.fetchall()
def searchPackagesByName(self, keyword, sensitive = False, branch = None, justid = False):
if sensitive:
searchkeywords = [keyword]
else:
searchkeywords = [keyword.lower()]
branchstring = ''
atomstring = ''
if not justid:
atomstring = 'atom,'
if branch:
searchkeywords.append(branch)
branchstring = ' and branch = (?)'
if sensitive:
self.cursor.execute('SELECT '+atomstring+'idpackage FROM baseinfo WHERE name = (?)'+branchstring, searchkeywords)
else:
self.cursor.execute('SELECT '+atomstring+'idpackage FROM baseinfo WHERE LOWER(name) = (?)'+branchstring, searchkeywords)
if justid:
results = self.fetchall2list(self.cursor.fetchall())
else:
results = self.cursor.fetchall()
return results
def searchPackagesByCategory(self, keyword, like = False, branch = None):
searchkeywords = [keyword]
branchstring = ''
if branch:
searchkeywords.append(branch)
branchstring = 'and branch = (?)'
if like:
self.cursor.execute('SELECT baseinfo.atom,baseinfo.idpackage FROM baseinfo,categories WHERE categories.category LIKE (?) and baseinfo.idcategory = categories.idcategory %s' % (branchstring,), searchkeywords)
else:
self.cursor.execute('SELECT baseinfo.atom,baseinfo.idpackage FROM baseinfo,categories WHERE categories.category = (?) and baseinfo.idcategory = categories.idcategory %s' % (branchstring,), searchkeywords)
return self.cursor.fetchall()
def searchPackagesByNameAndCategory(self, name, category, sensitive = False, branch = None, justid = False):
myname = name
mycat = category
if not sensitive:
myname = name.lower()
mycat = category.lower()
searchkeywords = [myname,mycat]
branchstring = ''
if branch:
searchkeywords.append(branch)
branchstring = ' and branch = (?)'
atomstring = ''
if not justid:
atomstring = 'atom,'
if sensitive:
self.cursor.execute('SELECT '+atomstring+'idpackage FROM baseinfo WHERE name = (?) AND idcategory IN (SELECT idcategory FROM categories WHERE category = (?))'+branchstring, searchkeywords)
else:
self.cursor.execute('SELECT '+atomstring+'idpackage FROM baseinfo WHERE LOWER(name) = (?) AND idcategory IN (SELECT idcategory FROM categories WHERE LOWER(category) = (?))'+branchstring, searchkeywords)
''
if justid:
results = self.fetchall2list(self.cursor.fetchall())
else:
results = self.cursor.fetchall()
return results
def searchPackagesKeyVersion(self, key, version, branch = None, sensitive = False):
searchkeywords = []
if sensitive:
searchkeywords.append(key)
else:
searchkeywords.append(key.lower())
searchkeywords.append(version)
branchstring = ''
if branch:
searchkeywords.append(branch)
branchstring = ' and branch = (?)'
if (sensitive):
self.cursor.execute('SELECT baseinfo.atom,baseinfo.idpackage FROM baseinfo,categories WHERE categories.category || "/" || baseinfo.name = (?) and version = (?) and baseinfo.idcategory = categories.idcategory '+branchstring, searchkeywords)
else:
self.cursor.execute('SELECT baseinfo.atom,baseinfo.idpackage FROM baseinfo,categories WHERE LOWER(categories.category) || "/" || LOWER(baseinfo.name) = (?) and version = (?) and baseinfo.idcategory = categories.idcategory '+branchstring, searchkeywords)
results = self.cursor.fetchall()
return results
def isPackageScopeAvailable(self, atom, slot, revision):
searchdata = (atom,slot,revision,)
self.cursor.execute('SELECT idpackage FROM baseinfo where atom = (?) and slot = (?) and revision = (?)',searchdata)
rslt = self.cursor.fetchone()
idreason = 0
idpackage = -1
if rslt:
# check if it's masked
idpackage, idreason = self.idpackageValidator(rslt[0])
return idpackage,idreason
def listAllPackages(self, get_scope = False, order_by = None, branch = None, branch_operator = "="):
branchstring = ''
searchkeywords = []
if branch:
searchkeywords = [branch]
branchstring = ' where branch %s (?)' % (str(branch_operator),)
order_txt = ''
if order_by:
order_txt = ' order by '+str(order_by)
if get_scope:
self.cursor.execute('SELECT idpackage,atom,slot,revision FROM baseinfo'+order_txt+branchstring,searchkeywords)
else:
self.cursor.execute('SELECT atom,idpackage,branch FROM baseinfo'+order_txt+branchstring,searchkeywords)
return self.cursor.fetchall()
def listAllInjectedPackages(self, justFiles = False):
self.cursor.execute('SELECT idpackage FROM injected')
injecteds = self.fetchall2set(self.cursor.fetchall())
results = set()
# get download
for injected in injecteds:
download = self.retrieveDownloadURL(injected)
if justFiles:
results.add(download)
else:
results.add((download,injected))
return results
def listAllCounters(self, onlycounters = False, branch = None, branch_operator = "="):
branchstring = ''
if branch:
branchstring = ' WHERE branch '+branch_operator+' "'+str(branch)+'"'
if onlycounters:
self.cursor.execute('SELECT counter FROM counters'+branchstring)
return self.fetchall2set(self.cursor.fetchall())
else:
self.cursor.execute('SELECT counter,idpackage FROM counters'+branchstring)
return self.cursor.fetchall()
def listAllIdpackages(self, branch = None, branch_operator = "=", order_by = None):
branchstring = ''
orderbystring = ''
searchkeywords = []
if branch:
searchkeywords.append(branch)
branchstring = ' where branch %s (?)' % (str(branch_operator),)
if order_by:
orderbystring = ' order by '+order_by
self.cursor.execute('SELECT idpackage FROM baseinfo'+branchstring+orderbystring, searchkeywords)
try:
if order_by:
results = self.fetchall2list(self.cursor.fetchall())
else:
results = self.fetchall2set(self.cursor.fetchall())
return results
except self.dbapi2.OperationalError:
if order_by:
return []
return set()
def listAllDependencies(self, only_deps = False):
if only_deps:
self.cursor.execute('SELECT dependency FROM dependenciesreference')
return self.fetchall2set(self.cursor.fetchall())
else:
self.cursor.execute('SELECT * FROM dependenciesreference')
return self.cursor.fetchall()
def listAllBranches(self):
cache = self.live_cache.get('listAllBranches')
if cache != None:
return cache
self.cursor.execute('SELECT distinct branch FROM baseinfo')
results = self.fetchall2set(self.cursor.fetchall())
self.live_cache['listAllBranches'] = results.copy()
return results
def listIdPackagesInIdcategory(self,idcategory, order_by = 'atom'):
order_by_string = ''
if order_by in ("atom","name","version",):
order_by_string = ' ORDER BY %s' % (order_by,)
self.cursor.execute('SELECT idpackage FROM baseinfo where idcategory = (?)'+order_by_string, (idcategory,))
return self.fetchall2set(self.cursor.fetchall())
def listIdpackageDependencies(self, idpackage):
self.cursor.execute('SELECT dependenciesreference.iddependency,dependenciesreference.dependency FROM dependenciesreference,dependencies WHERE dependencies.idpackage = (?) AND dependenciesreference.iddependency = dependencies.iddependency', (idpackage,))
return set(self.cursor.fetchall())
def listBranchPackagesTbz2(self, branch, do_sort = True, full_path = False):
order_string = ''
if do_sort: order_string = 'ORDER BY extrainfo.download'
self.cursor.execute('SELECT extrainfo.download FROM baseinfo,extrainfo WHERE baseinfo.branch = (?) AND baseinfo.idpackage = extrainfo.idpackage %s' % (order_string,), (branch,))
if do_sort: results = self.fetchall2list(self.cursor.fetchall())
else: results = self.fetchall2set(self.cursor.fetchall())
if not full_path: results = [os.path.basename(x) for x in results]
if do_sort: return results
return set(results)
def listAllFiles(self, clean = False, count = False):
self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
if count:
self.cursor.execute('SELECT count(file) FROM content')
else:
self.cursor.execute('SELECT file FROM content')
if count:
return self.cursor.fetchone()[0]
else:
if clean:
return self.fetchall2set(self.cursor.fetchall())
else:
return self.fetchall2list(self.cursor.fetchall())
def listAllCategories(self, order_by = ''):
order_by_string = ''
if order_by: order_by_string = ' order by %s' % (order_by,)
self.cursor.execute('SELECT idcategory,category FROM categories %s' % (order_by_string,))
return self.cursor.fetchall()
def listConfigProtectDirectories(self, mask = False):
mask_t = ''
if mask: mask_t = 'mask'
self.cursor.execute('SELECT DISTINCT(protect) FROM configprotectreference where idprotect >= 1 and idprotect <= (SELECT max(idprotect) FROM configprotect%s) order by protect' % (mask_t,))
results = self.fetchall2set(self.cursor.fetchall())
dirs = set()
for mystr in results:
dirs |= set(map(unicode,mystr.split()))
return sorted(list(dirs))
def switchBranch(self, idpackage, tobranch):
self.checkReadOnly()
key, slot = self.retrieveKeySlot(idpackage)
# if there are entries already, remove idpackage directly
my_idpackage, result = self.atomMatch(key, matchSlot = slot, matchBranches = (tobranch,))
if my_idpackage != -1: return False
# otherwise, update the old one (set the new branch)
with self.WriteLock:
self.cursor.execute('UPDATE baseinfo SET branch = (?) WHERE idpackage = (?)', (tobranch,idpackage,))
self.commitChanges()
self.clearCache()
return True
def databaseStructureUpdates(self):
old_readonly = self.readOnly
self.readOnly = False
if not self.doesTableExist("licensedata"):
self.createLicensedataTable()
if not self.doesTableExist("licenses_accepted") and (self.dbname == etpConst['clientdbid']):
self.createLicensesAcceptedTable()
if not self.doesTableExist("trashedcounters"):
self.createTrashedcountersTable()
if not self.doesTableExist("counters"):
self.createCountersTable()
if not self.doesTableExist("installedtable") and (self.dbname == etpConst['clientdbid']):
self.createInstalledTable()
if not self.doesTableExist("categoriesdescription"):
self.createCategoriesdescriptionTable()
if not self.doesTableExist('packagesets'):
self.createPackagesetsTable()
self.readOnly = old_readonly
self.connection.commit()
def validateDatabase(self):
self.cursor.execute('select name from SQLITE_MASTER where type = (?) and name = (?)', ("table","baseinfo"))
rslt = self.cursor.fetchone()
if rslt == None:
mytxt = _("baseinfo table not found. Either does not exist or corrupted.")
raise exceptionTools.SystemDatabaseError("SystemDatabaseError: %s" % (mytxt,))
self.cursor.execute('select name from SQLITE_MASTER where type = (?) and name = (?)', ("table","extrainfo"))
rslt = self.cursor.fetchone()
if rslt == None:
mytxt = _("extrainfo table not found. Either does not exist or corrupted.")
raise exceptionTools.SystemDatabaseError("SystemDatabaseError: %s" % (mytxt,))
def getIdpackagesDifferences(self, foreign_idpackages):
myids = self.listAllIdpackages()
if type(foreign_idpackages) in (list,tuple,):
outids = set(foreign_idpackages)
else:
outids = foreign_idpackages
added_ids = outids - myids
removed_ids = myids - outids
return added_ids, removed_ids
def uniformBranch(self, branch):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE baseinfo SET branch = (?)', (branch,))
self.commitChanges()
self.clearCache()
def alignDatabases(self, dbconn, force = False, output_header = " ", align_limit = 300):
added_ids, removed_ids = self.getIdpackagesDifferences(dbconn.listAllIdpackages())
if not force:
if len(added_ids) > align_limit: # too much hassle
return 0
if len(removed_ids) > align_limit: # too much hassle
return 0
if not added_ids and not removed_ids:
return -1
mytxt = red("%s, %s ...") % (_("Syncing current database"),_("please wait"),)
self.updateProgress(
mytxt,
importance = 1,
type = "info",
header = output_header,
back = True
)
maxcount = len(removed_ids)
mycount = 0
for idpackage in removed_ids:
mycount += 1
mytxt = "%s: %s" % (red(_("Removing entry")),blue(str(self.retrieveAtom(idpackage))),)
self.updateProgress(
mytxt,
importance = 0,
type = "info",
header = output_header,
back = True,
count = (mycount,maxcount)
)
self.removePackage(idpackage, do_cleanup = False, do_commit = False)
maxcount = len(added_ids)
mycount = 0
for idpackage in added_ids:
mycount += 1
mytxt = "%s: %s" % (red(_("Adding entry")),blue(str(dbconn.retrieveAtom(idpackage))),)
self.updateProgress(
mytxt,
importance = 0,
type = "info",
header = output_header,
back = True,
count = (mycount,maxcount)
)
mydata = dbconn.getPackageData(idpackage, get_content = True, content_insert_formatted = True)
self.addPackage(
mydata,
revision = mydata['revision'],
idpackage = idpackage,
do_remove = False,
do_commit = False,
formatted_content = True
)
# do some cleanups
self.doCleanups()
# clear caches
self.clearCache()
self.commitChanges()
self.regenerateDependsTable(output = False)
# verify both checksums, if they don't match, bomb out
mycheck = self.database_checksum(do_order = True, strict = False)
outcheck = dbconn.database_checksum(do_order = True, strict = False)
if mycheck == outcheck:
return 1
return 0
def checkDatabaseApi(self):
dbapi = self.getApi()
if int(dbapi) > int(etpConst['etpapi']):
self.updateProgress(
red(_("Repository EAPI > Entropy EAPI. Please update Equo/Entropy as soon as possible !")),
importance = 1,
type = "warning",
header = " * ! * ! * ! * "
)
def doDatabaseImport(self, dumpfile, dbfile):
sqlite3_exec = "/usr/bin/sqlite3 %s < %s" % (dbfile,dumpfile,)
retcode = subprocess.call(sqlite3_exec, shell = True)
return retcode
def doDatabaseExport(self, dumpfile, gentle_with_tables = True):
dumpfile.write("BEGIN TRANSACTION;\n")
self.cursor.execute("SELECT name, type, sql FROM sqlite_master WHERE sql NOT NULL AND type=='table'")
for name, x, sql in self.cursor.fetchall():
self.updateProgress(
red("%s " % (_("Exporting database table"),) )+"["+blue(str(name))+"]",
importance = 0,
type = "info",
back = True,
header = " "
)
if name == "sqlite_sequence":
dumpfile.write("DELETE FROM sqlite_sequence;\n")
elif name == "sqlite_stat1":
dumpfile.write("ANALYZE sqlite_master;\n")
elif name.startswith("sqlite_"):
continue
else:
t_cmd = "CREATE TABLE"
if sql.startswith(t_cmd) and gentle_with_tables:
sql = "CREATE TABLE IF NOT EXISTS"+sql[len(t_cmd):]
dumpfile.write("%s;\n" % sql)
self.cursor.execute("PRAGMA table_info('%s')" % name)
cols = [str(r[1]) for r in self.cursor.fetchall()]
q = "SELECT 'INSERT INTO \"%(tbl_name)s\" VALUES("
q += ", ".join(["'||quote(" + x + ")||'" for x in cols])
q += ")' FROM '%(tbl_name)s'"
self.cursor.execute(q % {'tbl_name': name})
self.connection.text_factory = lambda x: unicode(x, "raw_unicode_escape")
for row in self.cursor:
dumpfile.write("%s;\n" % str(row[0].encode('raw_unicode_escape')))
self.cursor.execute("SELECT name, type, sql FROM sqlite_master WHERE sql NOT NULL AND type!='table' AND type!='meta'")
for name, x, sql in self.cursor.fetchall():
dumpfile.write("%s;\n" % sql)
dumpfile.write("COMMIT;\n")
try:
dumpfile.flush()
except:
pass
self.updateProgress(
red(_("Database Export completed.")),
importance = 0,
type = "info",
header = " "
)
# remember to close the file
# FIXME: this is only compatible with SQLITE
def doesTableExist(self, table):
self.cursor.execute('select name from SQLITE_MASTER where type = (?) and name = (?)', ("table",table))
rslt = self.cursor.fetchone()
if rslt == None:
return False
return True
# FIXME: this is only compatible with SQLITE
def doesColumnInTableExist(self, table, column):
self.cursor.execute('PRAGMA table_info( '+table+' )')
rslt = self.cursor.fetchall()
if not rslt:
return False
found = False
for row in rslt:
if row[1] == column:
found = True
break
return found
# FIXME: this is only compatible with SQLITE
def getColumnsInTable(self, table):
self.cursor.execute('PRAGMA table_info( '+table+' )')
rslt = self.cursor.fetchall()
columns = []
for row in rslt:
columns.append(row[1])
return columns
def database_checksum(self, do_order = False, strict = True, strings = False):
idpackage_order = ''
category_order = ''
license_order = ''
flags_order = ''
if do_order:
idpackage_order = 'order by idpackage'
category_order = 'order by category'
license_order = 'order by license'
flags_order = 'order by chost'
def do_update_md5(m, cursor):
mydata = cursor.fetchall()
for record in mydata:
for item in record:
m.update(str(item))
if strings:
import hashlib
m = hashlib.md5()
self.cursor.execute('select idpackage,atom,name,version,versiontag,revision,branch,slot,etpapi,trigger from baseinfo %s' % (idpackage_order,))
if strings:
do_update_md5(m,self.cursor)
else:
a_hash = hash(tuple(self.cursor.fetchall()))
self.cursor.execute('select idpackage,description,homepage,download,size,digest,datecreation from extrainfo %s' % (idpackage_order,))
if strings:
do_update_md5(m,self.cursor)
else:
b_hash = hash(tuple(self.cursor.fetchall()))
self.cursor.execute('select category from categories %s' % (category_order,))
if strings:
do_update_md5(m,self.cursor)
else:
c_hash = hash(tuple(self.cursor.fetchall()))
d_hash = '0'
e_hash = '0'
if strict:
self.cursor.execute('select * from licenses %s' % (license_order,))
if strings:
do_update_md5(m,self.cursor)
else:
d_hash = hash(tuple(self.cursor.fetchall()))
self.cursor.execute('select * from flags %s' % (flags_order,))
if strings:
do_update_md5(m,self.cursor)
else:
e_hash = hash(tuple(self.cursor.fetchall()))
if strings:
return m.hexdigest()
return "%s:%s:%s:%s:%s" % (a_hash,b_hash,c_hash,d_hash,e_hash,)
########################################################
####
## Client Database API / but also used by server part
#
def addPackageToInstalledTable(self, idpackage, repositoryName):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('INSERT into installedtable VALUES (?,?)', (idpackage, repositoryName,))
self.commitChanges()
def retrievePackageFromInstalledTable(self, idpackage):
self.checkReadOnly()
result = 'Not available'
with self.WriteLock:
try:
self.cursor.execute('SELECT repositoryname FROM installedtable WHERE idpackage = (?)', (idpackage,))
return self.cursor.fetchone()[0] # it's ok because it's inside try/except
except:
pass
return result
def removePackageFromInstalledTable(self, idpackage):
with self.WriteLock:
self.cursor.execute('DELETE FROM installedtable WHERE idpackage = (?)', (idpackage,))
def removePackageFromDependsTable(self, idpackage):
with self.WriteLock:
try:
self.cursor.execute('DELETE FROM dependstable WHERE idpackage = (?)', (idpackage,))
return 0
except (self.dbapi2.OperationalError,):
return 1 # need reinit
def removeDependencyFromDependsTable(self, iddependency):
self.checkReadOnly()
with self.WriteLock:
try:
self.cursor.execute('DELETE FROM dependstable WHERE iddependency = (?)',(iddependency,))
self.commitChanges()
return 0
except:
return 1 # need reinit
# temporary/compat functions
def createDependsTable(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.executescript("""
DROP TABLE IF EXISTS dependstable;
CREATE TABLE dependstable ( iddependency INTEGER PRIMARY KEY, idpackage INTEGER );
INSERT into dependstable VALUES (-1,-1);
""")
if self.indexing:
self.cursor.execute('CREATE INDEX IF NOT EXISTS dependsindex_idpackage ON dependstable ( idpackage )')
self.commitChanges()
def sanitizeDependsTable(self):
with self.WriteLock:
self.cursor.execute('DELETE FROM dependstable where iddependency = -1')
self.commitChanges()
def isDependsTableSane(self):
try:
self.cursor.execute('SELECT iddependency FROM dependstable WHERE iddependency = -1')
except (self.dbapi2.OperationalError,):
return False # table does not exist, please regenerate and re-run
status = self.cursor.fetchone()
if status: return False
self.cursor.execute('select count(*) from dependstable')
dependstable_count = self.cursor.fetchone()
if dependstable_count < 2:
return False
return True
def createXpakTable(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('CREATE TABLE xpakdata ( idpackage INTEGER PRIMARY KEY, data BLOB );')
self.commitChanges()
def storeXpakMetadata(self, idpackage, blob):
with self.WriteLock:
self.cursor.execute('INSERT into xpakdata VALUES (?,?)', (int(idpackage),buffer(blob),))
self.commitChanges()
def retrieveXpakMetadata(self, idpackage):
try:
self.cursor.execute('SELECT data from xpakdata where idpackage = (?)', (idpackage,))
mydata = self.cursor.fetchone()
if not mydata:
return ""
return mydata[0]
except:
return ""
def createCountersTable(self):
with self.WriteLock:
self.cursor.execute("CREATE TABLE IF NOT EXISTS counters ( counter INTEGER, idpackage INTEGER PRIMARY KEY, branch VARCHAR );")
def CreatePackedDataTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE packed_data ( idpack INTEGER PRIMARY KEY, data BLOB );')
def dropAllIndexes(self):
self.cursor.execute('SELECT name FROM SQLITE_MASTER WHERE type = "index"')
indexes = self.fetchall2set(self.cursor.fetchall())
with self.WriteLock:
for index in indexes:
if not index.startswith("sqlite"):
self.cursor.execute('DROP INDEX IF EXISTS %s' % (index,))
def listAllIndexes(self, only_entropy = True):
self.cursor.execute('SELECT name FROM SQLITE_MASTER WHERE type = "index"')
indexes = self.fetchall2set(self.cursor.fetchall())
if not only_entropy:
return indexes
myindexes = set()
for index in indexes:
if index.startswith("sqlite"):
continue
myindexes.add(index)
return myindexes
def createAllIndexes(self):
self.createContentIndex()
self.createBaseinfoIndex()
self.createKeywordsIndex()
self.createDependenciesIndex()
self.createProvideIndex()
self.createConflictsIndex()
self.createExtrainfoIndex()
self.createNeededIndex()
self.createUseflagsIndex()
self.createLicensedataIndex()
self.createLicensesIndex()
self.createConfigProtectReferenceIndex()
self.createMessagesIndex()
self.createSourcesIndex()
self.createCountersIndex()
self.createEclassesIndex()
self.createCategoriesIndex()
self.createCompileFlagsIndex()
self.createPackagesetsIndex()
def createPackagesetsIndex(self):
if self.indexing:
with self.WriteLock:
try:
self.cursor.execute('CREATE INDEX IF NOT EXISTS packagesetsindex ON packagesets ( setname )')
self.commitChanges()
except self.dbapi2.OperationalError:
pass
def createNeededIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS neededindex ON neededreference ( library );
CREATE INDEX IF NOT EXISTS neededindex_idneeded ON needed ( idneeded );
CREATE INDEX IF NOT EXISTS neededindex_idpackage ON needed ( idpackage );
CREATE INDEX IF NOT EXISTS neededindex_elfclass ON needed ( elfclass );
""")
self.commitChanges()
def createMessagesIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS messagesindex ON messages ( idpackage )')
self.commitChanges()
def createCompileFlagsIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS flagsindex ON flags ( chost,cflags,cxxflags )')
self.commitChanges()
def createUseflagsIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS useflagsindex_useflags_idpackage ON useflags ( idpackage );
CREATE INDEX IF NOT EXISTS useflagsindex_useflags_idflag ON useflags ( idflag );
CREATE INDEX IF NOT EXISTS useflagsindex ON useflagsreference ( flagname );
""")
self.commitChanges()
def createContentIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS contentindex_couple ON content ( idpackage );
CREATE INDEX IF NOT EXISTS contentindex_file ON content ( file );
""")
self.commitChanges()
def createConfigProtectReferenceIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS configprotectreferenceindex ON configprotectreference ( protect )')
self.commitChanges()
def createBaseinfoIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS baseindex_atom ON baseinfo ( atom );
CREATE INDEX IF NOT EXISTS baseindex_branch_name ON baseinfo ( name,branch );
CREATE INDEX IF NOT EXISTS baseindex_branch_name_idcategory ON baseinfo ( name,idcategory,branch );
CREATE INDEX IF NOT EXISTS baseindex_idcategory ON baseinfo ( idcategory );
""")
self.commitChanges()
def createLicensedataIndex(self):
if self.indexing:
if not self.doesTableExist("licensedata"):
return
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS licensedataindex ON licensedata ( licensename )')
self.commitChanges()
def createLicensesIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS licensesindex ON licenses ( license )')
self.commitChanges()
def createCategoriesIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS categoriesindex_category ON categories ( category )')
self.commitChanges()
def createKeywordsIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS keywordsreferenceindex ON keywordsreference ( keywordname );
CREATE INDEX IF NOT EXISTS keywordsindex_idpackage ON keywords ( idpackage );
CREATE INDEX IF NOT EXISTS keywordsindex_idkeyword ON keywords ( idkeyword );
""")
self.commitChanges()
def createDependenciesIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS dependenciesindex_idpackage ON dependencies ( idpackage );
CREATE INDEX IF NOT EXISTS dependenciesindex_iddependency ON dependencies ( iddependency );
CREATE INDEX IF NOT EXISTS dependenciesreferenceindex_dependency ON dependenciesreference ( dependency );
""")
self.commitChanges()
def createCountersIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS countersindex_idpackage ON counters ( idpackage );
CREATE INDEX IF NOT EXISTS countersindex_counter ON counters ( counter );
""")
self.commitChanges()
def createSourcesIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS sourcesindex_idpackage ON sources ( idpackage );
CREATE INDEX IF NOT EXISTS sourcesindex_idsource ON sources ( idsource );
CREATE INDEX IF NOT EXISTS sourcesreferenceindex_source ON sourcesreference ( source );
""")
self.commitChanges()
def createProvideIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS provideindex_idpackage ON provide ( idpackage );
CREATE INDEX IF NOT EXISTS provideindex_atom ON provide ( atom );
""")
self.commitChanges()
def createConflictsIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS conflictsindex_idpackage ON conflicts ( idpackage );
CREATE INDEX IF NOT EXISTS conflictsindex_atom ON conflicts ( conflict );
""")
self.commitChanges()
def createExtrainfoIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.execute('CREATE INDEX IF NOT EXISTS extrainfoindex ON extrainfo ( description )')
self.commitChanges()
def createEclassesIndex(self):
if self.indexing:
with self.WriteLock:
self.cursor.executescript("""
CREATE INDEX IF NOT EXISTS eclassesindex_idpackage ON eclasses ( idpackage );
CREATE INDEX IF NOT EXISTS eclassesindex_idclass ON eclasses ( idclass );
CREATE INDEX IF NOT EXISTS eclassesreferenceindex_classname ON eclassesreference ( classname );
""")
self.commitChanges()
def regenerateCountersTable(self, vdb_path, output = False):
self.checkReadOnly()
self.createCountersTable()
# assign a counter to an idpackage
myids = self.listAllIdpackages()
for myid in myids:
# get atom
myatom = self.retrieveAtom(myid)
mybranch = self.retrieveBranch(myid)
myatom = self.entropyTools.remove_tag(myatom)
myatomcounterpath = "%s%s/%s" % (vdb_path,myatom,etpConst['spm']['xpak_entries']['counter'],)
if os.path.isfile(myatomcounterpath):
try:
f = open(myatomcounterpath,"r")
counter = int(f.readline().strip())
f.close()
except:
if output:
mytxt = "%s: %s: %s" % (
bold(_("ATTENTION")),
red(_("cannot open Spm counter file for")),
bold(myatom),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning"
)
continue
# insert id+counter
with self.WriteLock:
try:
self.cursor.execute(
'INSERT into counters VALUES '
'(?,?,?)', ( counter, myid, mybranch )
)
except self.dbapi2.IntegrityError:
if output:
mytxt = "%s: %s: %s" % (
bold(_("ATTENTION")),
red(_("counter for atom is duplicated, ignoring")),
bold(myatom),
)
self.updateProgress(
mytxt,
importance = 1,
type = "warning"
)
continue
# don't trust counters, they might not be unique
self.commitChanges()
def clearTreeupdatesEntries(self, repository):
self.checkReadOnly()
if not self.doesTableExist("treeupdates"):
self.createTreeupdatesTable()
# treeupdates
with self.WriteLock:
self.cursor.execute("DELETE FROM treeupdates WHERE repository = (?)", (repository,))
self.commitChanges()
def resetTreeupdatesDigests(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('UPDATE treeupdates SET digest = "-1"')
self.commitChanges()
#
# FIXME: remove these when 1.0 will be out
#
def migrateCountersTable(self):
with self.WriteLock:
self._migrateCountersTable()
def _migrateCountersTable(self):
self.cursor.execute('DROP TABLE IF EXISTS counterstemp;')
self.cursor.execute('CREATE TABLE counterstemp ( counter INTEGER, idpackage INTEGER, branch VARCHAR, PRIMARY KEY(idpackage,branch) );')
self.cursor.execute('select * from counters')
countersdata = self.cursor.fetchall()
self.cursor.executemany('INSERT INTO counterstemp VALUES (?,?,?)',countersdata)
self.cursor.execute('DROP TABLE counters')
self.cursor.execute('ALTER TABLE counterstemp RENAME TO counters')
self.commitChanges()
def createPackagesetsTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE packagesets ( setname VARCHAR, dependency VARCHAR );')
def createCategoriesdescriptionTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE categoriesdescription ( category VARCHAR, locale VARCHAR, description VARCHAR );')
def createTreeupdatesTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE treeupdates ( repository VARCHAR PRIMARY KEY, digest VARCHAR );')
def createTreeupdatesactionsTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE treeupdatesactions ( idupdate INTEGER PRIMARY KEY AUTOINCREMENT, repository VARCHAR, command VARCHAR, branch VARCHAR, date VARCHAR );')
def createSizesTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE sizes ( idpackage INTEGER, size INTEGER );')
def createEntropyMiscCountersTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE entropy_misc_counters ( idtype INTEGER PRIMARY KEY, counter INTEGER );')
def createDependenciesTypeColumn(self):
with self.WriteLock:
self.cursor.execute('ALTER TABLE dependencies ADD COLUMN type INTEGER;')
self.cursor.execute('UPDATE dependencies SET type = (?)', (0,))
def createCountersBranchColumn(self):
with self.WriteLock:
self.cursor.execute('ALTER TABLE counters ADD COLUMN branch VARCHAR;')
idpackages = self.listAllIdpackages()
for idpackage in idpackages:
branch = self.retrieveBranch(idpackage)
self.cursor.execute('UPDATE counters SET branch = (?) WHERE idpackage = (?)', (branch,idpackage,))
def createTreeupdatesactionsDateColumn(self):
with self.WriteLock:
try:
# if database disk image is malformed, won't raise exception here
self.cursor.execute('ALTER TABLE treeupdatesactions ADD COLUMN date VARCHAR;')
mytime = str(self.entropyTools.getCurrentUnixTime())
self.cursor.execute('UPDATE treeupdatesactions SET date = (?)', (mytime,))
except:
pass
def createTreeupdatesactionsBranchColumn(self):
with self.WriteLock:
try: # if database disk image is malformed, won't raise exception here
self.cursor.execute('ALTER TABLE treeupdatesactions ADD COLUMN branch VARCHAR;')
self.cursor.execute('UPDATE treeupdatesactions SET branch = (?)', (etpConst['branch'],))
except:
pass
def createNeededElfclassColumn(self):
with self.WriteLock:
try: # if database disk image is malformed, won't raise exception here
self.cursor.execute('ALTER TABLE needed ADD COLUMN elfclass INTEGER;')
self.cursor.execute('UPDATE needed SET elfclass = -1')
except:
pass
def createContentTypeColumn(self):
with self.WriteLock:
try: # if database disk image is malformed, won't raise exception here
self.cursor.execute('ALTER TABLE content ADD COLUMN type VARCHAR;')
self.cursor.execute('UPDATE content SET type = "0"')
except:
pass
def createLicensedataTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE licensedata ( licensename VARCHAR UNIQUE, text BLOB, compressed INTEGER );')
def createLicensesAcceptedTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE licenses_accepted ( licensename VARCHAR UNIQUE );')
def createTrashedcountersTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE trashedcounters ( counter INTEGER );')
def createTriggerTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE triggers ( idpackage INTEGER PRIMARY KEY, data BLOB );')
def createTriggerColumn(self):
self.checkReadOnly()
with self.WriteLock:
self.cursor.execute('ALTER TABLE baseinfo ADD COLUMN trigger INTEGER;')
self.cursor.execute('UPDATE baseinfo SET trigger = 0')
def createMessagesTable(self):
with self.WriteLock:
self.cursor.execute("CREATE TABLE messages ( idpackage INTEGER, message VARCHAR );")
def createEclassesTable(self):
with self.WriteLock:
self.cursor.execute('DROP TABLE IF EXISTS eclasses;')
self.cursor.execute('DROP TABLE IF EXISTS eclassesreference;')
self.cursor.execute('CREATE TABLE eclasses ( idpackage INTEGER, idclass INTEGER );')
self.cursor.execute('CREATE TABLE eclassesreference ( idclass INTEGER PRIMARY KEY AUTOINCREMENT, classname VARCHAR );')
def createNeededTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE needed ( idpackage INTEGER, idneeded INTEGER, elfclass INTEGER );')
self.cursor.execute('CREATE TABLE neededreference ( idneeded INTEGER PRIMARY KEY AUTOINCREMENT, library VARCHAR );')
def createSystemPackagesTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE systempackages ( idpackage INTEGER PRIMARY KEY );')
def createInjectedTable(self):
with self.WriteLock:
self.cursor.execute('CREATE TABLE injected ( idpackage INTEGER PRIMARY KEY );')
def createProtectTable(self):
with self.WriteLock:
self.cursor.execute('DROP TABLE IF EXISTS configprotect;')
self.cursor.execute('DROP TABLE IF EXISTS configprotectmask;')
self.cursor.execute('DROP TABLE IF EXISTS configprotectreference;')
self.cursor.execute('CREATE TABLE configprotect ( idpackage INTEGER PRIMARY KEY, idprotect INTEGER );')
self.cursor.execute('CREATE TABLE configprotectmask ( idpackage INTEGER PRIMARY KEY, idprotect INTEGER );')
self.cursor.execute('CREATE TABLE configprotectreference ( idprotect INTEGER PRIMARY KEY AUTOINCREMENT, protect VARCHAR );')
def createInstalledTable(self):
with self.WriteLock:
self.cursor.execute('DROP TABLE IF EXISTS installedtable;')
self.cursor.execute('CREATE TABLE installedtable ( idpackage INTEGER PRIMARY KEY, repositoryname VARCHAR );')
def addDependRelationToDependsTable(self, iddependency, idpackage):
with self.WriteLock:
self.cursor.execute( 'INSERT into dependstable VALUES (?,?)', (iddependency, idpackage,))
if (self.entropyTools.is_user_in_entropy_group()) and \
(self.dbname.startswith(etpConst['serverdbid'])):
# force commit even if readonly, this will allow to automagically fix dependstable server side
self.connection.commit() # we don't care much about syncing the database since it's quite trivial
def addDependsRelationToDependsTable(self, iterable):
with self.WriteLock:
self.cursor.executemany('INSERT into dependstable VALUES (?,?)', iterable)
if (self.entropyTools.is_user_in_entropy_group()) and \
(self.dbname.startswith(etpConst['serverdbid'])):
# force commit even if readonly, this will allow to automagically fix dependstable server side
self.connection.commit() # we don't care much about syncing the database since it's quite trivial
def clearDependsTable(self):
if not self.doesTableExist("dependstable"): return
self.cursor.execute("DROP TABLE IF EXISTS dependstable")
'''
@description: recreate dependstable table in the chosen database, it's used for caching searchDepends requests
@input Nothing
@output: Nothing
'''
def regenerateDependsTable(self, output = True):
depends = self.listAllDependencies()
self.createDependsTable()
count = 0
total = len(depends)
mydata = set()
am = self.atomMatch
up = self.updateProgress
for iddep,atom in depends:
count += 1
if output:
up( red("Resolving %s") % (atom,), importance = 0,
type = "info", back = True, count = (count,total)
)
idpackage, rc = am(atom)
if idpackage == -1: continue
mydata.add((iddep,idpackage))
if mydata: self.addDependsRelationToDependsTable(mydata)
# now validate dependstable
self.sanitizeDependsTable()
def moveCountersToBranch(self, to_branch):
with self.WriteLock:
self.cursor.execute('UPDATE counters SET branch = (?)', to_branch)
self.commitChanges()
self.clearCache()
########################################################
####
## Dependency handling functions
#
def atomMatchFetchCache(self, *args):
if self.xcache:
cached = self.dumpTools.loadobj("%s/%s/%s" % (self.dbMatchCacheKey,self.dbname,hash(tuple(args)),))
if cached != None: return cached
def atomMatchStoreCache(self, *args, **kwargs):
if self.xcache:
if self.ServiceInterface:
self.ServiceInterface.Cacher.push("%s/%s/%s" % (self.dbMatchCacheKey,self.dbname,hash(tuple(args)),),kwargs.get('result')[:])
else:
self.dumpTools.dumpobj("%s/%s/%s" % (self.dbMatchCacheKey,self.dbname,hash(tuple(args)),),kwargs.get('result')[:])
def _idpackageValidator_live(self, idpackage, reponame):
if (idpackage,reponame) in self.ServiceInterface.SystemSettings['live_packagemasking']['mask_matches']:
# do not cache this
return -1,self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_live_mask']
elif (idpackage,reponame) in self.ServiceInterface.SystemSettings['live_packagemasking']['unmask_matches']:
return idpackage,self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_live_unmask']
def _idpackageValidator_user_package_mask(self, idpackage, reponame, live):
# check if user package.mask needs it masked
mykw = "%smask_ids" % (reponame,)
user_package_mask_ids = self.ServiceInterface.SystemSettings.get(mykw)
if not isinstance(user_package_mask_ids,set):
user_package_mask_ids = set()
for atom in self.ServiceInterface.SystemSettings['mask']:
matches, r = self.atomMatch(atom, multiMatch = True, packagesFilter = False)
if r != 0: continue
user_package_mask_ids |= set(matches)
self.ServiceInterface.SystemSettings[mykw] = user_package_mask_ids
if idpackage in user_package_mask_ids:
# sorry, masked
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_package_mask']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = -1,myr
return -1,myr
def _idpackageValidator_user_package_unmask(self, idpackage, reponame, live):
# see if we can unmask by just lookin into user package.unmask stuff -> self.ServiceInterface.SystemSettings['unmask']
mykw = "%sunmask_ids" % (reponame,)
user_package_unmask_ids = self.ServiceInterface.SystemSettings.get(mykw)
if not isinstance(user_package_unmask_ids,set):
user_package_unmask_ids = set()
for atom in self.ServiceInterface.SystemSettings['unmask']:
matches, r = self.atomMatch(atom, multiMatch = True, packagesFilter = False)
if r != 0: continue
user_package_unmask_ids |= set(matches)
self.ServiceInterface.SystemSettings[mykw] = user_package_unmask_ids
if idpackage in user_package_unmask_ids:
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_package_unmask']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = idpackage,myr
return idpackage,myr
def _idpackageValidator_packages_db_mask(self, idpackage, reponame, live):
# check if repository packages.db.mask needs it masked
repos_mask = self.ServiceInterface.SystemSettings['repos_mask']
repomask = repos_mask.get(reponame)
if isinstance(repomask,set):
# first, seek into generic masking, all branches
mask_repo_id = "%s_ids@@:of:%s" % (reponame,reponame,) # avoid issues with repository names
repomask_ids = repos_mask.get(mask_repo_id)
if not isinstance(repomask_ids,set):
repomask_ids = set()
for atom in repomask:
matches, r = self.atomMatch(atom, multiMatch = True, packagesFilter = False)
if r != 0: continue
repomask_ids |= set(matches)
repos_mask[mask_repo_id] = repomask_ids
if idpackage in repomask_ids:
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['repository_packages_db_mask']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = -1,myr
return -1,myr
def _idpackageValidator_package_license_mask(self, idpackage, reponame, live):
if self.ServiceInterface.SystemSettings['license_mask']:
mylicenses = self.retrieveLicense(idpackage)
mylicenses = mylicenses.strip().split()
lic_mask = self.ServiceInterface.SystemSettings['license_mask']
for mylicense in mylicenses:
if mylicense not in lic_mask: continue
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_license_mask']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = -1,myr
return -1,myr
def _idpackageValidator_keyword_mask(self, idpackage, reponame, live):
mykeywords = self.retrieveKeywords(idpackage)
# XXX WORKAROUND
if not mykeywords: mykeywords = [''] # ** is fine then
# firstly, check if package keywords are in etpConst['keywords']
# (universal keywords have been merged from package.mask)
for key in etpConst['keywords']:
if key not in mykeywords: continue
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['system_keyword']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = idpackage,myr
return idpackage,myr
# if we get here, it means we didn't find mykeywords in etpConst['keywords']
# we need to seek self.ServiceInterface.SystemSettings['keywords']
# seek in repository first
if reponame in self.ServiceInterface.SystemSettings['keywords']['repositories']:
for keyword in self.ServiceInterface.SystemSettings['keywords']['repositories'][reponame]:
if keyword not in mykeywords: continue
keyword_data = self.ServiceInterface.SystemSettings['keywords']['repositories'][reponame].get(keyword)
if not keyword_data: continue
if "*" in keyword_data: # all packages in this repo with keyword "keyword" are ok
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_repo_package_keywords_all']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = idpackage,myr
return idpackage,myr
kwd_key = "%s_ids" % (keyword,)
keyword_data_ids = self.ServiceInterface.SystemSettings['keywords']['repositories'][reponame].get(kwd_key)
if not isinstance(keyword_data_ids,set):
keyword_data_ids = set()
for atom in keyword_data:
matches, r = self.atomMatch(atom, multiMatch = True, packagesFilter = False)
if r != 0: continue
keyword_data_ids |= matches
self.ServiceInterface.SystemSettings['keywords']['repositories'][reponame][kwd_key] = keyword_data_ids
if idpackage in keyword_data_ids:
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_repo_package_keywords']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = idpackage,myr
return idpackage,myr
# if we get here, it means we didn't find a match in repositories
# so we scan packages, last chance
for keyword in self.ServiceInterface.SystemSettings['keywords']['packages']:
# first of all check if keyword is in mykeywords
if keyword not in mykeywords: continue
keyword_data = self.ServiceInterface.SystemSettings['keywords']['packages'].get(keyword)
if not keyword_data: continue
kwd_key = "%s_ids" % (keyword,)
keyword_data_ids = self.ServiceInterface.SystemSettings['keywords']['packages'].get(reponame+kwd_key)
if not isinstance(keyword_data_ids,set):
keyword_data_ids = set()
for atom in keyword_data:
# match atom
matches, r = self.atomMatch(atom, multiMatch = True, packagesFilter = False)
if r != 0: continue
keyword_data_ids |= matches
self.ServiceInterface.SystemSettings['keywords']['packages'][reponame+kwd_key] = keyword_data_ids
if idpackage in keyword_data_ids:
# valid!
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['user_package_keywords']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = idpackage,myr
return idpackage,myr
# function that validate one atom by reading keywords settings
# self.ServiceInterface.package_match_validator_cache = {} >> function cache
def idpackageValidator(self,idpackage, live = True):
if self.dbname == etpConst['clientdbid']:
return idpackage,0
elif self.dbname.startswith(etpConst['serverdbid']):
return idpackage,0
if self.ServiceInterface == None:
return idpackage,0
elif not hasattr(self.ServiceInterface,'SystemSettings'):
return idpackage,0
reponame = self.dbname[5:]
cached = self.ServiceInterface.package_match_validator_cache.get((idpackage,reponame,live))
if cached != None:
return cached
# avoid memleaks
if len(self.ServiceInterface.package_match_validator_cache) > 10000:
self.ServiceInterface.package_match_validator_cache.clear()
if live:
data = self._idpackageValidator_live(idpackage, reponame)
if data: return data
data = self._idpackageValidator_user_package_mask(idpackage, reponame, live)
if data: return data
data = self._idpackageValidator_user_package_unmask(idpackage, reponame, live)
if data: return data
data = self._idpackageValidator_packages_db_mask(idpackage, reponame, live)
if data: return data
data = self._idpackageValidator_package_license_mask(idpackage, reponame, live)
if data: return data
data = self._idpackageValidator_keyword_mask(idpackage, reponame, live)
if data: return data
# holy crap, can't validate
myr = self.ServiceInterface.SystemSettings['pkg_masking_reference']['completely_masked']
self.ServiceInterface.package_match_validator_cache[(idpackage,reponame,live)] = -1,myr
return -1,myr
# packages filter used by atomMatch, input must me foundIDs, a list like this:
# [608,1867]
def packagesFilter(self, results):
# keywordsFilter ONLY FILTERS results if
# self.dbname.startswith(etpConst['dbnamerepoprefix']) => repository database is open
if not self.dbname.startswith(etpConst['dbnamerepoprefix']):
return results
newresults = set()
for idpackage in results:
idpackage, reason = self.idpackageValidator(idpackage)
if idpackage == -1: continue
newresults.add(idpackage)
return newresults
def __filterSlot(self, idpackage, slot):
if slot == None:
return idpackage
dbslot = self.retrieveSlot(idpackage)
if str(dbslot) == str(slot):
return idpackage
def __filterTag(self, idpackage, tag, operators):
if tag == None:
return idpackage
dbtag = self.retrieveVersionTag(idpackage)
compare = cmp(tag,dbtag)
if not operators or operators == "=":
if compare == 0:
return idpackage
else:
return self.__do_operator_compare(idpackage, operators, compare)
def __filterUse(self, idpackage, use):
if not use:
return idpackage
pkguse = self.retrieveUseflags(idpackage)
disabled = set([x[1:] for x in use if x.startswith("-")])
enabled = set([x for x in use if not x.startswith("-")])
enabled_not_satisfied = enabled - pkguse
# check enabled
if enabled_not_satisfied:
return None
# check disabled
disabled_not_satisfied = disabled - pkguse
if len(disabled_not_satisfied) != len(disabled):
return None
return idpackage
def __do_operator_compare(self, token, operators, compare):
if operators == ">" and compare == -1:
return token
elif operators == ">=" and compare < 1:
return token
elif operators == "<" and compare == 1:
return token
elif operators == "<=" and compare > -1:
return token
def __filterSlotTagUse(self, foundIDs, slot, tag, use, operators):
def myfilter(idpackage):
idpackage = self.__filterSlot(idpackage, slot)
if not idpackage:
return False
idpackage = self.__filterUse(idpackage, use)
if not idpackage:
return False
idpackage = self.__filterTag(idpackage, tag, operators)
if not idpackage:
return False
return True
return set(filter(myfilter,foundIDs))
'''
@description: matches the user chosen package name+ver, if possibile, in a single repository
@input atom: string, atom to match
@input caseSensitive: bool, should the atom be parsed case sensitive?
@input matchSlot: string, match atoms with the provided slot
@input multiMatch: bool, return all the available atoms
@input matchBranches: tuple or list, match packages only in the specified branches
@input matchTag: match packages only for the specified tag
@input matchUse: match packages only if it owns the specified use flags
@input packagesFilter: enable/disable package.mask/.keywords/.unmask filter
@output: the package id, if found, otherwise -1 plus the status, 0 = ok, 1 = error
'''
def atomMatch(self, atom, caseSensitive = True, matchSlot = None, multiMatch = False, matchBranches = (), matchTag = None, matchUse = (), packagesFilter = True, matchRevision = None, extendedResults = False, useCache = True):
if not atom:
return -1,1
if useCache:
cached = self.atomMatchFetchCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults
)
if isinstance(cached,tuple):
return cached
atomTag = self.entropyTools.dep_gettag(atom)
try:
atomUse = self.entropyTools.dep_getusedeps(atom)
except exceptionTools.InvalidAtom:
atomUse = ()
atomSlot = self.entropyTools.dep_getslot(atom)
atomRev = self.entropyTools.dep_get_entropy_revision(atom)
# use match
scan_atom = self.entropyTools.remove_usedeps(atom)
if (not matchUse) and (atomUse):
matchUse = atomUse
# tag match
scan_atom = self.entropyTools.remove_tag(scan_atom)
if (matchTag == None) and (atomTag != None):
matchTag = atomTag
# slot match
scan_atom = self.entropyTools.remove_slot(scan_atom)
if (matchSlot == None) and (atomSlot != None):
matchSlot = atomSlot
# revision match
scan_atom = self.entropyTools.remove_entropy_revision(scan_atom)
if (matchRevision == None) and (atomRev != None):
matchRevision = atomRev
branch_list = ()
direction = ''
justname = True
pkgkey = ''
strippedAtom = ''
foundIDs = []
dbpkginfo = set()
if scan_atom:
while 1:
pkgversion = ''
# check for direction
strippedAtom = self.entropyTools.dep_getcpv(scan_atom)
if scan_atom[-1] == "*":
strippedAtom += "*"
direction = scan_atom[0:len(scan_atom)-len(strippedAtom)]
justname = self.entropyTools.isjustname(strippedAtom)
pkgkey = strippedAtom
if justname == 0:
# get version
data = self.entropyTools.catpkgsplit(strippedAtom)
if data == None: break # badly formatted
pkgversion = data[2]+"-"+data[3]
pkgkey = self.entropyTools.dep_getkey(strippedAtom)
splitkey = pkgkey.split("/")
if (len(splitkey) == 2):
pkgname = splitkey[1]
pkgcat = splitkey[0]
else:
pkgname = splitkey[0]
pkgcat = "null"
branch_list = (self.db_branch,)
if matchBranches:
# force to tuple for security
branch_list = tuple(matchBranches)
break
if branch_list:
# IDs found in the database that match our search
foundIDs = self.__generate_found_ids_match(branch_list, pkgkey, pkgname, pkgcat, caseSensitive, multiMatch)
### FILTERING
# filter slot and tag
if foundIDs:
foundIDs = self.__filterSlotTagUse(foundIDs, matchSlot, matchTag, matchUse, direction)
if packagesFilter:
foundIDs = self.packagesFilter(foundIDs)
### END FILTERING
if foundIDs:
dbpkginfo = self.__handle_found_ids_match(foundIDs, direction, matchTag, matchRevision, justname, strippedAtom, atom, pkgversion)
if not dbpkginfo:
if extendedResults:
x = (-1,1,None,None,None)
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x,1)
)
return x,1
else:
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (-1,1)
)
return -1,1
if multiMatch:
if extendedResults:
x = set([(x[0],0,x[1],self.retrieveVersionTag(x[0]),self.retrieveRevision(x[0])) for x in dbpkginfo])
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x,0)
)
return x,0
else:
x = set([x[0] for x in dbpkginfo])
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x,0)
)
return x,0
if len(dbpkginfo) == 1:
x = dbpkginfo.pop()
if extendedResults:
x = (x[0],0,x[1],self.retrieveVersionTag(x[0]),self.retrieveRevision(x[0]))
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x,0)
)
return x,0
else:
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x[0],0)
)
return x[0],0
dbpkginfo = list(dbpkginfo)
pkgdata = {}
versions = set()
for x in dbpkginfo:
info_tuple = (x[1],self.retrieveVersionTag(x[0]),self.retrieveRevision(x[0]))
versions.add(info_tuple)
pkgdata[info_tuple] = x[0]
newer = self.entropyTools.getEntropyNewerVersion(list(versions))[0]
x = pkgdata[newer]
if extendedResults:
x = (x,0,newer[0],newer[1],newer[2])
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x,0)
)
return x,0
else:
self.atomMatchStoreCache(
atom, caseSensitive, matchSlot,
multiMatch, matchBranches, matchTag,
matchUse, packagesFilter, matchRevision,
extendedResults, result = (x,0)
)
return x,0
def __generate_found_ids_match(self, branch_list, pkgkey, pkgname, pkgcat, caseSensitive, multiMatch):
foundIDs = set()
for idx in branch_list:
if pkgcat == "null":
results = self.searchPackagesByName(
pkgname, sensitive = caseSensitive,
branch = idx, justid = True
)
else:
results = self.searchPackagesByNameAndCategory(
name = pkgname, category = pkgcat, branch = idx,
sensitive = caseSensitive, justid = True
)
mypkgcat = pkgcat
mypkgname = pkgname
virtual = False
# if it's a PROVIDE, search with searchProvide
# there's no package with that name
if (not results) and (mypkgcat == "virtual"):
virtuals = self.searchProvide(pkgkey, branch = idx, justid = True)
if virtuals:
virtual = True
mypkgname = self.retrieveName(virtuals[0])
mypkgcat = self.retrieveCategory(virtuals[0])
results = virtuals
# now validate
if not results:
continue # search into a stabler branch
elif (len(results) > 1):
# if it's because category differs, it's a problem
foundCat = None
cats = set()
for idpackage in results:
cat = self.retrieveCategory(idpackage)
cats.add(cat)
if (cat == mypkgcat) or ((not virtual) and (mypkgcat == "virtual") and (cat == mypkgcat)):
# in case of virtual packages only (that they're not stored as provide)
foundCat = cat
# if we found something at least...
if (not foundCat) and (len(cats) == 1) and (mypkgcat in ("virtual","null")):
foundCat = sorted(list(cats))[0]
if not foundCat:
# got the issue
continue
# we can use foundCat
mypkgcat = foundCat
# we need to search using the category
if (not multiMatch) and (pkgcat == "null" or virtual):
# we searched by name, we need to search using category
results = self.searchPackagesByNameAndCategory(
name = mypkgname, category = mypkgcat,
branch = idx, sensitive = caseSensitive, justid = True
)
# validate again
if not results:
continue # search into another branch
# if we get here, we have found the needed IDs
foundIDs |= set(results)
break
else:
idpackage = results[0]
# if mypkgcat is virtual, we can force
if (mypkgcat == "virtual") and (not virtual):
# in case of virtual packages only (that they're not stored as provide)
mypkgcat = self.retrieveCategory(idpackage)
# check if category matches
if mypkgcat != "null":
foundCat = self.retrieveCategory(idpackage)
if mypkgcat == foundCat:
foundIDs.add(idpackage)
continue
foundIDs.add(idpackage)
break
return foundIDs
def __handle_found_ids_match(self, foundIDs, direction, matchTag, matchRevision, justname, strippedAtom, atom, pkgversion):
dbpkginfo = set()
# now we have to handle direction
if ((direction) or ((not direction) and (not justname)) or ((not direction) and (not justname) and strippedAtom.endswith("*"))) and foundIDs:
if (not justname) and \
((direction == "~") or (direction == "=") or \
(direction == '' and not justname) or (direction == '' and not justname and strippedAtom.endswith("*"))):
# any revision within the version specified OR the specified version
if (direction == '' and not justname):
direction = "="
# remove gentoo revision (-r0 if none)
if (direction == "="):
if (pkgversion.split("-")[-1] == "r0"):
pkgversion = self.entropyTools.remove_revision(pkgversion)
if (direction == "~"):
pkgrevision = self.entropyTools.dep_get_portage_revision(pkgversion)
pkgversion = self.entropyTools.remove_revision(pkgversion)
for idpackage in foundIDs:
dbver = self.retrieveVersion(idpackage)
if (direction == "~"):
myrev = self.entropyTools.dep_get_portage_revision(dbver)
myver = self.entropyTools.remove_revision(dbver)
if myver == pkgversion and pkgrevision <= myrev:
# found
dbpkginfo.add((idpackage,dbver))
else:
# media-libs/test-1.2* support
if pkgversion[-1] == "*":
if dbver.startswith(pkgversion[:-1]):
dbpkginfo.add((idpackage,dbver))
elif (matchRevision != None) and (pkgversion == dbver):
dbrev = self.retrieveRevision(idpackage)
if dbrev == matchRevision:
dbpkginfo.add((idpackage,dbver))
elif (pkgversion == dbver) and (matchRevision == None):
dbpkginfo.add((idpackage,dbver))
elif (direction.find(">") != -1) or (direction.find("<") != -1):
if not justname:
# remove revision (-r0 if none)
if pkgversion.endswith("r0"):
# remove
self.entropyTools.remove_revision(pkgversion)
for idpackage in foundIDs:
revcmp = 0
tagcmp = 0
if matchRevision != None:
dbrev = self.retrieveRevision(idpackage)
revcmp = cmp(matchRevision,dbrev)
if matchTag != None:
dbtag = self.retrieveVersionTag(idpackage)
tagcmp = cmp(matchTag,dbtag)
dbver = self.retrieveVersion(idpackage)
pkgcmp = self.entropyTools.compareVersions(pkgversion,dbver)
if isinstance(pkgcmp,tuple):
failed = pkgcmp[1]
if failed == 0:
failed = pkgversion
else:
failed = dbver
# I am sorry, but either pkgversion or dbver are invalid
self.updateProgress(
bold("atomMatch: ")+red("%s %s %s %s %s. %s: %s") % (
_("comparison between"),
pkgversion,
_("and"),
dbver,
_("failed"),
_("Wrong syntax for"),
failed,
),
importance = 1,
type = "error",
header = darkred(" !!! ")
)
mytxt = "%s: %s, cmp(): %s, %s: %s" % (
_("from atom"),
atom,
pkgcmp,
_("failed"),
failed,
)
raise exceptionTools.InvalidVersionString("InvalidVersionString: %s" % (mytxt, ))
if direction == ">":
if pkgcmp < 0:
dbpkginfo.add((idpackage,dbver))
elif (matchRevision != None) and pkgcmp <= 0 and revcmp < 0:
#print "found >",self.retrieveAtom(idpackage)
dbpkginfo.add((idpackage,dbver))
elif (matchTag != None) and tagcmp < 0:
dbpkginfo.add((idpackage,dbver))
elif direction == "<":
if pkgcmp > 0:
dbpkginfo.add((idpackage,dbver))
elif (matchRevision != None) and pkgcmp >= 0 and revcmp > 0:
#print "found <",self.retrieveAtom(idpackage)
dbpkginfo.add((idpackage,dbver))
elif (matchTag != None) and tagcmp > 0:
dbpkginfo.add((idpackage,dbver))
elif direction == ">=":
if (matchRevision != None) and pkgcmp <= 0:
if pkgcmp == 0:
if revcmp <= 0:
dbpkginfo.add((idpackage,dbver))
#print "found >=",self.retrieveAtom(idpackage)
else:
dbpkginfo.add((idpackage,dbver))
elif pkgcmp <= 0 and matchRevision == None:
dbpkginfo.add((idpackage,dbver))
elif (matchTag != None) and tagcmp <= 0:
dbpkginfo.add((idpackage,dbver))
elif direction == "<=":
if (matchRevision != None) and pkgcmp >= 0:
if pkgcmp == 0:
if revcmp >= 0:
dbpkginfo.add((idpackage,dbver))
#print "found <=",self.retrieveAtom(idpackage)
else:
dbpkginfo.add((idpackage,dbver))
elif pkgcmp >= 0 and matchRevision == None:
dbpkginfo.add((idpackage,dbver))
elif (matchTag != None) and tagcmp >= 0:
dbpkginfo.add((idpackage,dbver))
else: # just the key
dbpkginfo = set([(x,self.retrieveVersion(x)) for x in foundIDs])
return dbpkginfo
class EmailSender:
def __init__(self):
import smtplib
self.smtplib = smtplib
from email.mime.audio import MIMEAudio
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email import encoders
from email.message import Message
import mimetypes
self.smtpuser = None
self.smtppassword = None
self.smtphost = 'localhost'
self.smtpport = 25
self.text = MIMEText
self.mimefile = MIMEBase
self.audio = MIMEAudio
self.image = MIMEImage
self.multipart = MIMEMultipart
self.default_sender = self.smtp_send
self.mimetypes = mimetypes
self.encoders = encoders
self.Message = Message
def smtp_send(self, sender, destinations, message):
s = self.smtplib.SMTP(self.smtphost,self.smtpport)
if self.smtpuser and self.smtppassword:
s.login(self.smtpuser,self.smtppassword)
s.sendmail(sender, destinations, message)
s.quit()
def send_text_email(self, sender_email, destination_emails, subject, content):
# Create a text/plain message
if isinstance(content,unicode):
content = content.encode('utf-8')
if isinstance(subject,unicode):
subject = subject.encode('utf-8')
msg = self.text(content)
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = ', '.join(destination_emails)
return self.default_sender(sender_email, destination_emails, msg.as_string())
def send_mime_email(self, sender_email, destination_emails, subject, content, files):
outer = self.multipart()
outer['Subject'] = subject
outer['From'] = sender_email
outer['To'] = ', '.join(destination_emails)
outer.preamble = subject
mymsg = self.text(content)
outer.attach(mymsg)
# attach files
for myfile in files:
if not (os.path.isfile(myfile) and os.access(myfile,os.R_OK)):
continue
ctype, encoding = self.mimetypes.guess_type(myfile)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'image':
fp = open(myfile, 'rb')
msg = self.image(fp.read(), _subtype = subtype)
fp.close()
elif maintype == 'audio':
fp = open(myfile, 'rb')
msg = self.audio(fp.read(), _subtype = subtype)
fp.close()
else:
fp = open(myfile, 'rb')
msg = self.mimefile(maintype, subtype)
msg.set_payload(fp.read())
fp.close()
self.encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment', filename = os.path.basename(myfile))
outer.attach(msg)
composed = outer.as_string()
return self.default_sender(sender_email, destination_emails, composed)