Merge pull request #2493 from s0undt3ch/develop

Fix `salt.modules.yumpkg`.
This commit is contained in:
Thomas S Hatch 2012-11-09 08:56:33 -08:00
commit 4dcf0e9308
6 changed files with 132 additions and 57 deletions

View File

@ -18,7 +18,11 @@ import salt.key
from salt.utils import parsers from salt.utils import parsers
from salt.utils.verify import verify_env from salt.utils.verify import verify_env
from salt.exceptions import SaltInvocationError, SaltClientError, EauthAuthenticationError from salt.exceptions import (
SaltInvocationError,
SaltClientError,
EauthAuthenticationError
)
class SaltCMD(parsers.SaltCMDOptionParser): class SaltCMD(parsers.SaltCMDOptionParser):
@ -33,7 +37,9 @@ class SaltCMD(parsers.SaltCMDOptionParser):
self.parse_args() self.parse_args()
try: try:
local = salt.client.LocalClient(self.get_config_file_path('master')) local = salt.client.LocalClient(
self.get_config_file_path('master')
)
except SaltClientError as exc: except SaltClientError as exc:
self.exit(2, '{0}\n'.format(exc)) self.exit(2, '{0}\n'.format(exc))
return return

View File

@ -16,6 +16,7 @@ import re
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def __virtual__(): def __virtual__():
''' '''
Confine this module to yum based systems Confine this module to yum based systems
@ -40,37 +41,37 @@ def __virtual__():
else: else:
return False return False
if has_yumdeps:
class _YumErrorLogger(yum.rpmtrans.NoOutputCallBack): class _YumErrorLogger(yum.rpmtrans.NoOutputCallBack):
'''
A YUM callback handler that logs failed packages with their associated
script output.
'''
def __init__(self):
self.messages = {}
self.failed = []
def log_accumulated_errors(self):
''' '''
Convenience method for logging all messages from failed packages A YUM callback handler that logs failed packages with their associated
script output.
''' '''
for pkg in self.failed: def __init__(self):
log.error('{0} {1}'.format(pkg, self.messages[pkg])) self.messages = {}
self.failed = []
def errorlog(self, msg): def log_accumulated_errors(self):
# Log any error we receive '''
log.error(msg) Convenience method for logging all messages from failed packages
'''
for pkg in self.failed:
log.error('{0} {1}'.format(pkg, self.messages[pkg]))
def filelog(self, package, action): def errorlog(self, msg):
# TODO: extend this for more conclusive transaction handling for # Log any error we receive
# installs and removes VS. the pkg list compare method used now. log.error(msg)
if action == yum.constants.TS_FAILED:
self.failed.append(package)
def scriptout(self, package, msgs): def filelog(self, package, action):
# This handler covers ancillary messages coming from the RPM script. # TODO: extend this for more conclusive transaction handling for
# Will sometimes contain more detailed error messages. # installs and removes VS. the pkg list compare method used now.
self.messages[package] = msgs if action == yum.constants.TS_FAILED:
self.failed.append(package)
def scriptout(self, package, msgs):
# This handler covers ancillary messages coming from the RPM script
# Will sometimes contain more detailed error messages.
self.messages[package] = msgs
def _list_removed(old, new): def _list_removed(old, new):
@ -96,22 +97,23 @@ def _parse_pkg_meta(path):
if result['retcode'] == 0: if result['retcode'] == 0:
for line in result['stdout'].splitlines(): for line in result['stdout'].splitlines():
if not name: if not name:
m = re.match('^Name\s*:\s*(.+)\s*$',line) m = re.match('^Name\s*:\s*(.+)\s*$', line)
if m: if m:
name = m.group(1) name = m.group(1)
continue continue
if not version: if not version:
m = re.match('^Version\s*:\s*(.+)\s*$',line) m = re.match('^Version\s*:\s*(.+)\s*$', line)
if m: if m:
version = m.group(1) version = m.group(1)
continue continue
if not rel: if not rel:
m = re.match('^Release\s*:\s*(.+)\s*$',line) m = re.match('^Release\s*:\s*(.+)\s*$', line)
if m: if m:
version = m.group(1) version = m.group(1)
continue continue
if rel: version += '-{0}'.format(rel) if rel:
return name,version version += '-{0}'.format(rel)
return name, version
def _compare_versions(old, new): def _compare_versions(old, new):
@ -135,6 +137,7 @@ def _compare_versions(old, new):
'new': new[npkg]} 'new': new[npkg]}
return pkgs return pkgs
def list_upgrades(*args): def list_upgrades(*args):
''' '''
Check whether or not an upgrade is available for all packages Check whether or not an upgrade is available for all packages
@ -143,19 +146,24 @@ def list_upgrades(*args):
salt '*' pkg.list_upgrades salt '*' pkg.list_upgrades
''' '''
pkgs=list_pkgs() pkgs = list_pkgs()
yb=yum.YumBase() yb = yum.YumBase()
versions_list={} versions_list = {}
for pkgtype in ['updates']: for pkgtype in ['updates']:
pl=yb.doPackageLists(pkgtype) pl = yb.doPackageLists(pkgtype)
for pkg in pkgs: for pkg in pkgs:
exactmatch, matched, unmatched = yum.packages.parsePackages(pl, [pkg]) exactmatch, matched, unmatched = yum.packages.parsePackages(
pl, [pkg]
)
for pkg in exactmatch: for pkg in exactmatch:
if pkg.arch == getBaseArch() or pkg.arch == 'noarch': if pkg.arch == getBaseArch() or pkg.arch == 'noarch':
versions_list[pkg['name']] = '-'.join([pkg['version'],pkg['release']]) versions_list[pkg['name']] = '-'.join(
[pkg['version'], pkg['release']]
)
return versions_list return versions_list
def available_version(name): def available_version(name):
''' '''
The available version of the package in the repository The available version of the package in the repository
@ -190,6 +198,7 @@ def available_version(name):
# remove the duplicate items from the list and return the first one # remove the duplicate items from the list and return the first one
return list(set(versions_list))[0] return list(set(versions_list))[0]
def upgrade_available(name): def upgrade_available(name):
''' '''
Check whether or not an upgrade is available for a given package Check whether or not an upgrade is available for a given package
@ -200,6 +209,7 @@ def upgrade_available(name):
''' '''
return available_version(name) return available_version(name)
def version(name): def version(name):
''' '''
Returns a version if the package is installed, else returns an empty string Returns a version if the package is installed, else returns an empty string
@ -214,6 +224,7 @@ def version(name):
else: else:
return '' return ''
def list_pkgs(*args): def list_pkgs(*args):
''' '''
List the packages currently installed in a dict:: List the packages currently installed in a dict::
@ -226,15 +237,15 @@ def list_pkgs(*args):
''' '''
ts = rpm.TransactionSet() ts = rpm.TransactionSet()
pkgs = {} pkgs = {}
# In order to support specific package versions, we are going to use the yum # In order to support specific package versions, we are going to use the
# libraries to handle pattern matching # yum libraries to handle pattern matching
yb = yum.YumBase() yb = yum.YumBase()
setattr(yb.conf, 'assumeyes', True) setattr(yb.conf, 'assumeyes', True)
# if no args are passed in get all packages # if no args are passed in get all packages
if len(args) == 0: if len(args) == 0:
for h in ts.dbMatch(): for h in ts.dbMatch():
pkgs[h['name']] = '-'.join([h['version'],h['release']]) pkgs[h['name']] = '-'.join([h['version'], h['release']])
else: else:
# get package version for each package in *args # get package version for each package in *args
for arg in args: for arg in args:
@ -242,13 +253,14 @@ def list_pkgs(*args):
a = yb.pkgSack.returnPackages(patterns=[arg], ignore_case=False) a = yb.pkgSack.returnPackages(patterns=[arg], ignore_case=False)
# make sure there is an a # make sure there is an a
if len(a) > 0: if len(a) > 0:
arg = a[0].name arg = a[0].name
# use the name from yum to do an rpm lookup # use the name from yum to do an rpm lookup
for h in ts.dbMatch('name', arg): for h in ts.dbMatch('name', arg):
pkgs[h['name']] = '-'.join([h['version'],h['release']]) pkgs[h['name']] = '-'.join([h['version'], h['release']])
return pkgs return pkgs
def refresh_db(): def refresh_db():
''' '''
Since yum refreshes the database automatically, this runs a yum clean, Since yum refreshes the database automatically, this runs a yum clean,
@ -262,6 +274,7 @@ def refresh_db():
yb.cleanMetadata() yb.cleanMetadata()
return True return True
def clean_metadata(): def clean_metadata():
''' '''
Cleans local yum metadata. Cleans local yum metadata.
@ -272,6 +285,7 @@ def clean_metadata():
''' '''
return refresh_db() return refresh_db()
def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None, def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None,
**kwargs): **kwargs):
''' '''
@ -319,9 +333,11 @@ def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None,
'({1})'.format(len(srcsplit), len(pkgs))) '({1})'.format(len(srcsplit), len(pkgs)))
return {} return {}
sources = [__salt__['cp.cache_file'](x) sources = [
if __salt__['config.valid_fileproto'](x) else x __salt__['cp.cache_file'](x)
for x in srcsplit] if __salt__['config.valid_fileproto'](x) else x
for x in srcsplit
]
# Check metadata to make sure the name passed matches the source # Check metadata to make sure the name passed matches the source
for i in range(0, len(pkgs)): for i in range(0, len(pkgs)):
@ -332,7 +348,6 @@ def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None,
'({2})'.format(sources[i], pname, pkgs[i])) '({2})'.format(sources[i], pname, pkgs[i]))
return {} return {}
old = list_pkgs(*pkgs) old = list_pkgs(*pkgs)
yb = yum.YumBase() yb = yum.YumBase()
@ -340,13 +355,16 @@ def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None,
setattr(yb.conf, 'gpgcheck', not skip_verify) setattr(yb.conf, 'gpgcheck', not skip_verify)
if repo: if repo:
log.info("Enabling repo '{0}'".format(repo)) log.info('Enabling repo \'{0}\''.format(repo))
yb.repos.enableRepo(repo) yb.repos.enableRepo(repo)
for i in range(0,len(pkgs)):
for i in range(0, len(pkgs)):
try: try:
if sources is not None: if sources is not None:
target = sources[i] target = sources[i]
log.info("Selecting '{0}' for local installation".format(target)) log.info(
'Selecting \'{0}\' for local installation'.format(target)
)
a = yb.installLocal(target) a = yb.installLocal(target)
# if yum didn't install anything, maybe its a downgrade? # if yum didn't install anything, maybe its a downgrade?
log.debug('Added {0} transactions'.format(len(a))) log.debug('Added {0} transactions'.format(len(a)))
@ -355,7 +373,7 @@ def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None,
a = yb.downgradeLocal(target) a = yb.downgradeLocal(target)
else: else:
target = pkgs[i] target = pkgs[i]
log.info("Selecting '{0}' for installation".format(target)) log.info('Selecting \'{0}\' for installation'.format(target))
# Changed to pattern to allow specific package versions # Changed to pattern to allow specific package versions
a = yb.install(pattern=target) a = yb.install(pattern=target)
# if yum didn't install anything, maybe its a downgrade? # if yum didn't install anything, maybe its a downgrade?
@ -382,6 +400,7 @@ def install(pkgs, refresh=False, repo='', skip_verify=False, sources=None,
return _compare_versions(old, new) return _compare_versions(old, new)
def upgrade(): def upgrade():
''' '''
Run a full system upgrade, a yum upgrade Run a full system upgrade, a yum upgrade
@ -416,6 +435,7 @@ def upgrade():
new = list_pkgs() new = list_pkgs()
return _compare_versions(old, new) return _compare_versions(old, new)
def remove(pkgs): def remove(pkgs):
''' '''
Removes packages with yum remove Removes packages with yum remove
@ -448,6 +468,7 @@ def remove(pkgs):
return _list_removed(old, new) return _list_removed(old, new)
def purge(pkgs): def purge(pkgs):
''' '''
Yum does not have a purge, this function calls remove Yum does not have a purge, this function calls remove

View File

@ -64,8 +64,8 @@ class Runner(RunnerClient):
''' '''
ret = super(Runner, self).get_docs() ret = super(Runner, self).get_docs()
for fun, doc in ret.items(): for fun in sorted(ret):
print("{0}:\n{1}\n".format(fun, doc)) print("{0}:\n{1}\n".format(fun, ret[fun]))
def run(self): def run(self):
''' '''

View File

@ -728,9 +728,11 @@ class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, TimeoutMixIn,
if self.options.doc: if self.options.doc:
# Include the target # Include the target
self.args.insert(0, '*') if self.args[0] != '*':
# Include the function self.args.insert(0, '*')
self.args.insert(1, 'sys.doc') if len(self.args) < 2 or self.args[1] != 'sys.doc':
# Include the function
self.args.insert(1, 'sys.doc')
if self.options.list: if self.options.list:
self.config['tgt'] = self.args[0].split(',') self.config['tgt'] = self.args[0].split(',')

View File

@ -11,6 +11,7 @@ import shutil
import subprocess import subprocess
import tempfile import tempfile
import time import time
from datetime import timedelta
try: try:
import pwd import pwd
except ImportError: except ImportError:
@ -326,6 +327,33 @@ class TestDaemon(object):
if os.path.isdir(TMP): if os.path.isdir(TMP):
shutil.rmtree(TMP) shutil.rmtree(TMP)
def wait_for_jid(self, targets, jid, timeout=120):
while timeout > 0:
running = self.__client_job_running(targets, jid)
sys.stdout.write('\r' + ' ' * PNUM + '\r')
if not running:
return True
sys.stdout.write(
' * [Quit in {0}] Waiting for {1}'.format(
timedelta(seconds=timeout), ', '.join(running)
)
)
sys.stdout.flush()
timeout -= 1
time.sleep(1)
else:
sys.stdout.write('\n * ERROR: Failed to get information back\n')
sys.stdout.flush()
return False
def __client_job_running(self, targets, jid):
running = self.client.cmd(
','.join(targets), 'saltutil.running', expr_form='list'
)
return [
k for (k, v) in running.iteritems() if v and v[0]['jid'] == jid
]
def __wait_for_minions_connections(self, evt, targets): def __wait_for_minions_connections(self, evt, targets):
print_header( print_header(
'Waiting at most {0} secs for local minions to connect ' 'Waiting at most {0} secs for local minions to connect '
@ -362,6 +390,10 @@ class TestDaemon(object):
timeout=9999999999999999, timeout=9999999999999999,
) )
if self.wait_for_jid(targets, jid_info['jid']) is False:
evt.set()
return
while syncing: while syncing:
rdata = self.client.get_returns(jid_info['jid'], syncing, 1) rdata = self.client.get_returns(jid_info['jid'], syncing, 1)
if rdata: if rdata:

View File

@ -150,6 +150,20 @@ class MatchTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
data = self.run_salt('-d user.add') data = self.run_salt('-d user.add')
self.assertIn('user.add:', data) self.assertIn('user.add:', data)
def test_salt_documentation_arguments_not_assumed(self):
'''
Test to see if we're not auto-adding '*' and 'sys.doc' to the call
'''
data = self.run_salt('\'*\' -d')
self.assertIn('user.add:', data)
data = self.run_salt('\'*\' -d user.add')
self.assertIn('user.add:', data)
data = self.run_salt('\'*\' sys.doc -d user.add')
self.assertIn('user.add:', data)
data = self.run_salt('\'*\' sys.doc user.add')
self.assertIn('user.add:', data)
if __name__ == "__main__": if __name__ == "__main__":
loader = TestLoader() loader = TestLoader()