2461 lines
99 KiB
Python
2461 lines
99 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|
'''
|
|
# DESCRIPTION:
|
|
# Entropy Object Oriented Interface
|
|
|
|
Copyright (C) 2007-2009 Fabio Erculiani
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
'''
|
|
from __future__ import with_statement
|
|
import os
|
|
import subprocess
|
|
import time
|
|
import shutil
|
|
from entropy.const import *
|
|
from entropy.exceptions import *
|
|
from entropy.i18n import _
|
|
from entropy.output import TextInterface, brown, blue, bold, darkgreen, \
|
|
darkblue, red, purple, darkred, print_info, print_error, print_warning
|
|
from entropy.misc import TimeScheduled
|
|
from entropy.db import dbapi2, LocalRepository
|
|
from entropy.client.interfaces.client import Client
|
|
from entropy.cache import EntropyCacher
|
|
|
|
class Package:
|
|
|
|
import entropy.tools as entropyTools
|
|
import entropy.dump as dumpTools
|
|
def __init__(self, EquoInstance):
|
|
|
|
if not isinstance(EquoInstance,Client):
|
|
mytxt = _("A valid Client instance or subclass is needed")
|
|
raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
|
|
self.Entropy = EquoInstance
|
|
|
|
self.Cacher = EntropyCacher()
|
|
self.infoDict = {}
|
|
self.prepared = False
|
|
self.matched_atom = ()
|
|
self.valid_actions = ("source","fetch","multi_fetch","remove",
|
|
"remove_conflict","install","config"
|
|
)
|
|
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 PermissionDenied("PermissionDenied: %s" % (mytxt,))
|
|
|
|
def error_on_not_prepared(self):
|
|
if not self.prepared:
|
|
mytxt = _("Not yet prepared")
|
|
raise PermissionDenied("PermissionDenied: %s" % (mytxt,))
|
|
|
|
def check_action_validity(self, action):
|
|
if action not in self.valid_actions:
|
|
mytxt = _("Action must be in")
|
|
raise InvalidData("InvalidData: %s %s" % (mytxt,self.valid_actions,))
|
|
|
|
def match_checksum(self, repository = None, checksum = None,
|
|
download = None, signatures = None):
|
|
|
|
self.error_on_not_prepared()
|
|
|
|
if repository == None:
|
|
repository = self.infoDict['repository']
|
|
if checksum == None:
|
|
checksum = self.infoDict['checksum']
|
|
if download == None:
|
|
download = self.infoDict['download']
|
|
if signatures == None:
|
|
signatures = self.infoDict['signatures']
|
|
|
|
def do_signatures_validation(signatures):
|
|
# check signatures, if available
|
|
if isinstance(signatures,dict):
|
|
for hash_type in sorted(signatures):
|
|
hash_val = signatures[hash_type]
|
|
# XXX workaround bug on unreleased
|
|
# entropy versions
|
|
if hash_val in signatures:
|
|
continue
|
|
elif hash_val == None:
|
|
continue
|
|
elif not hasattr(self.entropyTools,
|
|
'compare_%s' % (hash_type,)):
|
|
continue
|
|
|
|
self.Entropy.updateProgress(
|
|
"%s: %s" % (blue(_("Checking package hash")),
|
|
purple(hash_type.upper()),),
|
|
importance = 0,
|
|
type = "info",
|
|
header = red(" ## "),
|
|
back = True
|
|
)
|
|
cmp_func = getattr(self.entropyTools,
|
|
'compare_%s' % (hash_type,))
|
|
mydownload = os.path.join(etpConst['entropyworkdir'],
|
|
download)
|
|
valid = cmp_func(mydownload, hash_val)
|
|
if not valid:
|
|
self.Entropy.updateProgress(
|
|
"%s: %s %s" % (
|
|
darkred(_("Package hash")),
|
|
purple(hash_type.upper()),
|
|
darkred(_("does not match the recorded one")),
|
|
),
|
|
importance = 0,
|
|
type = "warning",
|
|
header = darkred(" ## ")
|
|
)
|
|
return 1
|
|
self.Entropy.updateProgress(
|
|
"%s %s" % (
|
|
purple(hash_type.upper()),
|
|
darkgreen(_("matches")),
|
|
),
|
|
importance = 0,
|
|
type = "info",
|
|
header = " : "
|
|
)
|
|
return 0
|
|
|
|
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(download,
|
|
checksum = checksum)
|
|
if dlcheck == 0:
|
|
basef = os.path.basename(download)
|
|
self.Entropy.updateProgress(
|
|
"%s: %s" % (
|
|
blue(_("Package checksum matches")),
|
|
darkgreen(basef),
|
|
),
|
|
importance = 0,
|
|
type = "info",
|
|
header = red(" ## ")
|
|
)
|
|
|
|
dlcheck = do_signatures_validation(signatures)
|
|
if dlcheck == 0:
|
|
self.infoDict['verified'] = True
|
|
match = True
|
|
break # file downloaded successfully
|
|
|
|
if dlcheck != 0:
|
|
dlcount += 1
|
|
self.Entropy.updateProgress(
|
|
darkred(_("Package checksum does not match. Redownloading... attempt #%s") % (dlcount,)),
|
|
importance = 0,
|
|
type = "warning",
|
|
header = darkred(" ## ")
|
|
)
|
|
fetch = self.Entropy.fetch_file_on_mirrors(
|
|
repository,
|
|
self.Entropy.get_branch_from_download_relative_uri(download),
|
|
download,
|
|
checksum,
|
|
fetch_abort_function = self.fetch_abort_function
|
|
)
|
|
if fetch != 0:
|
|
self.Entropy.updateProgress(
|
|
blue(_("Cannot properly fetch package! Quitting.")),
|
|
importance = 0,
|
|
type = "error",
|
|
header = darkred(" ## ")
|
|
)
|
|
return fetch
|
|
|
|
# package is fetched, let's loop one more time
|
|
# to make sure to run all the checksum checks
|
|
continue
|
|
|
|
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
|
|
|
|
def multi_match_checksum(self):
|
|
rc = 0
|
|
for repository, branch, download, digest, signatures in self.infoDict['multi_checksum_list']:
|
|
rc = self.match_checksum(repository, digest, download, signatures)
|
|
if rc != 0: break
|
|
return rc
|
|
|
|
'''
|
|
@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):
|
|
|
|
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.entropyTools.spawn_function(
|
|
self.entropyTools.uncompress_tar_bz2,
|
|
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, self.dumpTools.pickle.PicklingError,):
|
|
# 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/Pickling Error for "+self.infoDict['pkgpath'])
|
|
rc = self.entropyTools.uncompress_tar_bz2(
|
|
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 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.open_repository(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.entropyTools.unpack_xpak(
|
|
xpakPath,
|
|
self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']
|
|
)
|
|
else:
|
|
self.infoDict['xpakstatus'] = None
|
|
del xpakdata
|
|
else:
|
|
self.infoDict['xpakstatus'] = self.entropyTools.extract_xpak(
|
|
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 __configure_package(self):
|
|
|
|
try: Spm = self.Entropy.Spm()
|
|
except: return 1
|
|
|
|
spm_atom = self.infoDict['key']+"-"+self.infoDict['version']
|
|
myebuild = Spm.get_vdb_path()+spm_atom+"/"+self.infoDict['key'].split("/")[1]+"-"+self.infoDict['version']+etpConst['spm']['source_build_ext']
|
|
if not os.path.isfile(myebuild):
|
|
return 2
|
|
|
|
self.Entropy.updateProgress(
|
|
brown(" Ebuild: pkg_config()"),
|
|
importance = 0,
|
|
header = red(" ##")
|
|
)
|
|
|
|
try:
|
|
rc = Spm.spm_doebuild(
|
|
myebuild,
|
|
mydo = "config",
|
|
tree = "bintree",
|
|
cpv = spm_atom
|
|
)
|
|
if rc == 1:
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"[PRE] ATTENTION Cannot properly run Spm pkg_config() for " + \
|
|
str(spm_atom)+". Something bad happened."
|
|
)
|
|
return 3
|
|
except Exception, e:
|
|
self.entropyTools.print_traceback()
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"[PRE] ATTENTION Cannot run Spm pkg_config() for "+spm_atom+"!! "+str(type(Exception))+": "+str(e)
|
|
)
|
|
mytxt = "%s: %s %s. %s. %s: %s, %s" % (
|
|
bold(_("QA")),
|
|
brown(_("Cannot run Spm pkg_config() for")),
|
|
bold(str(spm_atom)),
|
|
brown(_("Please report it")),
|
|
bold(_("Error")),
|
|
type(Exception),
|
|
e,
|
|
)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 0,
|
|
header = red(" ## ")
|
|
)
|
|
return 1
|
|
|
|
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'],))
|
|
|
|
protected_removable_config_files = {}
|
|
# 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(" ## ")
|
|
)
|
|
protected_removable_config_files = self.Entropy.clientDbconn.retrieveAutomergefiles(
|
|
self.infoDict['removeidpackage'], get_dict = True
|
|
)
|
|
self.__remove_package_from_database()
|
|
|
|
# Handle spm database
|
|
spm_atom = self.entropyTools.remove_tag(self.infoDict['removeatom'])
|
|
self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing from Spm: "+str(spm_atom))
|
|
self.__remove_package_from_spm_database(spm_atom)
|
|
|
|
self.__remove_content_from_system(protected_removable_config_files)
|
|
return 0
|
|
|
|
def __remove_content_from_system(self, protected_removable_config_files):
|
|
|
|
sys_root = etpConst['systemroot']
|
|
# load CONFIG_PROTECT and CONFIG_PROTECT_MASK
|
|
sys_settings = self.Entropy.SystemSettings
|
|
protect = self.Entropy.get_installed_package_config_protect(
|
|
self.infoDict['idpackage'])
|
|
mask = self.Entropy.get_installed_package_config_protect(
|
|
self.infoDict['idpackage'], mask = True)
|
|
sys_set_plg_id = \
|
|
etpConst['system_settings_plugins_ids']['client_plugin']
|
|
col_protect = sys_settings[sys_set_plg_id]['misc']['collisionprotect']
|
|
|
|
# remove files from system
|
|
directories = set()
|
|
for item in self.infoDict['removecontent']:
|
|
sys_root_item = sys_root+item
|
|
# 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
|
|
if isinstance(protected_item_test,unicode):
|
|
protected_item_test = protected_item_test.encode('utf-8')
|
|
|
|
in_mask, protected, x, do_continue = self._handle_config_protect(
|
|
protect, mask, None, protected_item_test,
|
|
do_allocation_check = False, do_quiet = True)
|
|
|
|
if do_continue: protected = True
|
|
|
|
# when files have not been modified by the user
|
|
# and they are inside a config protect directory
|
|
# we could even remove them directly
|
|
if in_mask:
|
|
|
|
oldprot_md5 = protected_removable_config_files.get(item)
|
|
if oldprot_md5 and os.path.exists(protected_item_test) and \
|
|
os.access(protected_item_test, os.R_OK):
|
|
|
|
in_system_md5 = self.entropyTools.md5sum(protected_item_test)
|
|
if oldprot_md5 == in_system_md5:
|
|
mytxt = "%s: %s" % (
|
|
darkgreen(_("Removing config file, never modified")),
|
|
blue(item),)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "info",
|
|
header = red(" ## ")
|
|
)
|
|
protected = False
|
|
do_continue = False
|
|
|
|
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(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 Spm database
|
|
@input spm package atom (cat/name+ver):
|
|
@output: 0 = all fine, <0 = error!
|
|
'''
|
|
def __remove_package_from_spm_database(self, atom):
|
|
|
|
# handle spm support
|
|
try:
|
|
Spm = self.Entropy.Spm()
|
|
except:
|
|
return -1 # no Spm support ??
|
|
|
|
portdb_dir = Spm.get_vdb_path()
|
|
remove_path = portdb_dir+atom
|
|
key = self.entropyTools.dep_getkey(atom)
|
|
others_installed = [x for x in Spm.search_keys(key) if \
|
|
self.entropyTools.dep_getkey(x) == key]
|
|
if atom in others_installed:
|
|
others_installed.remove(atom)
|
|
slot = self.infoDict['slot']
|
|
tag = self.infoDict['versiontag']
|
|
if (tag == slot) and tag: slot = "0"
|
|
|
|
def do_rm_path_atomic(xpath):
|
|
for my_el in os.listdir(xpath):
|
|
my_el = os.path.join(xpath, my_el)
|
|
try:
|
|
os.remove(my_el)
|
|
except OSError:
|
|
pass
|
|
try:
|
|
os.rmdir(xpath)
|
|
except OSError:
|
|
pass
|
|
|
|
if os.path.isdir(remove_path):
|
|
do_rm_path_atomic(remove_path)
|
|
|
|
if others_installed:
|
|
|
|
for myatom in others_installed:
|
|
myslot = Spm.get_installed_package_slot(myatom)
|
|
if myslot != slot:
|
|
continue
|
|
mydir = portdb_dir+myatom
|
|
if not os.path.isdir(mydir):
|
|
continue
|
|
do_rm_path_atomic(mydir)
|
|
|
|
else:
|
|
|
|
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()
|
|
os.rename(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, do_commit = False, do_cleanup = False):
|
|
self.error_on_not_prepared()
|
|
self.Entropy.clientDbconn.removePackage(self.infoDict['removeidpackage'],
|
|
do_commit = do_commit, do_cleanup = do_cleanup)
|
|
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, complete_path = 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},
|
|
complete_path = True
|
|
)
|
|
else:
|
|
key, slot = self.Entropy.clientDbconn.retrieveKeySlot(self.infoDict['removeidpackage'])
|
|
matches = self.Entropy.atom_match(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},
|
|
complete_path = 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")):
|
|
|
|
branch = self.Entropy.SystemSettings['repositories']['branch']
|
|
c_hash = self.Entropy.get_available_packages_chash(branch)
|
|
|
|
disk_cache = self.Entropy.get_available_packages_cache(
|
|
myhash = c_hash)
|
|
if disk_cache != None:
|
|
try:
|
|
|
|
# remove and old install
|
|
if self.infoDict['removeidpackage'] != -1:
|
|
taint = False
|
|
key = self.entropyTools.dep_getkey(
|
|
self.infoDict['removeatom'])
|
|
slot = self.infoDict['slot']
|
|
matches = self.Entropy.atom_match(key,
|
|
matchSlot = slot, multiRepo = True,
|
|
multiMatch = True)
|
|
for mymatch in matches[0]:
|
|
if mymatch not in disk_cache:
|
|
disk_cache.append(mymatch)
|
|
taint = True
|
|
if taint:
|
|
mydata = {}
|
|
mylist = []
|
|
for myidpackage,myrepo in disk_cache:
|
|
mydbc = self.Entropy.open_repository(myrepo)
|
|
mydata[mydbc.retrieveAtom(myidpackage)] = (myidpackage,myrepo)
|
|
for mykey in sorted(mydata):
|
|
mylist.append(mydata[mykey])
|
|
disk_cache = mylist
|
|
|
|
# install, doing here because matches[0]
|
|
# could contain self.matched_atoms
|
|
if self.matched_atom in disk_cache:
|
|
disk_cache.remove(self.matched_atom)
|
|
|
|
self.Cacher.push("%s%s" % (etpCache['world_available'],
|
|
c_hash), disk_cache)
|
|
|
|
except KeyError:
|
|
self.Cacher.push("%s%s" % (etpCache['world_available'],
|
|
c_hash), {})
|
|
|
|
elif not self.Entropy.xcache:
|
|
|
|
self.Entropy.clear_dump_cache(etpCache['world_available'])
|
|
|
|
|
|
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'],)
|
|
)
|
|
|
|
already_protected_config_files = {}
|
|
if self.infoDict['removeidpackage'] != -1:
|
|
already_protected_config_files = self.Entropy.clientDbconn.retrieveAutomergefiles(
|
|
self.infoDict['removeidpackage'], get_dict = True
|
|
)
|
|
|
|
# copy files over - install
|
|
# use fork? (in this case all the changed structures need to be pushed back)
|
|
rc = self.__move_image_to_system(already_protected_config_files)
|
|
if rc != 0:
|
|
return rc
|
|
del already_protected_config_files
|
|
|
|
# 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 spm 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
|
|
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"Removing Entropy and Spm database entry for %s" % (
|
|
self.infoDict['removeatom'],)
|
|
)
|
|
|
|
self.Entropy.updateProgress(
|
|
blue(_("Cleaning old package files...")),
|
|
importance = 1,
|
|
type = "info",
|
|
header = red(" ## ")
|
|
)
|
|
self.__remove_package()
|
|
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"Installing new Spm database entry: %s" % (self.infoDict['atom'],)
|
|
)
|
|
rc = self._install_package_into_spm_database(newidpackage)
|
|
|
|
return rc
|
|
|
|
'''
|
|
@description: inject the database information into the Gentoo database
|
|
@output: 0 = all fine, !=0 = error!
|
|
'''
|
|
def _install_package_into_spm_database(self, newidpackage):
|
|
|
|
# handle spm support
|
|
try:
|
|
Spm = self.Entropy.Spm()
|
|
except:
|
|
return -1 # no Spm support
|
|
|
|
portdb_dir = Spm.get_vdb_path()
|
|
if not os.path.isdir(portdb_dir):
|
|
os.makedirs(portdb_dir)
|
|
|
|
category = self.infoDict['category']
|
|
name = self.infoDict['name']
|
|
key = category + "/" + name
|
|
|
|
atomsfound = set()
|
|
dbdirs = os.listdir(portdb_dir)
|
|
if category in dbdirs:
|
|
catdirs = os.listdir(portdb_dir + "/" + category)
|
|
dirsfound = set([category + "/" + x for x in catdirs if \
|
|
key == self.entropyTools.dep_getkey(category + "/" + x)])
|
|
atomsfound.update(dirsfound)
|
|
|
|
### REMOVE
|
|
# parse slot and match and remove
|
|
if atomsfound:
|
|
pkg_to_remove = None
|
|
for atom in atomsfound:
|
|
atomslot = Spm.get_installed_package_slot(atom)
|
|
# get slot from spm db
|
|
if atomslot == self.infoDict['slot']:
|
|
pkg_to_remove = atom
|
|
break
|
|
if pkg_to_remove:
|
|
remove_path = portdb_dir + pkg_to_remove
|
|
shutil.rmtree(remove_path, True)
|
|
try:
|
|
os.rmdir(remove_path)
|
|
except OSError:
|
|
pass
|
|
del atomsfound
|
|
|
|
# we now install it
|
|
xpak_rel_path = etpConst['entropyxpakdatarelativepath']
|
|
if ((self.infoDict['xpakstatus'] != None) and \
|
|
os.path.isdir( self.infoDict['xpakpath'] + "/" + xpak_rel_path)) 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'] + "/" + xpak_rel_path
|
|
|
|
if not os.path.isdir(portdb_dir + category):
|
|
os.makedirs(portdb_dir + category, 0755)
|
|
destination = portdb_dir + category + "/" + 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 spm 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(" ## ")
|
|
)
|
|
|
|
user_inst_source = etpConst['install_sources']['user']
|
|
if self.infoDict['install_source'] != user_inst_source:
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"Not updating Portage world file for: %s" % (self.infoDict['atom'],)
|
|
)
|
|
# only user selected packages in Portage world file
|
|
return 0
|
|
|
|
# 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.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.entropyTools.print_traceback(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.open_repository(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 # spm counter will be set in self._install_package_into_spm_database()
|
|
# there is no need to store needed paths into db
|
|
if data.get('needed_paths'):
|
|
del data['needed_paths']
|
|
|
|
idpackage, rev, x = self.Entropy.clientDbconn.handlePackage(
|
|
etpData = data, forcedRevision = data['revision'],
|
|
formattedContent = True)
|
|
|
|
# update datecreation
|
|
ctime = self.entropyTools.get_current_unix_time()
|
|
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'], self.infoDict['install_source'])
|
|
|
|
automerge_data = self.infoDict.get('configprotect_data')
|
|
if automerge_data:
|
|
self.Entropy.clientDbconn.insertAutomergefiles(idpackage,
|
|
automerge_data)
|
|
|
|
# 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.open_repository(self.infoDict['repository'])
|
|
package_content = dbconn.retrieveContent(self.infoDict['idpackage'], extended = True, formatted = True)
|
|
contents = sorted(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, already_protected_config_files):
|
|
|
|
# load CONFIG_PROTECT and its mask
|
|
repoid = self.infoDict['repository']
|
|
protect = self.Entropy.get_package_match_config_protect(
|
|
self.matched_atom)
|
|
mask = self.Entropy.get_package_match_config_protect(
|
|
self.matched_atom, mask = True)
|
|
sys_root = etpConst['systemroot']
|
|
sys_set_plg_id = \
|
|
etpConst['system_settings_plugins_ids']['client_plugin']
|
|
col_protect = self.Entropy.SystemSettings[sys_set_plg_id]['misc']['collisionprotect']
|
|
items_installed = set()
|
|
|
|
# setup imageDir properly
|
|
imageDir = self.infoDict['imagedir']
|
|
encoded_imageDir = imageDir.encode('utf-8')
|
|
movefile = self.entropyTools.movefile
|
|
|
|
# merge data into system
|
|
for currentdir,subdirs,files in os.walk(encoded_imageDir):
|
|
# 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.entropyTools.ebeep(20)
|
|
os.remove(rootdir)
|
|
|
|
# if our directory is a symlink instead, then copy the symlink
|
|
if os.path.islink(imagepathDir):
|
|
|
|
# if our live system features a directory instead of
|
|
# a symlink, we should consider removing the directory
|
|
if (not os.path.islink(rootdir)) and os.path.isdir(rootdir):
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"WARNING!!! %s is a directory when it should be a symlink !! Removing in 20 seconds..." % (rootdir,)
|
|
)
|
|
mytxt = darkred(_("%s is a directory when should be a symlink !! Removing in 20 seconds...") % (rootdir,))
|
|
self.Entropy.updateProgress(
|
|
red("QA: ")+mytxt,
|
|
importance = 1,
|
|
type = "warning",
|
|
header = red(" !!! ")
|
|
)
|
|
self.entropyTools.ebeep(20)
|
|
shutil.rmtree(rootdir, True)
|
|
os.rmdir(rootdir)
|
|
|
|
tolink = os.readlink(imagepathDir)
|
|
live_tolink = None
|
|
if os.path.islink(rootdir):
|
|
live_tolink = os.readlink(rootdir)
|
|
if tolink != live_tolink:
|
|
if os.path.lexists(rootdir):
|
|
# at this point, it must be a file
|
|
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 doesn't need permissions, also until os.walk ends they might be broken
|
|
# XXX also, added os.access() check because there might be directories/files unwritable
|
|
# 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
|
|
|
|
prot_old_tofile = tofile[len(sys_root):]
|
|
pre_tofile = tofile[:]
|
|
in_mask, protected, tofile, do_continue = self._handle_config_protect(
|
|
protect, mask, fromfile, tofile)
|
|
|
|
# collect new config automerge data
|
|
if in_mask and os.path.exists(fromfile):
|
|
try:
|
|
prot_md5 = self.entropyTools.md5sum(fromfile)
|
|
self.infoDict['configprotect_data'].append(
|
|
(prot_old_tofile,prot_md5,))
|
|
except (IOError,):
|
|
pass
|
|
|
|
# check if it's really necessary to protect file
|
|
if protected:
|
|
|
|
try:
|
|
|
|
# second task
|
|
oldprot_md5 = already_protected_config_files.get(
|
|
prot_old_tofile)
|
|
|
|
if oldprot_md5 and os.path.exists(pre_tofile) and \
|
|
os.access(pre_tofile, os.R_OK):
|
|
|
|
in_system_md5 = self.entropyTools.md5sum(pre_tofile)
|
|
if oldprot_md5 == in_system_md5:
|
|
# we can merge it, files, even if
|
|
# contains changes have not been modified
|
|
# by the user
|
|
mytxt = "%s: %s" % (
|
|
darkgreen(_("Automerging config file, never modified")),
|
|
blue(pre_tofile),)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "info",
|
|
header = red(" ## ")
|
|
)
|
|
protected = False
|
|
do_continue = False
|
|
tofile = pre_tofile
|
|
|
|
except (IOError,):
|
|
pass
|
|
|
|
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.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
|
|
|
|
# XXX
|
|
# XXX moving file using the raw format like portage does
|
|
# XXX
|
|
done = movefile(fromfile, tofile, src_basedir = encoded_imageDir)
|
|
if not done:
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"WARNING!!! Error during file move to system: %s => %s" % (fromfile,tofile,)
|
|
)
|
|
mytxt = "%s: %s => %s, %s" % (_("File move error"),fromfile,tofile,_("please report"),)
|
|
self.Entropy.updateProgress(
|
|
red("QA: ")+darkred(mytxt),
|
|
importance = 1,
|
|
type = "warning",
|
|
header = red(" !!! ")
|
|
)
|
|
return 4
|
|
|
|
except IOError, e:
|
|
# try to move forward, sometimes packages might be
|
|
# fucked up and contain broken things
|
|
if e.errno != 2: raise
|
|
|
|
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
|
|
# LocalRepository.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, do_quiet = False):
|
|
|
|
protected = False
|
|
tofile_before_protect = tofile
|
|
do_continue = False
|
|
in_mask = False
|
|
|
|
try:
|
|
encoded_protect = [x.encode('raw_unicode_escape') for x in protect]
|
|
if tofile in encoded_protect:
|
|
protected = True
|
|
in_mask = True
|
|
elif os.path.dirname(tofile) in encoded_protect:
|
|
protected = True
|
|
in_mask = 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
|
|
in_mask = 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
|
|
in_mask = False
|
|
elif os.path.dirname(tofile) in newmask:
|
|
protected = False
|
|
in_mask = False
|
|
else:
|
|
tofile_testdir = os.path.dirname(tofile)
|
|
old_tofile_testdir = None
|
|
while tofile_testdir != old_tofile_testdir:
|
|
if tofile_testdir in newmask:
|
|
protected = False
|
|
in_mask = False
|
|
break
|
|
old_tofile_testdir = tofile_testdir
|
|
tofile_testdir = os.path.dirname(tofile_testdir)
|
|
|
|
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.entropyTools.istextfile(tofile)
|
|
in_mask = protected
|
|
else:
|
|
protected = False # it's not a file
|
|
|
|
# request new tofile then
|
|
if protected:
|
|
sys_set_plg_id = \
|
|
etpConst['system_settings_plugins_ids']['client_plugin']
|
|
client_settings = self.Entropy.SystemSettings[sys_set_plg_id]['misc']
|
|
if tofile not in client_settings['configprotectskip']:
|
|
prot_status = True
|
|
if do_allocation_check:
|
|
tofile, prot_status = self.entropyTools.allocate_masked_file(tofile, fromfile)
|
|
if not prot_status:
|
|
protected = False
|
|
else:
|
|
oldtofile = tofile
|
|
if oldtofile.find("._cfg") != -1:
|
|
oldtofile = os.path.join(os.path.dirname(oldtofile),
|
|
os.path.basename(oldtofile)[10:])
|
|
if not do_quiet:
|
|
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:
|
|
if not do_quiet:
|
|
self.Entropy.clientLog.log(
|
|
ETP_LOGPRI_INFO,
|
|
ETP_LOGLEVEL_NORMAL,
|
|
"Skipping config file installation/removal, as stated in client.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.entropyTools.print_traceback()
|
|
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 in_mask, 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']
|
|
down_keys = down_data.keys()
|
|
d_cache = set()
|
|
rc = 0
|
|
key_cache = [os.path.basename(x) for x in down_keys]
|
|
for key in sorted(down_keys):
|
|
key_name = os.path.basename(key)
|
|
if key_name in d_cache: continue
|
|
# first fine wins
|
|
for url in down_data[key]:
|
|
file_name = os.path.basename(url)
|
|
if self.infoDict.get('fetch_path'):
|
|
dest_file = os.path.join(self.infoDict['fetch_path'],
|
|
file_name)
|
|
else:
|
|
dest_file = os.path.join(self.infoDict['unpackdir'],
|
|
file_name)
|
|
rc = self._fetch_source(url, dest_file)
|
|
if rc == 0:
|
|
d_cache.add(key_name)
|
|
break
|
|
key_cache.remove(key_name)
|
|
if rc != 0 and key_name not in key_cache:
|
|
break
|
|
rc = 0
|
|
|
|
return rc
|
|
|
|
def _fetch_source(self, url, dest_file):
|
|
rc = 1
|
|
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.entropyTools.spliturl(url)[1])
|
|
mytxt += " %s %s/%s" % (_("at"),self.entropyTools.bytes_into_human(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(" # ")
|
|
)
|
|
else:
|
|
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 == -3:
|
|
error_message += " - not found."
|
|
elif rc == -100:
|
|
error_message += " - %s." % (_("discarded download"),)
|
|
else:
|
|
error_message += " - %s: %s" % (_("unknown reason"),rc,)
|
|
self.Entropy.updateProgress(
|
|
error_message,
|
|
importance = 1,
|
|
type = "warning",
|
|
header = red(" ## ")
|
|
)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
return rc
|
|
|
|
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 = 0
|
|
if not self.infoDict['verified']:
|
|
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'],
|
|
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
|
|
|
|
def multi_fetch_step(self):
|
|
self.error_on_not_prepared()
|
|
m_fetch_len = len(self.infoDict['multi_fetch_list'])
|
|
mytxt = "%s: %s %s" % (blue(_("Downloading")),darkred(str(m_fetch_len)),_("archives"),)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "info",
|
|
header = red(" ## ")
|
|
)
|
|
# fetch_files_on_mirrors(self, download_list, checksum = False, fetch_abort_function = None)
|
|
rc, err_list = self.Entropy.fetch_files_on_mirrors(
|
|
self.infoDict['multi_fetch_list'],
|
|
self.infoDict['checksum'],
|
|
fetch_abort_function = self.fetch_abort_function
|
|
)
|
|
if rc != 0:
|
|
mytxt = "%s. %s: %s" % (
|
|
red(_("Some packages cannot be fetched. Try to update repositories and retry")),
|
|
blue(_("Error")),
|
|
rc,
|
|
)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "error",
|
|
header = darkred(" ## ")
|
|
)
|
|
for repo,branch,fname,cksum in err_list:
|
|
self.Entropy.updateProgress(
|
|
"[%s:%s|%s] %s" % (blue(repo),brown(branch),
|
|
darkgreen(cksum),darkred(fname),),
|
|
importance = 1,
|
|
type = "error",
|
|
header = darkred(" # ")
|
|
)
|
|
return rc
|
|
|
|
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()
|
|
return self.match_checksum()
|
|
|
|
def multi_checksum_step(self):
|
|
self.error_on_not_prepared()
|
|
return self.multi_match_checksum()
|
|
|
|
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(" ## ")
|
|
)
|
|
self._cleanup_package(self.infoDict['unpackdir'])
|
|
# 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 postinstall_step(self):
|
|
self.error_on_not_prepared()
|
|
pkgdata = self.infoDict['triggers'].get('install')
|
|
if pkgdata:
|
|
trigger = self.Entropy.Triggers('postinstall',pkgdata, self.action)
|
|
do = trigger.prepare()
|
|
if do:
|
|
trigger.run()
|
|
trigger.kill()
|
|
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)
|
|
do = trigger.prepare()
|
|
if self.infoDict.get("diffremoval") and do:
|
|
# diffremoval is true only when the
|
|
# removal is triggered by a package install
|
|
remdata = self.infoDict['triggers'].get('remove')
|
|
if remdata:
|
|
r_trigger = self.Entropy.Triggers('preremove',remdata, self.action)
|
|
r_trigger.prepare()
|
|
r_trigger.triggers = [x for x in trigger.triggers if x not in r_trigger.triggers]
|
|
r_trigger.kill()
|
|
del remdata
|
|
if do:
|
|
trigger.run()
|
|
trigger.kill()
|
|
|
|
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)
|
|
do = trigger.prepare()
|
|
if do:
|
|
trigger.run()
|
|
trigger.kill()
|
|
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)
|
|
do = trigger.prepare()
|
|
if self.infoDict['diffremoval'] and (self.infoDict.get("atom") != None) and do:
|
|
# diffremoval is true only when the remove action is triggered by installPackages()
|
|
pkgdata = self.infoDict['triggers'].get('install')
|
|
if pkgdata:
|
|
i_trigger = self.Entropy.Triggers('postinstall',pkgdata, self.action)
|
|
i_trigger.prepare()
|
|
i_trigger.triggers = [x for x in trigger.triggers if x not in i_trigger.triggers]
|
|
i_trigger.kill()
|
|
del pkgdata
|
|
if do:
|
|
trigger.run()
|
|
trigger.kill()
|
|
|
|
del remdata
|
|
return 0
|
|
|
|
def removeconflict_step(self):
|
|
self.error_on_not_prepared()
|
|
for idpackage in self.infoDict['conflicts']:
|
|
if not self.Entropy.clientDbconn.isIDPackageAvailable(idpackage):
|
|
continue
|
|
pkg = self.Entropy.Package()
|
|
pkg.prepare((idpackage,),"remove_conflict", self.infoDict['remove_metaopts'])
|
|
rc = pkg.run(xterm_header = self.xterm_title)
|
|
pkg.kill()
|
|
if rc != 0:
|
|
return rc
|
|
|
|
return 0
|
|
|
|
def config_step(self):
|
|
self.error_on_not_prepared()
|
|
mytxt = "%s: %s" % (blue(_("Configuring package")),red(self.infoDict['atom']),)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "info",
|
|
header = red(" ## ")
|
|
)
|
|
rc = self.__configure_package()
|
|
if rc == 1:
|
|
mytxt = "%s. %s. %s: %s" % (
|
|
red(_("An error occured while trying to configure the package")),
|
|
red(_("Make sure that your system is healthy")),
|
|
blue(_("Error")),
|
|
rc,
|
|
)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "error",
|
|
header = red(" ## ")
|
|
)
|
|
elif rc == 2:
|
|
mytxt = "%s. %s. %s: %s" % (
|
|
red(_("An error occured while trying to configure the package")),
|
|
red(_("It seems that the Source Package Manager entry is missing")),
|
|
blue(_("Error")),
|
|
rc,
|
|
)
|
|
self.Entropy.updateProgress(
|
|
mytxt,
|
|
importance = 1,
|
|
type = "error",
|
|
header = red(" ## ")
|
|
)
|
|
return rc
|
|
|
|
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():
|
|
self.xterm_title += ' %s: %s' % (_("Fetching"),os.path.basename(self.infoDict['download']),)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.fetch_step()
|
|
|
|
def do_multi_fetch():
|
|
self.xterm_title += ' %s: %s %s' % (_("Multi Fetching"),
|
|
len(self.infoDict['multi_fetch_list']),_("packages"),)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.multi_fetch_step()
|
|
|
|
def do_sources_fetch():
|
|
self.xterm_title += ' %s: %s' % (_("Fetching sources"),os.path.basename(self.infoDict['atom']),)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.sources_fetch_step()
|
|
|
|
def do_checksum():
|
|
self.xterm_title += ' %s: %s' % (_("Verifying"),os.path.basename(self.infoDict['download']),)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.checksum_step()
|
|
|
|
def do_multi_checksum():
|
|
self.xterm_title += ' %s: %s %s' % (_("Multi Verification"),
|
|
len(self.infoDict['multi_checksum_list']),_("packages"),)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.multi_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():
|
|
self.xterm_title += ' %s: %s' % (_("Installing"),self.infoDict['atom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.install_step()
|
|
|
|
def do_remove():
|
|
self.xterm_title += ' %s: %s' % (_("Removing"),self.infoDict['removeatom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.remove_step()
|
|
|
|
def do_logmessages():
|
|
return self.logmessages_step()
|
|
|
|
def do_cleanup():
|
|
self.xterm_title += ' %s: %s' % (_("Cleaning"),self.infoDict['atom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.cleanup_step()
|
|
|
|
def do_postinstall():
|
|
self.xterm_title += ' %s: %s' % (_("Postinstall"),self.infoDict['atom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.postinstall_step()
|
|
|
|
def do_preinstall():
|
|
self.xterm_title += ' %s: %s' % (_("Preinstall"),self.infoDict['atom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.preinstall_step()
|
|
|
|
def do_preremove():
|
|
self.xterm_title += ' %s: %s' % (_("Preremove"),self.infoDict['removeatom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.preremove_step()
|
|
|
|
def do_postremove():
|
|
self.xterm_title += ' %s: %s' % (_("Postremove"),self.infoDict['removeatom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.postremove_step()
|
|
|
|
def do_config():
|
|
self.xterm_title += ' %s: %s' % (_("Configuring"),self.infoDict['atom'],)
|
|
self.Entropy.setTitle(self.xterm_title)
|
|
return self.config_step()
|
|
|
|
steps_data = {
|
|
"fetch": do_fetch,
|
|
"multi_fetch": do_multi_fetch,
|
|
"multi_checksum": do_multi_checksum,
|
|
"sources_fetch": do_sources_fetch,
|
|
"checksum": do_checksum,
|
|
"unpack": do_unpack,
|
|
"remove_conflicts": do_remove_conflicts,
|
|
"install": do_install,
|
|
"remove": do_remove,
|
|
"logmessages": do_logmessages,
|
|
"cleanup": do_cleanup,
|
|
"postinstall": do_postinstall,
|
|
"preinstall": do_preinstall,
|
|
"postremove": do_postremove,
|
|
"preremove": do_preremove,
|
|
"config": do_config,
|
|
}
|
|
|
|
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_check_lock)
|
|
if gave_up:
|
|
return 20
|
|
|
|
locked = self.Entropy.application_lock_check()
|
|
if locked:
|
|
self.Entropy.resources_remove_lock()
|
|
return 21
|
|
|
|
# lock
|
|
self.Entropy.resources_create_lock()
|
|
|
|
try:
|
|
rc = self.run_stepper(xterm_header)
|
|
except:
|
|
self.Entropy.resources_remove_lock()
|
|
raise
|
|
|
|
# remove lock
|
|
self.Entropy.resources_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.atom_match:
|
|
(idpackage,repoid):
|
|
(2000,u'sabayonlinux.org')
|
|
NOTE: in case of remove action, matched_atom must be:
|
|
(idpackage,)
|
|
NOTE: in case of multi_fetch, matched_atom can be a list of matches
|
|
@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 == "multi_fetch":
|
|
self.__generate_multi_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)
|
|
elif self.action == "config":
|
|
self.__generate_config_metadata()
|
|
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['idpackage'] = idpackage
|
|
self.infoDict['configprotect_data'] = []
|
|
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_config_metadata(self):
|
|
self.infoDict.clear()
|
|
idpackage = self.matched_atom[0]
|
|
|
|
self.infoDict['atom'] = self.Entropy.clientDbconn.retrieveAtom(idpackage)
|
|
key, slot = self.Entropy.clientDbconn.retrieveKeySlot(idpackage)
|
|
self.infoDict['key'], self.infoDict['slot'] = key, slot
|
|
self.infoDict['version'] = self.Entropy.clientDbconn.retrieveVersion(idpackage)
|
|
self.infoDict['steps'] = []
|
|
self.infoDict['steps'].append("config")
|
|
|
|
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')
|
|
|
|
install_source = etpConst['install_sources']['unknown']
|
|
meta_inst_source = self.metaopts.get('install_source', install_source)
|
|
if meta_inst_source in etpConst['install_sources'].values():
|
|
install_source = meta_inst_source
|
|
self.infoDict['install_source'] = install_source
|
|
|
|
self.infoDict['configprotect_data'] = []
|
|
dbconn = self.Entropy.open_repository(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['signatures'] = dbconn.retrieveSignatures(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
|
|
|
|
pkgkey = self.entropyTools.dep_getkey(self.infoDict['atom'])
|
|
inst_match = self.Entropy.clientDbconn.atomMatch(pkgkey, matchSlot = self.infoDict['slot'])
|
|
inst_idpackage = -1
|
|
if inst_match[1] == 0: inst_idpackage = inst_match[0]
|
|
self.infoDict['removeidpackage'] = inst_idpackage
|
|
|
|
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'] = self.Entropy.SystemSettings['repositories']['available'][self.infoDict['repository']]['smartpackage']
|
|
self.infoDict['pkgpath'] = self.Entropy.SystemSettings['repositories']['available'][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']
|
|
|
|
# spm xpak data
|
|
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.entropyTools.entropy_compare_versions(
|
|
(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
|
|
self.infoDict['steps'].append("unpack")
|
|
# preinstall placed before preremove in order
|
|
# to respect Spm order
|
|
self.infoDict['steps'].append("preinstall")
|
|
if (self.infoDict['removeidpackage'] != -1):
|
|
self.infoDict['steps'].append("preremove")
|
|
self.infoDict['steps'].append("install")
|
|
if (self.infoDict['removeidpackage'] != -1):
|
|
self.infoDict['steps'].append("postremove")
|
|
self.infoDict['steps'].append("postinstall")
|
|
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']
|
|
self.infoDict['triggers']['install']['imagedir'] = self.infoDict['imagedir']
|
|
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')
|
|
|
|
# fetch_path is the path where data should be downloaded
|
|
# at the moment is implemented only for sources = True
|
|
if self.metaopts.has_key('fetch_path'):
|
|
fetch_path = self.metaopts.get('fetch_path')
|
|
if self.entropyTools.is_valid_path(fetch_path):
|
|
self.infoDict['fetch_path'] = fetch_path
|
|
|
|
self.infoDict['repository'] = repository
|
|
self.infoDict['idpackage'] = idpackage
|
|
dbconn = self.Entropy.open_repository(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['signatures'] = dbconn.retrieveSignatures(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 not self.infoDict.get('fetch_path'):
|
|
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
|
|
|
|
def __generate_multi_fetch_metadata(self):
|
|
self.infoDict.clear()
|
|
|
|
if not isinstance(self.matched_atom,list):
|
|
raise IncorrectParameter("IncorrectParameter: "
|
|
"matched_atom must be a list of tuples, not %s" % (type(self.matched_atom,))
|
|
)
|
|
|
|
dochecksum = True
|
|
|
|
# meta options
|
|
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['checksum'] = dochecksum
|
|
|
|
matches = self.matched_atom
|
|
self.infoDict['matches'] = matches
|
|
self.infoDict['atoms'] = []
|
|
self.infoDict['repository_atoms'] = {}
|
|
temp_fetch_list = []
|
|
temp_checksum_list = []
|
|
temp_already_downloaded_count = 0
|
|
etp_workdir = etpConst['entropyworkdir']
|
|
for idpackage, repository in matches:
|
|
if repository.endswith(etpConst['packagesext']): continue
|
|
|
|
dbconn = self.Entropy.open_repository(repository)
|
|
myatom = dbconn.retrieveAtom(idpackage)
|
|
|
|
# general purpose metadata
|
|
self.infoDict['atoms'].append(myatom)
|
|
if not self.infoDict['repository_atoms'].has_key(repository):
|
|
self.infoDict['repository_atoms'][repository] = set()
|
|
self.infoDict['repository_atoms'][repository].add(myatom)
|
|
|
|
download = dbconn.retrieveDownloadURL(idpackage)
|
|
digest = dbconn.retrieveDigest(idpackage)
|
|
signatures = dbconn.retrieveSignatures(idpackage)
|
|
repo_size = dbconn.retrieveSize(idpackage)
|
|
orig_branch = self.Entropy.get_branch_from_download_relative_uri(download)
|
|
if self.Entropy.check_needed_package_download(download, None) < 0:
|
|
temp_fetch_list.append((repository, orig_branch, download, digest, signatures,))
|
|
continue
|
|
elif dochecksum:
|
|
temp_checksum_list.append((repository, orig_branch, download, digest, signatures,))
|
|
down_path = os.path.join(etp_workdir,download)
|
|
if os.path.isfile(down_path):
|
|
with open(down_path,"r") as f:
|
|
f.seek(0,2)
|
|
disk_size = f.tell()
|
|
if repo_size == disk_size:
|
|
temp_already_downloaded_count += 1
|
|
|
|
self.infoDict['steps'] = []
|
|
self.infoDict['multi_fetch_list'] = temp_fetch_list
|
|
self.infoDict['multi_checksum_list'] = temp_checksum_list
|
|
if self.infoDict['multi_fetch_list']:
|
|
self.infoDict['steps'].append("multi_fetch")
|
|
if self.infoDict['multi_checksum_list']:
|
|
self.infoDict['steps'].append("multi_checksum")
|
|
if temp_already_downloaded_count == len(temp_checksum_list):
|
|
self.infoDict['steps'].reverse()
|
|
|
|
return 0
|