| Server IP : 74.91.139.10 / Your IP : 10.45.147.122 Web Server : Apache/2.4.67 (Unix) System : Linux prod-iad-shwpweb1001f 5.14.0-687.26.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Jul 14 16:32:02 EDT 2026 x86_64 User : FAsyXTyKGY ( 3004) PHP Version : 8.2.30 Disable Function : passthru,shell_exec,system MySQL : ON | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /usr/share/oracle-cloud-agent/plugins/osms/osms/ |
Upload File : |
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.org/licenses/UPL.
import logging
import sys
import time
import datetime
from rpm import RPMPROB_FILTER_OLDPACKAGE
from rpm import labelCompare
import yum
import yum.Errors
from yum.plugins import PluginYumExit
sys.path.append("/usr/share/yum-cli")
import callback
sys.path.insert(0, '/usr/share/oracle-cloud-agent/plugins/osms')
from osms import alxutils
from osms import config
from osms import utils
from osms.i18n import sstr
YUM_PID_FILE = '/var/run/yum.pid'
YUM_LOCK_TIMEOUT = 900
YUM_LOCK_INTERVAL = 10
YUM_BASE = None
class YumAction(yum.YumBase):
def __init__(self):
yum.YumBase.__init__(self)
self.cfg = config.initUp2dateConfig()
self.doConfigSetup(debuglevel=self.cfg["debug"])
self.cache_only = None
_do_yum_lock(self)
self.doTsSetup()
self.doRpmDBSetup()
self.doRepoSetup()
self.doSackSetup()
# Copied from yum/cli.py, more or less
def doTransaction(self, p_obj):
"""takes care of package downloading, checking, user confirmation and actually
RUNNING the transaction"""
#allow downgrades to support rollbacks
self.tsInfo.probFilterFlags.append(RPMPROB_FILTER_OLDPACKAGE)
# Check which packages have to be downloaded
downloadpkgs = []
do_erase = False;
for txmbr in self.tsInfo.getMembers():
if txmbr.ts_state in ['i', 'u']:
po = txmbr.po
if po:
downloadpkgs.append(po)
elif txmbr.ts_state in ['e']:
do_erase = True;
#if we're not deleting, we should use the downloadpkgs
if not do_erase:
p_obj['packages'] = downloadpkgs
logging.debug('Downloading Packages:')
problems = self.downloadPkgs(downloadpkgs)
if len(problems.keys()) > 0:
errstring = ''
errstring += 'Error Downloading Packages:\n'
for key in problems.keys():
errors = yum.misc.unique(problems[key])
for error in errors:
errstring += ' %s: %s\n' % (key, error)
raise yum.Errors.YumBaseError(errstring)
if self.cfg['retrieveOnly']:
# We are configured to only download packages (rather than install/update)
#
# If all we were asked to do was install/update, skip rest of transaction
# and return now.
#
# If all we were asked to do was remove packages, process the transaction.
#
# If we were asked to do a mixture of install/erase - then we can't do a
# complete transaction, and we have to FAIL, rather than execute or show a
# success.
#
if downloadpkgs and do_erase: # FAIL
err = 'Mix of install/erase and "retrieveOnly" set - transaction FAILS'
logging.debug(err)
raise yum.Errors.YumBaseError(err)
elif downloadpkgs and not do_erase: # download and exit
logging.debug('Configured to "retrieveOnly" so skipping package install')
return 0
else: # erase-only, continue
logging.debug('Configured to "retrieveOnly" but only erase-commands - continuing')
if self.cache_only:
logging.debug('Just pre-caching packages, skipping package install')
return 0
# Check GPG signatures
if self.gpgsigcheck(downloadpkgs) != 0:
return 1
logging.debug('Running Transaction Test')
tsConf = {}
for feature in ['diskspacecheck']: # more to come, I'm sure
tsConf[feature] = getattr(self.conf, feature)
# clean out the ts b/c we have to give it new paths to the rpms
del self.ts
self.initActionTs()
# save our dsCallback out
testcb = callback.RPMInstallCallback(output=0)
testcb.tsInfo = self.tsInfo
dscb = self.dsCallback
self.dsCallback = None # dumb, dumb dumb dumb!
self.populateTs(keepold=0) # sigh
tserrors = self.ts.test(testcb, conf=tsConf)
logging.debug('Finished Transaction Test')
if len(tserrors) > 0:
errstring = 'Transaction Check Error: '
for descr in tserrors:
errstring += ' %s\n' % descr
raise yum.Errors.YumBaseError(errstring)
logging.debug('Transaction Test Succeeded')
del self.ts
self.initActionTs() # make a new, blank ts to populate
self.populateTs(keepold=0) # populate the ts
self.ts.check() #required for ordering
self.ts.order() # order
# put back our depcheck callback
self.dsCallback = dscb
logging.debug('Running Transaction')
self.runTransaction(testcb)
#log the transaction results
self.log_successful_transaction(p_obj)
# close things
return 0
# Also taken from yum/cli.py
def gpgsigcheck(self, pkgs):
'''Perform GPG signature verification on the given packages, installing
keys if possible
Returns non-zero if execution should stop (user abort).
Will raise YumBaseError if there's a problem
'''
for po in pkgs:
result, errmsg = self.sigCheckPkg(po)
if result == 0:
# Verified ok, or verify not req'd
continue
elif result == 1:
# bz 433781
# If the package is a Red Hat pkg, try to install the key and see if it helps
if self.isRepoUsingRedHatGPG(po):
logging.debug("GPG check wasn't successful, will attempt to import key")
self.getKeyForPackage(po, askcb=lambda x,y,z: True)
logging.debug("GPG key import was good.")
# if we got here, the key worked, otherwise an exception is thrown
else:
raise yum.Errors.YumBaseError('Refusing to automatically import keys when running unattended.')
else:
# Fatal error
raise yum.Errors.YumBaseError(errmsg)
return 0
def isRepoUsingRedHatGPG(self, po):
goodValues = ["file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"]
repo = self.repos.getRepo(po.repoid)
keyurls = repo.gpgkey
if len(keyurls) < 1:
return False
for keyurl in keyurls:
if keyurl not in goodValues:
logging.debug(
"keyurl = %s, isn't a known Red Hat key, so this " \
"will not be imported. Manually import this key " \
"or set gpgcheck=0 in the OSMS yum plugin configuration file"
% (keyurl))
return False
return True
def getInstalledPkgObject(self, package_tup):
installed = self.rpmdb.returnPackages()
logging.debug("Searching for installed package to remove: %s"
% str(package_tup))
exactmatch, matched, unmatched = yum.packages.parsePackages(
installed, (package_tup), casematch=1)
erases = yum.misc.unique(matched + exactmatch)
if len(erases) >= 1:
logging.debug("Found %d package(s) to remove" % len(erases))
return erases
else:
# TODO: we should just fail, I think
logging.debug("Couldn't find packages to remove")
return ()
def check_obsoletes(self, pkgkeys):
"""
Checks if packages are obsoleted by
existing packages. If package is obsolete,
throw yum error.
"""
nevra_dict = self._nevra_kwarg_parse(pkgkeys)
pkgs = self.pkgSack.searchNevra(name=nevra_dict['name'],epoch=nevra_dict['epoch'], arch=nevra_dict['arch'], ver=nevra_dict['version'], rel=nevra_dict['release'])
for po in pkgs:
obsoleting_pkg = self._test_loop(po, self._pkg2obspkg)
poprovtup = (po.name, 'EQ', (po.epoch, po.ver, po.release))
for pkg in self.rpmdb.searchNevra(name=obsoleting_pkg.name):
if pkg.inPrcoRange('obsoletes', poprovtup):
already_obs = pkg
if already_obs:
error_string = 'Package %s is obsoleted by %s which is already installed' % (po, already_obs)
raise yum.Errors.YumBaseError(error_string)
def log_successful_transaction(self, pkgs):
"""
Log packages and actions to yum log.
"""
action_key = pkgs['action']
actions = {'i': 'Installed', 'u': 'Updated', 'e': 'Erased'}
try:
with open(self._getConfig().logfile, 'a') as logfile:
for pkg in pkgs['packages']:
install_time = datetime.datetime.now()
log_msg = '{0} OSMS Action - {1}: {2} \n'
logfile.write(log_msg.format(install_time.strftime('%b %d %X'), actions[action_key], pkg))
#We don't want to return an error to the user if logging fails for some reason
except IOError as err:
logging.info("Error logging to yum log: {0}".format(err))
def add_transaction_data(self, transaction_data):
""" Add packages to transaction.
transaction_data is in format:
{ 'packages' : [
[['name', '1.0.0', '1', '', ''], 'e'], ...
# name, versio, rel., epoch, arch, flag
]}
where flag can be:
i - install
u - update
e - remove
r - rollback
Note: install and update will check for dependecies and
obsoletes and will install them if needed.
Rollback do not check anything and will assume that state
to which we are rolling back should be correct.
"""
for pkgtup, action in transaction_data['packages']:
pkgkeys = {
'name' : pkgtup[0],
'epoch' : pkgtup[3],
'version' : pkgtup[1],
'release' : pkgtup[2],
}
if len(pkgtup) > 4:
pkgkeys['arch'] = pkgtup[4]
else:
pkgtup.append('')
pkgkeys['arch'] = None
if action == 'u':
self.update(**pkgkeys)
elif action == 'i':
transaction_len = len(self.install(**pkgkeys))
# if an empty transaction is returned,
# this could be due to obsoleted package
if transaction_len == 0:
self.check_obsoletes(pkgkeys)
elif action == 'r':
# we are doing rollback, we want exact version
# no dependecy check
pkgs = self.pkgSack.searchNevra(name=pkgkeys['name'],
epoch=pkgkeys['epoch'], arch=pkgkeys['arch'],
ver=pkgkeys['version'], rel=pkgkeys['release'])
if not pkgs:
raise yum.Errors.YumBaseError(
"Cannot find package %s:%s-%s-%s.%s in any of enabled repositories."
% (pkgkeys['epoch'], pkgkeys['name'], pkgkeys['version'], pkgkeys['release'], pkgkeys['arch'])
)
for po in pkgs:
self.tsInfo.addInstall(po)
elif action == 'e':
package_tup = _yum_package_tup(pkgtup)
packages = self.getInstalledPkgObject(package_tup)
for package in packages:
self.remove(package)
else:
assert False, "Unknown package transaction action."
def get_yum_base():
global YUM_BASE
if YUM_BASE is None:
YUM_BASE = YumAction()
return YUM_BASE
def _yum_package_tup(package_tup):
""" Create a yum-style package tuple from an rhn package tuple.
Allowed styles: n, n.a, n-v-r, n-e:v-r.a, n-v, n-v-r.a,
e:n-v-r.a
Choose from the above styles to be compatible with yum.parsePackage
"""
n, v, r, e, a = package_tup[:]
if not e:
# set epoch to 0 as yum expects
e = '0'
if not a:
pkginfo = '%s-%s-%s' % (n, v, r)
else:
pkginfo = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
return (pkginfo,)
def package_remove(package_list):
transaction_data = __make_transaction(package_list, 'e')
return _runTransaction(transaction_data)
def package_update(package_list, cache_only=None):
yum_base = get_yum_base()
err = None
errmsgs = []
# Remove already installed packages from the list
for package in package_list[:]:
pkgkeys = {
'name' : package[0],
'epoch' : package[3],
'version' : package[1],
'release' : package[2],
}
if len(package) == 5 \
and package[1] == '' \
and package[2] == '' \
and package[3] == '' \
and package[4] == '' \
and yum_base.rpmdb.searchNevra(name=package[0]):
logging.debug('Package %s is already installed' % package[0])
package_list.remove(package)
continue
if len(package) > 4:
pkgkeys['arch'] = package[4]
else:
package.append('')
pkgkeys['arch'] = None
if pkgkeys['epoch'] == '':
pkgkeys['epoch'] = None
pkgs = yum_base.rpmdb.searchNevra(name=pkgkeys['name'], arch=pkgkeys['arch'])
evr = yum.packages.PackageEVR(pkgkeys['epoch'], pkgkeys['version'], pkgkeys['release'])
found = False
for pkg in pkgs:
current = pkg.returnEVR()
currentEVR = (current.epoch, current.version, current.release)
candidateEVR = (evr.epoch, evr.version, evr.release)
compare = labelCompare(currentEVR, candidateEVR)
if compare == 0:
logging.debug('Package %s already installed' % _yum_package_tup(package))
package_list.remove(package)
found = True
break
elif compare > 0:
logging.debug('More recent version of package %s is already installed' % _yum_package_tup(package))
package_list.remove(package)
found = True
break
if not found:
available = yum_base.pkgSack.searchNevra(
name=pkgkeys['name'],
arch=pkgkeys['arch'],
epoch=pkgkeys['epoch'],
ver=pkgkeys['version'],
rel=pkgkeys['release']
)
if not available:
err = 'Package %s is not available for installation' % _yum_package_tup(package)
logging.debug('E: %s', err )
package_list.remove(package)
errmsgs.append(err)
# Don't proceed further with empty list,
# since this would result into an empty yum transaction
if not package_list:
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", {})
return ret
transaction_data = __make_transaction(package_list, 'i')
return _runTransaction(transaction_data, cache_only)
def __make_transaction(package_list, action):
"""
Build transaction Data like _runTransaction would expect.
This is a list of ((n,v,r,e,a), m) where m is either e, i, or u
"""
transaction_data = {}
transaction_data['packages'] = []
#We don't care about this stuff.
transaction_data['flags'] = []
transaction_data['vsflags'] = []
transaction_data['probFilterFlags'] = []
for package in package_list:
transaction_data['packages'].append((package, action))
return transaction_data
class RunTransactionCommand:
def __init__(self, transaction_data):
self.transaction_data = transaction_data
def execute(self, yum_base):
yum_base.add_transaction_data(self.transaction_data)
def _runTransaction(transaction_data, cache_only=None):
""" Run a tranaction on a group of packages. """
command = RunTransactionCommand(transaction_data)
return _run_yum_action(command, 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.
Therefore we change all actions "i" (install) to
"r" (rollback) where we will not check dependencies and obsoletes.
"""
if cache_only:
return (0, "no-ops for caching", {})
for package_object in transaction_data['packages'][:]:
[package, action] = package_object
pkgkeys = {
'name' : package[0],
'version' : package[1],
'release' : package[2],
'epoch' : package[3],
}
if len(package) > 4:
pkgkeys['arch'] = package[4]
else:
pkgkeys['arch'] = None
if pkgkeys['arch'] == '':
pkgkeys['arch'] = None
if pkgkeys['epoch'] == '':
pkgkeys['epoch'] = None
package_exists = get_yum_base().rpmdb.searchNevra(name=pkgkeys['name'],
epoch=pkgkeys['epoch'], arch=pkgkeys['arch'],
ver=pkgkeys['version'], rel=pkgkeys['release'])
# if we're deleting a package that no longer exists, noop
if action == 'e' and not package_exists:
transaction_data['packages'].remove(package_object)
# if we're installing a package that is already installed, noop
elif action == 'i' and package_exists:
transaction_data['packages'].remove(package_object)
# Don't proceed further with empty package list,
# since this would result in an empty yum transaction
if not transaction_data['packages']:
return (0, "Requested package actions have already been performed.", {})
for index, data in enumerate(transaction_data['packages']):
if data[1] == 'i':
transaction_data['packages'][index][1] = 'r'
return _runTransaction(transaction_data)
class FullUpdateCommand:
def execute(self, yum_base):
yum_base.update()
def package_full_update( cache_only=None):
command = FullUpdateCommand()
return _run_yum_action(command, cache_only)
def _do_yum_lock(yum_base):
elapsed_time = 0
while True:
try:
return yum_base.doLock(YUM_PID_FILE)
except Exception as e:
if 'Existing lock /var/run/yum.pid' not in str(e):
raise e
logging.info('Existing lock /var/run/yum.pid: wait for %s seconds, elapsed %s seconds', YUM_LOCK_INTERVAL, elapsed_time)
time.sleep(YUM_LOCK_INTERVAL)
elapsed_time += YUM_LOCK_INTERVAL
if elapsed_time >= YUM_LOCK_TIMEOUT:
raise e
def _run_yum_action(command, cache_only=None):
"""
Do something with yum.
command is an object with an 'execute' method taking yum_base,
so we can apply different operations to yum_base.
"""
# TODO: Note to future programmers:
# When this is running on python 2.5,
# use the unified try/except/finally
time_started = time.time()
try:
try:
yum_base = get_yum_base()
_do_yum_lock(yum_base)
# Accumulate transaction data
oldcount = len(yum_base.tsInfo)
p_list = []
p_obj = {}
# build package list for logs
for package in command.transaction_data['packages']:
p_list.append(_yum_package_tup(package[0])[0])
p_obj['action'] = package[1]
p_obj['packages'] = p_list
command.execute(yum_base)
if not len(yum_base.tsInfo) > oldcount:
raise yum.Errors.YumBaseError('empty transaction')
# depSolving stage
(result, resultmsgs) = yum_base.buildTransaction()
if result == 1:
# Fatal Error
for msg in resultmsgs:
logging.debug('Error: %s' % msg)
raise yum.Errors.DepError(resultmsgs)
elif result == 0 or result == 2:
# Continue on
pass
else:
# Unknown Error
for msg in resultmsgs:
logging.debug('Error: %s' % msg)
raise yum.Errors.YumBaseError(resultmsgs)
logging.debug("Dependencies Resolved")
yum_base.cache_only=cache_only
yum_base.doTransaction(p_obj)
# for sending yum update tid to alx
if utils.is_alx():
time_ended = time.time()
tid = yum_base.history.last().tid
exit_code = 0
finally:
yum_base.closeRpmDB()
yum_base.doUnlock(YUM_PID_FILE)
except (yum.Errors.InstallError, yum.Errors.UpdateError) as e:
message = u"Failed: Packages failed to install properly: %s" % e
data = {}
data['version'] = "1"
data['name'] = "package_install_failure"
return (32, message, data)
except yum.Errors.RemoveError as e:
data = {}
data['version'] = 0
data['name'] = "rpmremoveerrors"
return (15, u"%s" % e, data)
except yum.Errors.DepError as e:
message = u"Failed: packages requested raised dependency problems: %s" % e
data = {}
data["version"] = "1"
data["name"] = "failed_deps"
return (18, message, data)
except (yum.Errors.YumBaseError, PluginYumExit) as e:
message = u"Error while executing packages action: %s" % e
status = 6,
data = {}
return (status, message, data)
# send yum update tid to alx
if utils.is_alx():
alxutils.send_yum_updates(alxutils.SUCCESS, exit_code, tid,
time_started, time_ended)
return (0, "Update Succeeded", {})