def dnf_shell_rpm_download(self, overlayfs_info): dnf_command = [ '/usr/bin/dnf', 'shell', '-y', '--setopt=protected_packages=', '--disablerepo', '\'*\'', '--releasever', '8', '--allowerasing', '--best', '--nogpgcheck', '--setopt=tsflags=test' ] # get list of UIDs of target repositories that should be used for upgrade error = self._setup_target_repos(overlayfs_info) if error: return error if not self.target_uids: return preparetransaction.ErrorData( summary='Cannot find any required target repository.', details='The list of available required repositories is empty.' ) # enable repositores for upgrade dnf_command += ['--enablerepo', ','.join(self.target_uids)] if os.environ.get('LEAPP_DEBUG', '0') == '1': dnf_command.append('--debugsolver') error = preparetransaction.mount_dnf_cache(overlayfs_info) if error: return error data = next(self.consume(FilteredRpmTransactionTasks), FilteredRpmTransactionTasks()) with open(os.path.join(overlayfs_info.merged, 'var', 'lib', 'leapp', 'dnf-script.txt'), 'w+') as script: cmds = ['distro-sync'] cmds += ['remove ' + pkg for pkg in data.to_remove if pkg] cmds += ['install ' + pkg for pkg in data.to_install if pkg] script.write('\n'.join(cmds)) script.flush() error = preparetransaction.check_container_call(overlayfs_info, [ '--', '/bin/bash', '-c', ' '.join(dnf_command + ['/var/lib/leapp/dnf-script.txt'])]) if os.environ.get('LEAPP_DEBUG', '0') == '1': # We want the debug data available where we would expect it usually. call(['rm', '-rf', os.path.join('/tmp', 'download-debugdata')]) call(['cp', '-a', os.path.join(overlayfs_info.merged, 'debugdata'), '/tmp/download-debugdata']) if error: # FIXME: Do not swallow errors from umount preparetransaction.umount_dnf_cache(overlayfs_info) return error return preparetransaction.umount_dnf_cache(overlayfs_info)
def update_rhel_subscription(self, overlayfs_info): sys_var = "" for msg in self.consume(OSReleaseFacts): if msg.variant_id: sys_var = msg.variant_id break # Make sure Subscription Manager OS Release is unset cmd = ['subscription-manager', 'release', '--unset'] _unused, error = preparetransaction.guard_container_call( overlayfs_info, cmd, guards=(preparetransaction.connection_guard(),) ) if error: error.summary = 'Cannot remove version preference.' return error var_prodcert = {'server': '230.pem'} if sys_var not in var_prodcert: return preparetransaction.ErrorData( summary="Failed to to retrieve Product Cert file.", details="Product cert file not available for System Variant '{}'.".format(sys_var)) prod_cert_path = self.get_file_path(var_prodcert[sys_var]) for path in ('/etc/pki/product-default', '/etc/pki/product'): if not os.path.isdir(path): continue existing_certs = [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] if not existing_certs: continue error = preparetransaction.copy_file_to_container(overlayfs_info, prod_cert_path, path) if error: return error # FIXME: it's removing all certs from /etc/pki/product - but we should remove only the # + EngID provided by a redhat-release-* rpm in the directory (Maybe another RH cert # + but we should not remove user's 3rd party certs (e.g. for custom repositories it # + would be problem later). Needs a little investigation here about the impact, even # + in the container. for cert in existing_certs: # FIXME: fails on insufficient permissions cmd = ['rm', '-f', cert] _unused, error = preparetransaction.guard_container_call(overlayfs_info, cmd) if error: return error cmd = ['subscription-manager', 'refresh'] _unused, error = preparetransaction.guard_container_call( overlayfs_info, cmd, guards=(preparetransaction.connection_guard(),) ) return error
def _setup_target_repos(self, overlayfs_info): """ Setup target repositories. Set the list of UIDs that should be used for the upgrade of the system. In addition, it prepare repo file with custom repositories (in case they don't exist on the system already) and use them for the upgrade as well. The custom repositories should be persistent and enabled or disabled by default based on data in CustomTargetRepository models. Return ErrorData or None. """ self.target_uids = [] try: available_target_uids = set(preparetransaction.get_list_of_available_repo_uids(overlayfs_info)) except CalledProcessError as e: return preparetransaction.ErrorData( summary='Error while trying to get list of available RHEL repositories', details=str(e)) # FIXME: check that required UIDs (baseos, appstream) # + or check that all required RHEL UIDs are available. if not available_target_uids or len(available_target_uids) < 2: return preparetransaction.ErrorData( summary='Cannot find required basic RHEL repositories.', details=('It is required to have RHEL repository on the system' ' provided by the subscription-manager. Possibly you' ' are missing a valid SKU for the target system or network' ' connection failed. Check whether you the system is attached' ' to the valid SKU providing target repositories.')) for target_repo in self.consume(TargetRepositories): for rhel_repo in target_repo.rhel_repos: if rhel_repo.uid in available_target_uids: self.target_uids.append(rhel_repo.uid) for custom_repo in target_repo.custom_repos: # TODO: complete processing of custom repositories # HINT: now it will work only for custom repos that exist # + already on the system in a repo file # TODO: should check available_target_uids + additional custom repos # + outside of rhsm.. # #if custom_repo.uid in available_target_uids: self.target_uids.append(custom_repo.uid) return None
def process(self): skip_rhsm = os.getenv('LEAPP_DEVEL_SKIP_RHSM', '0') == '1' mounts_dir = os.getenv('LEAPP_CONTAINER_ROOT', '/var/lib/leapp/scratch') container_root = os.path.join(mounts_dir, 'mounts') error_flag = False if skip_rhsm: self.log.warning( "LEAPP_DEVEL_SKIP_RHSM has been used. The upgrade is unsupported." ) if not skip_rhsm and not self.is_system_registered_and_attached(): error = preparetransaction.ErrorData( summary='The system is not registered or subscribed.', details=( 'The system has to be registered and subscribed to be able' ' to proceed the upgrade. Register your system with the' ' subscription-manager tool and attach' ' it to proper SKUs to be able to proceed the upgrade.')) preparetransaction.produce_error(error) return # TODO: Find a better place where to run this (perhaps even gate this behind user prompt/question) # FIXME: fails on insufficient permissions cmd = ['/usr/bin/dnf', 'clean', 'all'] _unused, error = preparetransaction.guard_call(cmd) if error: preparetransaction.produce_error( error, summary='Cannot perform dnf cleanup.') return # prepare container # # TODO: wrap in one function (create ofs dirs, mount), or even context # manager (enter -> create dirs, mount; exit -> umount)? xfs_presence = next(self.consume(XFSPresence), XFSPresence()) if xfs_presence.present and xfs_presence.without_ftype: error = preparetransaction.create_disk_image(mounts_dir) if error: self.produce_error(error) ofs_info, error = preparetransaction.create_overlayfs_dirs( container_root) if not ofs_info: preparetransaction.produce_error(error) return error = preparetransaction.mount_overlayfs(ofs_info) if error: preparetransaction.produce_error(error) preparetransaction.remove_overlayfs_dirs(container_root) preparetransaction.remove_disk_image(mounts_dir) return prev_rhsm_release = None if not skip_rhsm: prev_rhsm_release = self.get_rhsm_system_release() if prev_rhsm_release is None: # TODO: error is produced inside - will be refactored later # with the whole actor return # switch EngID to use RHEL 8 subscriptions # error = self.update_rhel_subscription(ofs_info) if not error: dnfplugin_spath = self.get_file_path('rhel_upgrade.py') dnfplugin_dpath = '/lib/python2.7/site-packages/dnf-plugins' error = preparetransaction.copy_file_to_container( ofs_info, dnfplugin_spath, dnfplugin_dpath) if error: return error error = self.dnf_plugin_rpm_download(ofs_info) if not error: error = preparetransaction.copy_file_from_container( ofs_info, '/etc/yum.repos.d/redhat.repo', '/etc/yum.repos.d/', 'redhat.repo.upgrade', ) if error: preparetransaction.produce_error(error) error_flag = True # If Subscription Manager OS Release was set before, make sure we do not change it if not skip_rhsm and 'Release:' in prev_rhsm_release: release = prev_rhsm_release.split(':')[1].strip() cmd = ['subscription-manager', 'release', '--set', release] _unused, error = preparetransaction.guard_call( cmd, guards=(preparetransaction.connection_guard(), )) if error: preparetransaction.produce_error( error, summary='Cannot set minor release version.') error_flag = True # clean # error = preparetransaction.umount_overlayfs(ofs_info) if error: preparetransaction.produce_error(error) error_flag = True preparetransaction.remove_overlayfs_dirs(container_root) preparetransaction.remove_disk_image(mounts_dir) # produce msg for upgrading actor if not error_flag: self.produce_used_target_repos()
def dnf_plugin_rpm_download(self, overlayfs_info): dnf_command = ['/usr/bin/dnf', 'rhel-upgrade', 'download'] # get list of repo IDs of target repositories that should be used for upgrade error = self._setup_target_repos(overlayfs_info) if error: return error if not self.target_repoids: return preparetransaction.ErrorData( summary='Cannot find any required target repository.', details='The list of available required repositories is empty.' ) debugsolver = True if os.environ.get('LEAPP_DEBUG', '0') == '1' else False yum_script_path = self.get_tool_path('handleyumconfig') cmd = ['--', '/bin/bash', '-c', yum_script_path] _unused, error = preparetransaction.guard_container_call( overlayfs_info, cmd) if error: return error error = preparetransaction.mount_dnf_cache(overlayfs_info) if error: return error data = next(self.consume(FilteredRpmTransactionTasks), FilteredRpmTransactionTasks()) plugin_data = { 'pkgs_info': { 'local_rpms': [pkg for pkg in data.local_rpms], 'to_install': [pkg for pkg in data.to_install], 'to_remove': [pkg for pkg in data.to_remove] }, 'dnf_conf': { 'allow_erasing': True, 'best': True, 'debugsolver': debugsolver, 'disable_repos': True, 'enable_repos': self.target_repoids, 'gpgcheck': False, 'platform_id': 'platform:el8', 'releasever': '8', 'test_flag': True, } } with open( os.path.join(overlayfs_info.merged, 'var', 'lib', 'leapp', 'dnf-plugin-data.txt'), 'w+') as data: json.dump(plugin_data, data) data.flush() copy_error = preparetransaction.copy_file_from_container( overlayfs_info, '/var/lib/leapp/dnf-plugin-data.txt', '/var/log/leapp/', 'dnf-plugin-data.txt', ) if copy_error: preparetransaction.produce_warning(copy_error) # FIXME: fails on insufficient permissions cmd = [ '--', '/bin/bash', '-c', ' '.join(dnf_command + ['/var/lib/leapp/dnf-plugin-data.txt']) ] _unused, error = preparetransaction.guard_container_call( overlayfs_info, cmd, guards=(preparetransaction.connection_guard(), preparetransaction.space_guard()), print_output=True) if os.environ.get('LEAPP_DEBUG', '0') == '1': # We want the debug data available where we would expect it usually. debug_data_path = '/var/log/leapp/dnf-debugdata/' # The debugdata is a folder generated by dnf when using the --debugsolver dnf option. We switch on the # debug_solver dnf config parameter in our rhel-upgrade dnf plugin when LEAPP_DEBUG env var set to 1. cmd = [ 'cp', '-a', os.path.join(overlayfs_info.merged, 'debugdata'), debug_data_path ] _unused, dbg_error = preparetransaction.guard_call(cmd) if dbg_error: preparetransaction.produce_warning( dbg_error, summary='Cannot copy new debugdata.') if error: umount_error = preparetransaction.umount_dnf_cache(overlayfs_info) if umount_error: preparetransaction.produce_error(umount_error) return error return preparetransaction.umount_dnf_cache(overlayfs_info)
def process(self): container_root = os.getenv('LEAPP_CONTAINER_ROOT', '/tmp/leapp-overlay') error_flag = False if not self.is_system_registered_and_attached(): error = preparetransaction.ErrorData( summary='The system is not registered or subscribed.', details=('The system has to be registered and subscribed to be able' ' to proceed the upgrade. Register your system with the' ' subscription-manager tool and attach' ' it to proper SKUs to be able to proceed the upgrade.')) self.produce_error(error) return # TODO: Find a better place where to run this (perhaps even gate this behind user prompt/question) preparetransaction.call(['/usr/bin/dnf', 'clean', 'all']) # prepare container # ofs_info, error = preparetransaction.create_overlayfs_dirs(container_root) if not ofs_info: self.produce_error(error) return error = preparetransaction.mount_overlayfs(ofs_info) if error: self.produce_error(error) preparetransaction.remove_overlayfs_dirs(container_root) return # switch EngID to use RHEL 8 subscriptions # prev_rhsm_release = preparetransaction.call(['subscription-manager', 'release'])[0] error = self.update_rhel_subscription(ofs_info) if not error: error = self.dnf_shell_rpm_download(ofs_info) if not error: error = preparetransaction.copy_file_from_container( ofs_info, '/etc/yum.repos.d/redhat.repo', '/etc/yum.repos.d/', 'redhat.repo.upgrade', ) if error: self.produce_error(error) error_flag = True # If Subscription Manager OS Release was set before, make sure we do not change it if 'Release:' in prev_rhsm_release: release = prev_rhsm_release.split(':')[1].strip() error = preparetransaction.check_cmd_call(['subscription-manager', 'release', '--set', release]) if error: self.produce_error(error) error_flag = True # clean # error = preparetransaction.umount_overlayfs(ofs_info) if error: self.produce_error(error) error_flag = True preparetransaction.remove_overlayfs_dirs(container_root) # produce msg for upgrading actor if not error_flag: self.produce_used_target_repos()