[entropy.client.package.actions] install: rewrite lock handling

This is a complete rewrite of the PackageInstallAction class, due to
potentially state data collected on setup() that should be rather
collected with the lock held for the whole install transaction.
This commit is contained in:
Fabio Erculiani
2013-12-04 17:35:43 +01:00
parent 3b069f111d
commit 12e4c4a7a3
6 changed files with 397 additions and 345 deletions

View File

@@ -411,7 +411,10 @@ class _PackageInstallRemoveAction(PackageAction):
return in_mask, protected, tofile, do_continue
def _remove_content_from_system_loop(self, inst_repo, remove_content,
def _remove_content_from_system_loop(self, inst_repo, remove_atom,
remove_content, remove_config,
affected_directories,
affected_infofiles,
directories, directories_cache,
preserved_mgr,
not_removed_due_to_collisions,
@@ -423,7 +426,6 @@ class _PackageInstallRemoveAction(PackageAction):
Body of the _remove_content_from_system() method.
"""
info_dirs = self._get_info_directories()
metadata = self.metadata()
# collect all the library paths to be preserved
# in the final removal loop.
@@ -434,7 +436,7 @@ class _PackageInstallRemoveAction(PackageAction):
# determine without sys_root
paths = self._handle_preserved_lib(
item, metadata['removeatom'], preserved_mgr)
item, remove_atom, preserved_mgr)
if paths is not None:
preserved_lib_paths.update(paths)
@@ -465,7 +467,7 @@ class _PackageInstallRemoveAction(PackageAction):
protected = False
in_mask = False
if not metadata['removeconfig']:
if not remove_config:
protected_item_test = sys_root_item
(in_mask, protected, _x,
@@ -552,7 +554,7 @@ class _PackageInstallRemoveAction(PackageAction):
# so using os.path.isdir valid directory symlink
if sys_root_item not in directories_cache:
# collect for Trigger
metadata['affected_directories'].add(item)
affected_directories.add(item)
directories.add((sys_root_item, "link"))
directories_cache.add(sys_root_item)
continue
@@ -561,7 +563,7 @@ class _PackageInstallRemoveAction(PackageAction):
# plain directory
if sys_root_item not in directories_cache:
# collect for Trigger
metadata['affected_directories'].add(item)
affected_directories.add(item)
directories.add((sys_root_item, "dir"))
directories_cache.add(sys_root_item)
continue
@@ -592,13 +594,13 @@ class _PackageInstallRemoveAction(PackageAction):
# collect for Trigger
dir_name = os.path.dirname(item)
metadata['affected_directories'].add(dir_name)
affected_directories.add(dir_name)
# account for info files, if any
if dir_name in info_dirs:
for _ext in self._INFO_EXTS:
if item.endswith(_ext):
metadata['affected_infofiles'].add(item)
affected_infofiles.add(item)
break
# add its parent directory
@@ -613,15 +615,17 @@ class _PackageInstallRemoveAction(PackageAction):
directories_cache.add(dirobj)
def _remove_content_from_system(self, installed_repository,
automerge_metadata, preserved_mgr):
remove_atom, remove_config, sys_root,
protect_mask, removecontent_file,
automerge_metadata,
affected_directories, affected_infofiles,
preserved_mgr):
"""
Remove installed package content (files/directories) from live system.
@keyword automerge_metadata: Entropy "automerge metadata"
@type automerge_metadata: dict
"""
metadata = self.metadata()
sys_root = self._get_system_root(metadata)
# load CONFIG_PROTECT and CONFIG_PROTECT_MASK
misc_settings = self._entropy.ClientSettings()['misc']
col_protect = misc_settings['collisionprotect']
@@ -632,7 +636,6 @@ class _PackageInstallRemoveAction(PackageAction):
not_removed_due_to_collisions = set()
colliding_path_messages = set()
protect_mask = metadata['config_protect+mask']
if protect_mask is not None:
protect, mask = protect_mask
else:
@@ -643,13 +646,16 @@ class _PackageInstallRemoveAction(PackageAction):
try:
# simulate a removecontent list/set object
remove_content = []
if metadata['removecontent_file'] is not None:
if removecontent_file is not None:
remove_content = Content.FileContentReader(
metadata['removecontent_file'])
removecontent_file)
self._remove_content_from_system_loop(
installed_repository,
remove_content, directories, directories_cache,
installed_repository, remove_atom,
remove_content, remove_config,
affected_directories,
affected_infofiles,
directories, directories_cache,
preserved_mgr,
not_removed_due_to_collisions, colliding_path_messages,
automerge_metadata, col_protect, protect, mask, protectskip,
@@ -690,8 +696,7 @@ class _PackageInstallRemoveAction(PackageAction):
def _filter(_path):
return _path not in not_removed_due_to_collisions
Content.filter_content_file(
metadata['removecontent_file'],
_filter)
removecontent_file, _filter)
# now handle directories
directories = sorted(directories, reverse = True)
@@ -725,7 +730,7 @@ class _PackageInstallRemoveAction(PackageAction):
if not taint:
break
def _spm_remove_package(self, atom):
def _spm_remove_package(self, atom, metadata):
"""
Call Source Package Manager interface and tell it to remove our
just removed package.
@@ -739,4 +744,4 @@ class _PackageInstallRemoveAction(PackageAction):
etpConst['logging']['normal_loglevel_id'],
"Removing from SPM: %s" % (atom,)
)
return spm.remove_installed_package(self.metadata())
return spm.remove_installed_package(atom, metadata)

View File

@@ -55,11 +55,15 @@ class _PackageConfigAction(PackageAction):
# already configured
return
metadata = {}
splitdebug_metadata = self._get_splitdebug_metadata()
metadata.update(splitdebug_metadata)
inst_repo = self._entropy.open_repository(self._repository_id)
with inst_repo.shared():
return self._action_setup_unlocked(inst_repo)
def _action_setup_unlocked(self, inst_repo):
"""
Setup the PackageAction. Assume repository lock already held.
"""
metadata = {}
metadata['atom'] = inst_repo.retrieveAtom(self._package_id)
key, slot = inst_repo.retrieveKeySlot(self._package_id)

View File

@@ -61,6 +61,16 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
self._meta = None
meta.clear()
def _get_remove_package_id_unlocked(self, inst_repo):
"""
Return the installed packages repository package id
that would be removed.
"""
repo = self._entropy.open_repository(self._repository_id)
key_slot = repo.retrieveKeySlotAggregated(self._package_id)
remove_package_id, _inst_rc = inst_repo.atomMatch(key_slot)
return remove_package_id
def setup(self):
"""
Setup the PackageAction.
@@ -69,25 +79,19 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
# already configured
return
inst_repo = self._entropy.installed_repository()
with inst_repo.shared():
return self._action_setup_unlocked(inst_repo)
def _action_setup_unlocked(self, inst_repo):
"""
Setup the PackageAction. Assume repository lock already held.
"""
metadata = {}
splitdebug_metadata = self._get_splitdebug_metadata()
metadata.update(splitdebug_metadata)
repo = self._entropy.open_repository(self._repository_id)
misc_settings = self._entropy.ClientSettings()['misc']
metadata['edelta_support'] = misc_settings['edelta_support']
is_package_repo = self._entropy._is_package_repository(
self._repository_id)
# These are used by Spm.entropy_install_unpack_hook()
metadata['package_id'] = self._package_id
metadata['repository_id'] = self._repository_id
# if splitdebug is enabled, check if it's also enabled
# via package.splitdebug
if metadata['splitdebug']:
@@ -116,14 +120,18 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
metadata['already_protected_config_files'] = {}
metadata['configprotect_data'] = []
metadata['triggers'] = {}
metadata['atom'] = repo.retrieveAtom(self._package_id)
metadata['slot'] = repo.retrieveSlot(self._package_id)
ver, tag, rev = repo.getVersioningData(self._package_id)
metadata['version'] = ver
metadata['versiontag'] = tag
metadata['revision'] = rev
repo = self._entropy.open_repository(self._repository_id)
metadata['atom'] = repo.retrieveAtom(self._package_id)
# use by Spm.entropy_install_unpack_hook(),
# and remove_installed_package()
metadata['category'] = repo.retrieveCategory(self._package_id)
metadata['name'] = repo.retrieveName(self._package_id)
metadata['version'] = repo.retrieveVersion(self._package_id)
metadata['versiontag'] = repo.retrieveTag(self._package_id)
metadata['slot'] = repo.retrieveSlot(self._package_id)
metadata['extra_download'] = []
metadata['splitdebug_pkgfile'] = True
@@ -135,11 +143,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
x['type'] != "debug"]
metadata['extra_download'] += extra_download
metadata['category'] = repo.retrieveCategory(self._package_id)
metadata['download'] = repo.retrieveDownloadURL(self._package_id)
metadata['name'] = repo.retrieveName(self._package_id)
metadata['conflicts'] = self._get_package_conflicts_unlocked(
inst_repo, repo, self._package_id)
description = repo.retrieveDescription(self._package_id)
if description:
@@ -148,11 +152,6 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
description += "..."
metadata['description'] = description
# this is set by __install_package() and required by spm_install
# phase
metadata['installed_package_id'] = None
metadata['remove_package_id'] = -1
metadata['remove_metaopts'] = {
'removeconfig': True,
}
@@ -165,26 +164,11 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
metadata['merge_from'] = const_convert_to_unicode(mf)
metadata['removeconfig'] = self._opts.get('removeconfig', False)
remove_package_id, _inst_rc = inst_repo.atomMatch(
entropy.dep.dep_getkey(metadata['atom']),
matchSlot = metadata['slot'])
metadata['remove_package_id'] = remove_package_id
# setup the list of provided libraries that we're going to remove
if metadata['remove_package_id'] != -1:
repo_libs = repo.retrieveProvidedLibraries(self._package_id)
inst_libs = inst_repo.retrieveProvidedLibraries(
metadata['remove_package_id'])
metadata['removed_libs'] = frozenset(inst_libs - repo_libs)
else:
metadata['removed_libs'] = frozenset()
# collects directories whose content has been modified
# this information is then handed to the Trigger
metadata['affected_directories'] = set()
metadata['affected_infofiles'] = set()
# smartpackage ?
metadata['smartpackage'] = False
# set unpack dir and image dir
if is_package_repo:
@@ -217,93 +201,34 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
except OSError as err:
if err.errno != errno.EEXIST:
raise
metadata['unpackdir'] = const_mkdtemp(dir=unpack_dir)
metadata['imagedir'] = metadata['unpackdir'] + os.path.sep + \
etpConst['entropyimagerelativepath']
metadata['imagedir'] = os.path.join(
metadata['unpackdir'],
etpConst['entropyimagerelativepath'])
metadata['pkgdbpath'] = os.path.join(metadata['unpackdir'],
"edb/pkg.db")
if metadata['remove_package_id'] == -1:
# nothing to remove, fresh install
metadata['removecontent_file'] = None
else:
metadata['removeatom'] = inst_repo.retrieveAtom(
metadata['remove_package_id'])
# generate content file
content = inst_repo.retrieveContentIter(
metadata['remove_package_id'],
order_by="file", reverse=True)
metadata['removecontent_file'] = \
self._generate_content_file(content)
remove_trigger = inst_repo.getTriggerData(
metadata['remove_package_id'])
metadata['triggers']['remove'] = remove_trigger
remove_trigger['affected_directories'] = \
metadata['affected_directories']
remove_trigger['affected_infofiles'] = \
metadata['affected_infofiles']
remove_trigger['spm_repository'] = inst_repo.retrieveSpmRepository(
metadata['remove_package_id'])
remove_trigger.update(splitdebug_metadata)
remove_trigger['accept_license'] = self._get_licenses(
inst_repo, metadata['remove_package_id'])
# setup config_protect and config_protect_mask metadata before it's
# too late.
protect = self._get_config_protect_metadata(
inst_repo, metadata['remove_package_id'],
_metadata = metadata)
metadata.update(protect)
"edb", "pkg.db")
metadata['phases'] = []
if metadata['conflicts']:
metadata['phases'].append(self._remove_conflicts_phase)
metadata['phases'].append(self._remove_conflicts_phase)
if metadata['merge_from']:
metadata['phases'].append(self._merge_phase)
else:
metadata['phases'].append(self._unpack_phase)
# preinstall placed before preremove in order
# to respect Spm order
metadata['phases'].append(self._setup_phase)
metadata['phases'].append(self._setup_package_phase)
metadata['phases'].append(self._tarball_ownership_fixup_phase)
metadata['phases'].append(self._pre_install_phase)
metadata['phases'].append(self._install_phase)
if metadata['remove_package_id'] != -1:
metadata['phases'].append(self._pre_remove_phase)
metadata['phases'].append(self._install_clean_phase)
else:
metadata['phases'].append(self._preserved_libs_gc_phase)
if metadata['remove_package_id'] != -1:
metadata['phases'].append(self._post_remove_phase)
metadata['phases'].append(self._post_remove_install_phase)
metadata['phases'].append(self._install_spm_phase)
metadata['phases'].append(self._post_install_phase)
metadata['phases'].append(self._cleanup_phase)
install_trigger = repo.getTriggerData(self._package_id)
metadata['triggers']['install'] = install_trigger
install_trigger['unpackdir'] = metadata['unpackdir']
install_trigger['imagedir'] = metadata['imagedir']
install_trigger['spm_repository'] = repo.retrieveSpmRepository(
self._package_id)
metadata['accept_license'] = self._get_licenses(
repo, self._package_id)
install_trigger['accept_license'] = metadata['accept_license']
install_trigger.update(splitdebug_metadata)
# SPM can place metadata here if it should be copied to
# the install trigger
metadata['__install_trigger__'] = {}
self._meta = metadata
@@ -365,10 +290,11 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
Execute the package conflicts removal phase.
"""
inst_repo = self._entropy.installed_repository()
with inst_repo.shared():
confl_package_ids = [x for x in self._meta['conflicts'] if \
inst_repo.isPackageIdAvailable(x)]
repo = self._entropy.open_repository(self._repository_id)
confl_package_ids = self._get_package_conflicts_unlocked(
inst_repo, repo, self._package_id)
if not confl_package_ids:
return 0
@@ -715,9 +641,9 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
return spm_class.entropy_install_unpack_hook(self._entropy,
self._meta)
def _setup_phase(self):
def _setup_package_phase(self):
"""
Execute the setup phase.
Execute the package setup phase.
"""
xterm_title = "%s %s: %s" % (
self._xterm_header,
@@ -726,22 +652,55 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
)
self._entropy.set_title(xterm_title)
exit_st = 0
data = self._meta['triggers'].get('install')
action_data = self._meta['triggers'].get('install')
data = self._get_install_trigger_data()
trigger = self._entropy.Triggers(
self.NAME,
"setup",
data,
data)
if data:
trigger = self._entropy.Triggers(
self.NAME, "setup",
data, action_data)
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
exit_st = 0
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
if exit_st != 0:
return exit_st
return 0
def _pre_install_phase(self):
"""
Execute the pre-install phase.
"""
xterm_title = "%s %s: %s" % (
self._xterm_header,
_("Pre-install"),
self._meta['atom'],
)
self._entropy.set_title(xterm_title)
data = self._get_install_trigger_data()
trigger = self._entropy.Triggers(
self.NAME,
"preinstall",
data,
data)
exit_st = 0
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
return exit_st
def _tarball_ownership_fixup_phase(self):
"""
Execute the tarball file ownership fixup phase.
New uid or gids could have created after the setup phase.
"""
# NOTE: fixup permissions in the image directory
# the setup phase could have created additional users and groups
package_paths = [self._meta['pkgpath']]
@@ -760,7 +719,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
if not self._stat_path(package_path):
const_debug_write(
__name__,
"_setup_phase: %s vanished" % (
"_tarball_ownership_fixup_phase: %s vanished" % (
package_path,))
self._entropy.output(
@@ -797,33 +756,59 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
return 0
def _pre_install_phase(self):
def _get_remove_trigger_data_unlocked(self, inst_repo, remove_package_id):
"""
Execute the pre-install phase.
Get the metadata used during removal phases by Triggers.
"""
xterm_title = "%s %s: %s" % (
self._xterm_header,
_("Pre-install"),
self._meta['atom'],
)
self._entropy.set_title(xterm_title)
data = {}
data.update(inst_repo.getTriggerData(remove_package_id))
data = self._meta['triggers'].get('install')
action_data = self._meta['triggers'].get('install')
exit_st = 0
splitdebug_metadata = self._get_splitdebug_metadata()
data.update(splitdebug_metadata)
if data:
trigger = self._entropy.Triggers(
self.NAME, "preinstall",
data, action_data)
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
data['affected_directories'] = self._meta['affected_directories']
data['affected_infofiles'] = self._meta['affected_infofiles']
data['spm_repository'] = inst_repo.retrieveSpmRepository(
remove_package_id)
return exit_st
data['accept_license'] = self._get_licenses(
inst_repo, remove_package_id)
def _pre_remove_phase(self):
return data
def _get_install_trigger_data(self):
"""
Get the metadata used during removal phases by Triggers.
"""
repo = self._entropy.open_repository(self._repository_id)
data = {}
data.update(repo.getTriggerData(self._package_id))
splitdebug_metadata = self._get_splitdebug_metadata()
data.update(splitdebug_metadata)
data['unpackdir'] = self._meta['unpackdir']
data['imagedir'] = self._meta['imagedir']
data['affected_directories'] = self._meta['affected_directories']
data['affected_infofiles'] = self._meta['affected_infofiles']
data['spm_repository'] = repo.retrieveSpmRepository(self._package_id)
data['accept_license'] = self._get_licenses(repo, self._package_id)
# replace current empty "content" metadata info
# content metadata is required by
# _spm_install_package() -> Spm.add_installed_package()
# in case of injected packages (SPM metadata might be
# incomplete).
data['content'] = self._meta.get('content', data['content'])
# SPM hook
data.update(self._meta['__install_trigger__'])
return data
def _pre_remove_package_unlocked(self, data):
"""
Execute the pre-remove phase.
"""
@@ -834,88 +819,61 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
)
self._entropy.set_title(xterm_title)
data = self._meta['triggers'].get('remove')
action_data = self._meta['triggers'].get('install')
exit_st = 0
trigger = self._entropy.Triggers(
self.NAME,
"preremove",
data,
self._get_install_trigger_data())
if data:
trigger = self._entropy.Triggers(
self.NAME, "preremove", data,
action_data)
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
exit_st = 0
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
return exit_st
def _install_clean_phase(self):
def _install_clean_unlocked(self, inst_repo, installed_package_id,
clean_content, removecontent_file,
remove_atom, removed_libs,
config_protect_metadata):
"""
Cleanup package files not used anymore by newly installed version.
This is part of the atomic install, which overwrites the live fs with
new files and removes old afterwards.
"""
inst_repo = self._entropy.installed_repository()
with inst_repo.exclusive():
installed_package_id = self._meta['installed_package_id']
if inst_repo.isPackageIdAvailable(installed_package_id):
return self._install_clean_unlocked(
inst_repo, installed_package_id)
return 0
def _install_clean_unlocked(self, inst_repo, installed_package_id):
"""
_install_clean with no installed repository lock handling.
"""
self._entropy.output(
blue(_("Cleaning previously installed application data.")),
importance = 1,
level = "info",
header = red(" ## ")
)
sys_root = self._get_system_root(self._meta)
preserved_mgr = preservedlibs.PreservedLibraries(
inst_repo, installed_package_id,
self._meta['removed_libs'],
root = self._get_system_root(self._meta))
removed_libs, root = sys_root)
self._remove_content_from_system(
inst_repo,
self._meta['already_protected_config_files'],
preserved_mgr
if clean_content:
self._entropy.output(
blue(_("Cleaning previously installed application data.")),
importance = 1,
level = "info",
header = red(" ## ")
)
self._remove_content_from_system(
inst_repo,
remove_atom,
self._meta['removeconfig'],
sys_root,
config_protect_metadata['config_protect+mask'],
removecontent_file,
self._meta['already_protected_config_files'],
self._meta['affected_directories'],
self._meta['affected_infofiles'],
preserved_mgr)
# garbage collect preserved libraries that are no longer needed
self._garbage_collect_preserved_libs(preserved_mgr)
return 0
def _preserved_libs_gc_phase(self):
"""
Execute the garbage collection of preserved libraries.
"""
inst_repo = self._entropy.installed_repository()
with inst_repo.exclusive():
installed_package_id = self._meta['installed_package_id']
if inst_repo.isPackageIdAvailable(installed_package_id):
# NOTE: removed_libs is always empty because this phase is only
# called when remove_package_id == -1
preserved_mgr = preservedlibs.PreservedLibraries(
inst_repo, installed_package_id,
self._meta['removed_libs'],
root = self._get_system_root(self._meta))
self._garbage_collect_preserved_libs(preserved_mgr)
return 0
def _post_remove_phase(self):
def _post_remove_package_unlocked(self, data):
"""
Execute the post-remove phase.
"""
@@ -926,33 +884,33 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
)
self._entropy.set_title(xterm_title)
data = self._meta['triggers'].get('remove')
action_data = self._meta['triggers'].get('install')
exit_st = 0
trigger = self._entropy.Triggers(
self.NAME,
"postremove",
data,
self._get_install_trigger_data())
if data:
trigger = self._entropy.Triggers(
self.NAME, "postremove", data,
action_data)
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
exit_st = 0
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
return exit_st
def _post_remove_install_phase(self):
def _post_remove_install_package_unlocked(self, atom):
"""
Execute the post-remove SPM package metadata phase.
"""
self._entropy.logger.log(
"[Package]",
etpConst['logging']['normal_loglevel_id'],
"Remove old package (spm data): %s" % (self._meta['removeatom'],)
"Remove old package (spm data): %s" % (atom,)
)
return self._spm_remove_package(self._meta['removeatom'])
def _install_spm_phase(self):
return self._spm_remove_package(atom, self._meta)
def _install_spm_package_unlocked(self, inst_repo, installed_package_id):
"""
Execute the installation of SPM package metadata.
"""
@@ -964,16 +922,9 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
"Installing new SPM entry: %s" % (self._meta['atom'],)
)
# this comes from _add_installed_package()
installed_package_id = self._meta['installed_package_id']
spm_uid = spm.add_installed_package(self._meta)
if spm_uid != -1:
inst_repo = self._entropy.installed_repository()
with inst_repo.exclusive():
if inst_repo.isPackageIdAvailable(installed_package_id):
inst_repo.insertSpmUid(installed_package_id, spm_uid)
inst_repo.insertSpmUid(installed_package_id, spm_uid)
return 0
@@ -988,18 +939,18 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
)
self._entropy.set_title(xterm_title)
data = self._meta['triggers'].get('install')
action_data = self._meta['triggers'].get('install')
exit_st = 0
data = self._get_install_trigger_data()
trigger = self._entropy.Triggers(
self.NAME,
"postinstall",
data,
data)
if data:
trigger = self._entropy.Triggers(
self.NAME, "postinstall",
data, action_data)
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
exit_st = 0
ack = trigger.prepare()
if ack:
exit_st = trigger.run()
trigger.kill()
return exit_st
@@ -1083,18 +1034,29 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
return _path not in second_pass_removal
Content.filter_content_file(content_file, _filter)
def _add_installed_package_unlocked(self, inst_repo, items_installed,
items_not_installed):
def _add_installed_package_unlocked(self, inst_repo,removecontent_file,
items_installed, items_not_installed):
"""
For internal use only.
Copy package from repository to installed packages one.
"""
def _merge_removecontent(inst_repo, repo, _package_id):
# nothing to do if there is no content to remove
if removecontent_file is None:
return
# determine if there is a package to remove first
remove_package_id = self._get_remove_package_id_unlocked(inst_repo)
if remove_package_id == -1:
return
# NOTE: this could be a source of memory consumption
# but generally, the difference between two contents
# is really small
content_diff = list(inst_repo.contentDiff(
self._meta['remove_package_id'],
remove_package_id,
repo,
_package_id,
extended=True))
@@ -1114,7 +1076,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
content_diff.sort(reverse=True)
Content.merge_content_file(
self._meta['removecontent_file'],
removecontent_file,
content_diff, _cmp_func)
smart_pkg = self._meta['smartpackage']
@@ -1144,10 +1106,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
content_safety_file = self._generate_content_safety_file(
content_safety)
if self._meta['remove_package_id'] != -1 and \
self._meta['removecontent_file'] is not None:
_merge_removecontent(
inst_repo, repo, self._package_id)
_merge_removecontent(inst_repo, repo, self._package_id)
else:
@@ -1183,9 +1142,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
content_safety_file = self._generate_content_safety_file(
content_safety)
if self._meta['remove_package_id'] != -1 and \
self._meta['removecontent_file'] is not None:
_merge_removecontent(inst_repo, pkg_repo, pkg_package_id)
_merge_removecontent(inst_repo, pkg_repo, pkg_package_id)
pkg_repo.close()
@@ -1199,10 +1156,9 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
# --
# fix removecontent, need to check if we just installed files
# that resolves at the same directory path (different symlink)
if self._meta['removecontent_file'] is not None:
if removecontent_file is not None:
self._filter_out_files_installed_on_diff_path(
self._meta['removecontent_file'],
items_installed)
removecontent_file, items_installed)
# filter out files not installed from content metadata
# these include splitdebug files, when splitdebug is
@@ -1213,12 +1169,6 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
Content.filter_content_file(
content_file, _filter)
# this is needed to make postinstall trigger work properly
self._meta['triggers']['install']['affected_directories'] = \
self._meta['affected_directories']
self._meta['triggers']['install']['affected_infofiles'] = \
self._meta['affected_infofiles']
# always set data['injected'] to False
# installed packages database SHOULD never have more
# than one package for scope (key+slot)
@@ -1288,19 +1238,10 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
# _spm_install_package() -> Spm.add_installed_package()
# in case of injected packages (SPM metadata might be
# incomplete).
self._meta['triggers']['install']['content'] = \
Content.FileContentReader(content_file)
self._meta['content'] = Content.FileContentReader(content_file)
return package_id
def _install_package(self):
"""
Execute the package installation code.
"""
inst_repo = self._entropy.installed_repository()
with inst_repo.exclusive():
return self._install_package_unlocked(inst_repo)
def _install_package_unlocked(self, inst_repo):
"""
Execute the package installation code.
@@ -1313,20 +1254,37 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
"Installing package: %s" % (self._meta['atom'],)
)
if self._meta['remove_package_id'] != -1:
remove_package_id = self._get_remove_package_id_unlocked(inst_repo)
if remove_package_id != -1:
am_files = inst_repo.retrieveAutomergefiles(
self._meta['remove_package_id'],
remove_package_id,
get_dict = True)
self._meta['already_protected_config_files'] = am_files
self._meta['already_protected_config_files'].clear()
self._meta['already_protected_config_files'].update(am_files)
# items_*installed will be filled by _move_image_to_system
# then passed to _add_installed_package()
items_installed = set()
items_not_installed = set()
exit_st = self._move_image_to_system_unlocked(
inst_repo, items_installed, items_not_installed)
inst_repo, remove_package_id,
items_installed, items_not_installed)
if exit_st != 0:
return exit_st
txt = "%s. %s. %s: %s" % (
red(_("An error occured while trying to install the package")),
red(_("Check if your system is healthy")),
blue(_("Error")),
exit_st,
)
self._entropy.output(
txt,
importance = 1,
level = "error",
header = red(" ## ")
)
return exit_st, None, None
txt = "%s: %s" % (
blue(_("Updating installed packages repository")),
@@ -1338,11 +1296,22 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
level = "info",
header = red(" ## ")
)
package_id = self._add_installed_package_unlocked(
inst_repo, items_installed, items_not_installed)
self._meta['installed_package_id'] = package_id
return 0
# generate the files and directories that would be removed
removecontent_file = None
if remove_package_id != -1:
removecontent_file = self._generate_content_file(
inst_repo.retrieveContentIter(
remove_package_id,
order_by="file",
reverse=True)
)
package_id = self._add_installed_package_unlocked(
inst_repo, removecontent_file,
items_installed, items_not_installed)
return 0, package_id, removecontent_file
def _install_phase(self):
"""
@@ -1391,23 +1360,84 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
header = red(" ## ")
)
exit_st = self._install_package()
if exit_st != 0:
txt = "%s. %s. %s: %s" % (
red(_("An error occured while trying to install the package")),
red(_("Check if your system is healthy")),
blue(_("Error")),
exit_st,
)
self._entropy.output(
txt,
importance = 1,
level = "error",
header = red(" ## ")
)
return exit_st
inst_repo = self._entropy.installed_repository()
with inst_repo.exclusive():
return self._install_phase_unlocked(inst_repo)
def _handle_install_collision_protect_unlocked(self, inst_repo, tofile,
def _install_phase_unlocked(self, inst_repo):
"""
_install_phase(), assuming that the installed packages repository
lock is held in exclusive mode.
"""
remove_package_id = self._get_remove_package_id_unlocked(inst_repo)
remove_atom = None
if remove_package_id != -1:
remove_atom = inst_repo.retrieveAtom(remove_package_id)
# save trigger data
remove_trigger_data = None
if remove_package_id != -1:
remove_trigger_data = self._get_remove_trigger_data_unlocked(
inst_repo, remove_package_id)
if remove_package_id == -1:
removed_libs = frozenset()
else:
repo = self._entropy.open_repository(self._repository_id)
repo_libs = repo.retrieveProvidedLibraries(self._package_id)
inst_libs = inst_repo.retrieveProvidedLibraries(
remove_package_id)
removed_libs = frozenset(inst_libs - repo_libs)
config_protect_metadata = None
if remove_package_id != -1:
config_protect_metadata = self._get_config_protect_metadata(
inst_repo, remove_package_id, _metadata = self._meta)
# after this point, old package metadata is no longer available
(exit_st, installed_package_id,
removecontent_file) = self._install_package_unlocked(inst_repo)
if exit_st != 0:
return exit_st
if remove_trigger_data:
exit_st = self._pre_remove_package_unlocked(remove_trigger_data)
if exit_st != 0:
return exit_st
clean_content = remove_package_id != -1
exit_st = self._install_clean_unlocked(
inst_repo, installed_package_id,
clean_content, removecontent_file,
remove_atom, removed_libs,
config_protect_metadata)
if exit_st != 0:
return exit_st
if remove_trigger_data:
exit_st = self._post_remove_package_unlocked(
remove_trigger_data)
if exit_st != 0:
return exit_st
if remove_package_id != -1:
exit_st = self._post_remove_install_package_unlocked(
remove_atom)
if exit_st != 0:
return exit_st
exit_st = self._install_spm_package_unlocked(
inst_repo, installed_package_id)
if exit_st != 0:
return exit_st
return 0
def _handle_install_collision_protect_unlocked(self, inst_repo,
remove_package_id,
tofile,
todbfile):
"""
Handle files collition protection for the install phase.
@@ -1417,7 +1447,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
const_convert_to_unicode(todbfile),
get_id = True)
if (self._meta['remove_package_id'] not in avail) and avail:
if (remove_package_id not in avail) and avail:
mytxt = darkred(_("Collision found during install for"))
mytxt += " %s - %s" % (
blue(tofile),
@@ -1439,8 +1469,8 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
return True
def _move_image_to_system_unlocked(self, inst_repo, items_installed,
items_not_installed):
def _move_image_to_system_unlocked(self, inst_repo, remove_package_id,
items_installed, items_not_installed):
"""
Internal method that moves the package image directory to the live
filesystem.
@@ -1692,7 +1722,7 @@ class _PackageInstallAction(_PackageInstallRemoveAction):
if col_protect > 1:
todbfile = fromfile[len(image_dir):]
myrc = self._handle_install_collision_protect_unlocked(
inst_repo, tofile, todbfile)
inst_repo, remove_package_id, tofile, todbfile)
if not myrc:
return 0

View File

@@ -182,7 +182,16 @@ class _PackageRemoveAction(_PackageInstallRemoveAction):
root = self._get_system_root(self._meta))
self._remove_content_from_system(
inst_repo, automerge_metadata, preserved_mgr)
inst_repo,
self._meta['atom'],
self._meta['removeconfig'],
self._get_system_root(self._meta),
self._meta['config_protect+mask'],
self._meta['removecontent_file'],
automerge_metadata,
self._meta['affected_directories'],
self._meta['affected_infofiles'],
preserved_mgr)
# garbage collect preserved libraries that are no longer needed
self._garbage_collect_preserved_libs(preserved_mgr)
@@ -273,7 +282,7 @@ class _PackageRemoveAction(_PackageInstallRemoveAction):
spm_atom = spm.convert_from_entropy_package_name(atom)
if not installed_package_ids:
exit_st = self._spm_remove_package(spm_atom)
exit_st = self._spm_remove_package(spm_atom, self._meta)
if exit_st != 0:
return exit_st

View File

@@ -3211,7 +3211,7 @@ class PortagePlugin(SpmPlugin):
# Packages emerged with -B don't contain CONTENTS file
# in their metadata, so we have to create one
self._create_contents_file_if_not_available(pkg_dir,
package_metadata['triggers']['install'])
package_metadata)
try:
counter = self.assign_uid_to_installed_package(
@@ -3296,7 +3296,7 @@ class PortagePlugin(SpmPlugin):
return counter
def remove_installed_package(self, package_metadata):
def remove_installed_package(self, atom, package_metadata):
"""
Reimplemented from SpmPlugin class.
"""
@@ -3304,14 +3304,14 @@ class PortagePlugin(SpmPlugin):
with self._PortageVdbLocker(self, root = root):
return self._remove_installed_package_unlocked(
root, package_metadata)
root, atom, package_metadata)
def _remove_installed_package_unlocked(self, root, package_metadata):
def _remove_installed_package_unlocked(self, root, atom, package_metadata):
"""
remove_installed_package() body assuming that vdb lock has been
already acquired.
"""
atom = entropy.dep.remove_tag(package_metadata['removeatom'])
atom = self.convert_from_entropy_package_name(atom)
remove_build = self.get_installed_package_build_script_path(atom)
remove_path = os.path.dirname(remove_build)
key = entropy.dep.dep_getkey(atom)
@@ -3778,20 +3778,22 @@ class PortagePlugin(SpmPlugin):
"""
Reimplemented from SpmPlugin class.
"""
package_metadata['xpakpath'] = os.path.join(
install_dict = package_metadata['__install_trigger__']
install_dict['xpakpath'] = os.path.join(
package_metadata['unpackdir'],
PortagePlugin._xpak_const['entropyxpakrelativepath'])
if not package_metadata['merge_from']:
package_metadata['xpakstatus'] = None
package_metadata['xpakdir'] = os.path.join(
package_metadata['xpakpath'],
install_dict['xpakstatus'] = None
install_dict['xpakdir'] = os.path.join(
install_dict['xpakpath'],
PortagePlugin._xpak_const['entropyxpakdatarelativepath'])
else:
package_metadata['xpakstatus'] = True
install_dict['xpakstatus'] = True
try:
import portage.const as pc
@@ -3803,10 +3805,11 @@ class PortagePlugin(SpmPlugin):
portdbdir = os.path.join(portdbdir,
PortagePlugin._pkg_compose_atom(package_metadata))
package_metadata['xpakdir'] = portdbdir
install_dict['xpakdir'] = portdbdir
package_metadata['triggers']['install']['xpakdir'] = \
package_metadata['xpakdir']
package_metadata['xpakpath'] = install_dict['xpakpath']
package_metadata['xpakdir'] = install_dict['xpakdir']
package_metadata['xpakstatus'] = install_dict['xpakstatus']
return 0

View File

@@ -920,7 +920,7 @@ class SpmPlugin(Singleton):
"""
raise NotImplementedError()
def remove_installed_package(self, package_metadata):
def remove_installed_package(self, atom, package_metadata):
"""
Remove installed package from SPM database.
"package_metadata" is a dictionary featuring the following (relevant)
@@ -928,9 +928,10 @@ class SpmPlugin(Singleton):
['accept_license', 'imagedir', 'xpakpath', 'slot', 'pkgdbpath',
'versiontag', 'version', 'xpakstatus', 'unpackdir', 'revision',
'category', 'repository', 'xpakdir', 'name', 'install_source',
'removeatom'
]
@param atom: the Entropy package atom
@type atom: string
@param package_metadata: Entropy package metadata
@type package_metadata: dict
@return: execution status