Current File : //lib/python3.6/site-packages/rhn/actions/packages.py |
#
# Copyright (c) 2015--2017 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#
import os
import time
import dnf.exceptions
import dnf.cli
from up2date_client import up2dateLog
from up2date_client import config
from up2date_client import rpmUtils
from up2date_client import rhnPackageInfo
log = up2dateLog.initLog()
# file used to keep track of the next time rhn_check
# is allowed to update the package list on the server
LAST_UPDATE_FILE = "/var/lib/up2date/dbtimestamp"
# mark this module as acceptable
__rhnexport__ = [
'update',
'remove',
'refresh_list',
'fullUpdate',
'checkNeedUpdate',
'runTransaction',
'verify'
]
def remove(package_list, cache_only=None):
"""We have been told that we should remove packages"""
if cache_only:
return (0, "no-ops for caching", {})
if not isinstance(package_list, list):
return (13, "Invalid arguments passed to function", {})
log.log_debug("Called remove_packages", package_list)
# initialize dnf
base = _dnf_base(load_system_repo=True, load_available_repos=False)
installed = base.sack.query().installed()
to_remove = [_package_tup2obj(installed, tup) for tup in package_list]
return _dnf_transaction(base, remove=to_remove, allow_erasing=True,
cache_only=cache_only)
def update(package_list, cache_only=None):
"""We have been told that we should retrieve/install packages"""
if not isinstance(package_list, list):
return (13, "Invalid arguments passed to function", {})
log.log_debug("Called update", package_list)
# initialize dnf
base = _dnf_base(load_system_repo=True, load_available_repos=True)
installed = base.sack.query().installed()
available = base.sack.query().available()
# skip already installed packages
err = None
errmsgs = []
to_install = []
for package in package_list:
if len(package) < 5:
package.append('')
(name, version, release, epoch, arch) = package
if version == '' and release == '' \
and epoch == '' and arch == '' \
and installed.filter(name=name):
log.log_debug('Package %s is already installed' % name)
continue
if epoch == '':
epoch = 0
pkgs = installed.filter(name=name, arch=arch).latest()
requested_pkg = _package_tup2obj(available, package)
if not requested_pkg:
err = 'Package %s is not available for installation' \
% _package_tup2str(package)
log.log_me('E: ', err)
errmsgs.append(err)
continue
for pkg in pkgs:
pkg_cmp = pkg.evr_cmp(requested_pkg)
if pkg_cmp == 0:
log.log_debug('Package %s already installed'
% _package_tup2str(package))
break
elif pkg_cmp > 0:
log.log_debug('More recent version of package %s is already installed'
% _package_tup2str(package))
break
else:
to_install.append(requested_pkg)
# Don't proceed further with empty list,
# since this would result into an empty yum transaction
if not to_install:
if err:
ret = (32, "Failed: Packages failed to install properly:\n" + '\n'.join(errmsgs),
{'version': '1', 'name': "package_install_failure"})
else:
ret = (0, "Requested packages already installed", {})
# workaround for RhBug:1218071
base._plugins._unload()
base.close()
return ret
return _dnf_transaction(base, install=to_install, cache_only=cache_only)
def runTransaction(transaction_data, cache_only=None):
""" Run a transaction on a group of packages.
This was historicaly meant as generic call, but
is only called for rollback.
"""
if cache_only:
return (0, "no-ops for caching", {})
# initialize dnf
base = _dnf_base(load_system_repo=True, load_available_repos=True)
installed = base.sack.query().installed()
available = base.sack.query().available()
to_install = []
to_remove = []
for package_object in transaction_data['packages'][:]:
[package, action] = package_object
pkg = _package_tup2obj(installed, package)
if action == 'e' and pkg:
to_remove.append(pkg)
elif action == 'i' and not pkg:
new = _package_tup2obj(available, package)
to_install.append(new)
# Don't proceed further with empty package lists
if not to_install and not to_remove:
return (0, "Requested package actions have already been performed.", {})
return _dnf_transaction(base, install=to_install, remove=to_remove,
allow_erasing=True, cache_only=cache_only)
def fullUpdate(force=0, cache_only=None):
""" Update all packages on the system. """
base = _dnf_base(load_system_repo=True, load_available_repos=True)
return _dnf_transaction(base, full_update=True, cache_only=cache_only)
# The following functions are the same as the old up2date ones.
def checkNeedUpdate(rhnsd=None, cache_only=None):
""" Check if the locally installed package list changed, if
needed the list is updated on the server
In case of error avoid pushing data to stay safe
"""
if cache_only:
return (0, "no-ops for caching", {})
data = {}
dbpath = "/var/lib/rpm"
cfg = config.initUp2dateConfig()
if cfg['dbpath']:
dbpath = cfg['dbpath']
RPM_PACKAGE_FILE = "%s/Packages" % dbpath
try:
dbtime = os.stat(RPM_PACKAGE_FILE)[8] # 8 is st_mtime
except:
return (0, "unable to stat the rpm database", data)
try:
last = os.stat(LAST_UPDATE_FILE)[8]
except:
last = 0
# Never update the package list more than once every 1/2 hour
if last >= (dbtime - 10):
return (0, "rpm database not modified since last update (or package "
"list recently updated)", data)
if last == 0:
try:
file = open(LAST_UPDATE_FILE, "w+")
file.close()
except:
return (0, "unable to open the timestamp file", data)
# call the refresh_list action with a argument so we know it's
# from rhnsd
return refresh_list(rhnsd=1)
def refresh_list(rhnsd=None, cache_only=None):
""" push again the list of rpm packages to the server """
if cache_only:
return (0, "no-ops for caching", {})
log.log_debug("Called refresh_rpmlist")
ret = None
try:
rhnPackageInfo.updatePackageProfile()
except:
print("ERROR: refreshing remote package list for System Profile")
return (20, "Error refreshing package list", {})
touch_time_stamp()
return (0, "rpmlist refreshed", {})
def touch_time_stamp():
try:
file_d = open(LAST_UPDATE_FILE, "w+")
file_d.close()
except:
return (0, "unable to open the timestamp file", {})
# Never update the package list more than once every hour.
t = time.time()
try:
os.utime(LAST_UPDATE_FILE, (t, t))
except:
return (0, "unable to set the time stamp on the time stamp file %s"
% LAST_UPDATE_FILE, {})
def verify(packages, cache_only=None):
log.log_debug("Called packages.verify")
if cache_only:
return (0, "no-ops for caching", {})
data = {}
data['name'] = "packages.verify"
data['version'] = 0
ret, missing_packages = rpmUtils.verifyPackages(packages)
data['verify_info'] = ret
if len(missing_packages):
data['name'] = "packages.verify.missing_packages"
data['version'] = 0
data['missing_packages'] = missing_packages
return(43, "packages requested to be verified are missing", data)
return (0, "packages verified", data)
def _dnf_base(load_system_repo=True, load_available_repos=True):
# initialize dnf
base = dnf.Base()
if not base._plugins.plugins:
base.init_plugins()
if load_available_repos:
base.read_all_repos()
base.fill_sack(load_system_repo=True, load_available_repos=True)
return base
def _dnf_transaction(base, install=[], remove=[], full_update=False,
allow_erasing=False, cache_only=None):
"""
command is an function excpecting dnf.Base() as an argument
"""
try:
if full_update:
base.upgrade_all()
else:
for pkg in install:
if pkg:
base.package_install(pkg)
for pkg in remove:
if pkg:
base.package_remove(pkg)
base.resolve(allow_erasing)
log.log_debug("Dependencies Resolved")
if not len(base.transaction):
raise dnf.exceptions.Error('empty transaction')
if base.transaction.install_set:
log.log_debug("Downloading and installing: ",
[str(p) for p in base.transaction.install_set])
base.download_packages(base.transaction.install_set)
if base.transaction.remove_set:
log.log_debug("Removing: ",
[str(p) for p in base.transaction.remove_set])
if not cache_only:
base.do_transaction()
except dnf.exceptions.MarkingError as e:
data = {}
data['version'] = "1"
data['name'] = "package_install_failure"
return (32, "Failed: Packages failed to install "
"properly: %s" % str(e), data)
except dnf.exceptions.MarkingError as e:
data = {}
data['version'] = 0
data['name'] = "rpmremoveerrors"
return (15, "%s" % str(e), data)
except dnf.exceptions.DepsolveError as e:
data = {}
data["version"] = "1"
data["name"] = "failed_deps"
return (18, "Failed: packages requested raised "
"dependency problems: %s" % str(e), data)
except dnf.exceptions.Error as e:
status = 6,
message = "Error while executing packages action: %s" % str(e)
data = {}
return (status, message, data)
finally:
# workaround for RhBug:1218071
base._plugins._unload()
base.close()
return (0, "Update Succeeded", {})
def _package_tup2obj(q, tup):
(name, version, release, epoch) = tup[:4]
arch = tup[4] if len(tup) > 4 else None
query = {'name': name}
if version is not None and len(version) > 0:
query['version'] = version
if release is not None and len(release) > 0:
query['release'] = release
if epoch is not None and len(epoch) > 0:
query['epoch'] = int(epoch)
if arch is not None and len(arch) > 0:
query['arch'] = arch
pkgs = q.filter(**query).run()
if pkgs:
return pkgs[0]
return None
def _package_tup2str(package_tup):
""" Create a package name from an rhn package tuple.
"""
n, v, r, e, a = package_tup[:]
if not e:
e = '0'
pkginfo = '%s-%s:%s-%s' % (n, e, v, r)
if a:
pkginfo += '.%s' % (a)
return (pkginfo,)