Merge remote-tracking branch 'upstream/develop' into unify-api

This commit is contained in:
Samuel M Smith 2013-08-08 16:33:20 -06:00
commit a841b5f452
33 changed files with 508 additions and 556 deletions

View File

@ -167,16 +167,20 @@
# - cmd # - cmd
# The external auth system uses the Salt auth modules to authenticate and # The external auth system uses the Salt auth modules to authenticate and
# validate users to access areas of the Salt system # validate users to access areas of the Salt system.
# #
# external_auth: # external_auth:
# pam: # pam:
# fred: # fred:
# - test.* # - test.*
# #
# Time (in seconds) for a newly generated token to live. Default: 12 hours # Time (in seconds) for a newly generated token to live. Default: 12 hours
# token_expire: 43200 # token_expire: 43200
# Allow minions to push files to the master. This is disabled by default, for
# security purposes.
# file_recv: False
##### Master Module Management ##### ##### Master Module Management #####
########################################## ##########################################

View File

@ -11941,7 +11941,7 @@ salt \(aq*\(aq cp.is_cached salt://path/to/file
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B salt.modules.cp.list_master(env=\(aqbase\(aq) .B salt.modules.cp.list_master(env=\(aqbase\(aq, prefix=\(aq\(aq)
List all of the files stored on the master List all of the files stored on the master
.sp .sp
CLI Example: CLI Example:
@ -11954,7 +11954,7 @@ salt \(aq*\(aq cp.list_master
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
.B salt.modules.cp.list_master_dirs(env=\(aqbase\(aq) .B salt.modules.cp.list_master_dirs(env=\(aqbase\(aq, prefix=\(aq\(aq)
List all of the directories stored on the master List all of the directories stored on the master
.sp .sp
CLI Example: CLI Example:

View File

@ -305,7 +305,7 @@ insecure!
``client_acl`` ``client_acl``
-------------- --------------
Default: {} Default: ``{}``
Enable user accounts on the master to execute specific modules. These modules Enable user accounts on the master to execute specific modules. These modules
can be expressed as regular expressions can be expressed as regular expressions
@ -317,6 +317,74 @@ can be expressed as regular expressions
- test.ping - test.ping
- pkg.* - pkg.*
.. conf_master:: client_acl_blacklist
``client_acl_blacklist``
------------------------
Default: ``{}``
Blacklist users or modules
This example would blacklist all non sudo users, including root from
running any commands. It would also blacklist any use of the "cmd"
module.
This is completely disabled by default.
.. code-block:: yaml
client_acl_blacklist:
users:
- root
- '^(?!sudo_).*$' # all non sudo users
modules:
- cmd
.. conf_master:: external_auth
``external_auth``
-----------------
Default: ``{}``
The external auth system uses the Salt auth modules to authenticate and
validate users to access areas of the Salt system.
.. code-block:: yaml
external_auth:
pam:
fred:
- test.*
.. conf_master:: token_expire
``token_expire``
----------------
Default: ``43200``
Time (in seconds) for a newly generated token to live. Default: 12 hours
.. code-block:: yaml
token_expire: 43200
.. conf_master:: file_recv
``file_recv``
-------------
Default: ``False``
Allow minions to push files to the master. This is disabled by default, for
security purposes.
.. code-block:: yaml
file_recv: False
Master Module Management Master Module Management
------------------------ ------------------------

View File

@ -110,3 +110,9 @@ To execute the manage.up runner:
.. code-block:: bash .. code-block:: bash
# salt-call publish.runner manage.up # salt-call publish.runner manage.up
To match minions using other matchers, use ``expr_form``:
.. code-block:: bash
# salt-call publish.publish 'webserv* and not G@os:Ubuntu' test.ping expr_form='compound'

View File

@ -204,3 +204,16 @@ class Resolver(object):
except (IOError, OSError): except (IOError, OSError):
pass pass
return tdata return tdata
def request_token(self, load):
'''
Request a token fromt he master
'''
load['cmd'] = 'mk_token'
sreq = salt.payload.SREQ(
'tcp://{0[interface]}:{0[ret_port]}'.format(self.opts),
)
tdata = sreq.send('clear', load)
if 'token' not in tdata:
return tdata
return tdata

View File

@ -129,6 +129,7 @@ VALID_OPTS = {
'client_acl_blacklist': dict, 'client_acl_blacklist': dict,
'external_auth': dict, 'external_auth': dict,
'token_expire': int, 'token_expire': int,
'file_recv': bool,
'file_ignore_regex': bool, 'file_ignore_regex': bool,
'file_ignore_glob': bool, 'file_ignore_glob': bool,
'fileserver_backend': list, 'fileserver_backend': list,
@ -271,6 +272,7 @@ DEFAULT_MASTER_OPTS = {
'client_acl_blacklist': {}, 'client_acl_blacklist': {},
'external_auth': {}, 'external_auth': {},
'token_expire': 43200, 'token_expire': 43200,
'file_recv': False,
'file_buffer_size': 1048576, 'file_buffer_size': 1048576,
'file_ignore_regex': None, 'file_ignore_regex': None,
'file_ignore_glob': None, 'file_ignore_glob': None,

View File

@ -102,7 +102,7 @@ class Client(object):
''' '''
raise NotImplementedError raise NotImplementedError
def file_list_emptydirs(self, env='base'): def file_list_emptydirs(self, env='base', prefix=''):
''' '''
List the empty dirs List the empty dirs
''' '''
@ -207,13 +207,13 @@ class Client(object):
ldest = self._file_local_list(localfilesdest) ldest = self._file_local_list(localfilesdest)
return sorted(fdest.union(ldest)) return sorted(fdest.union(ldest))
def file_list(self, env='base'): def file_list(self, env='base', prefix=''):
''' '''
This function must be overwritten This function must be overwritten
''' '''
return [] return []
def dir_list(self, env='base'): def dir_list(self, env='base', prefix=''):
''' '''
This function must be overwritten This function must be overwritten
''' '''
@ -461,47 +461,56 @@ class LocalClient(Client):
return '' return ''
return fnd['path'] return fnd['path']
def file_list(self, env='base'): def file_list(self, env='base', prefix=''):
''' '''
Return a list of files in the given environment Return a list of files in the given environment
with optional relative prefix path to limit directory traversal
''' '''
ret = [] ret = []
if env not in self.opts['file_roots']: if env not in self.opts['file_roots']:
return ret return ret
for path in self.opts['file_roots'][env]: for path in self.opts['file_roots'][env]:
if prefix:
path = os.path.join(path, prefix)
for root, dirs, files in os.walk(path, followlinks=True): for root, dirs, files in os.walk(path, followlinks=True):
for fname in files: for fname in files:
ret.append( ret.append(
os.path.relpath( os.path.relpath(
os.path.join(root, fname), os.path.join(root, prefix, fname),
path path
) )
) )
return ret return ret
def file_list_emptydirs(self, env='base'): def file_list_emptydirs(self, env='base', prefix=''):
''' '''
List the empty dirs in the file_roots List the empty dirs in the file_roots
with optional relative prefix path to limit directory traversal
''' '''
ret = [] ret = []
if env not in self.opts['file_roots']: if env not in self.opts['file_roots']:
return ret return ret
for path in self.opts['file_roots'][env]: for path in self.opts['file_roots'][env]:
if prefix:
path = os.path.join(path, prefix)
for root, dirs, files in os.walk(path, followlinks=True): for root, dirs, files in os.walk(path, followlinks=True):
if len(dirs) == 0 and len(files) == 0: if len(dirs) == 0 and len(files) == 0:
ret.append(os.path.relpath(root, path)) ret.append(os.path.relpath(root, os.path.join(prefix, path)))
return ret return ret
def dir_list(self, env='base'): def dir_list(self, env='base', prefix=''):
''' '''
List the dirs in the file_roots List the dirs in the file_roots
with optional relative prefix path to limit directory traversal
''' '''
ret = [] ret = []
if env not in self.opts['file_roots']: if env not in self.opts['file_roots']:
return ret return ret
for path in self.opts['file_roots'][env]: for path in self.opts['file_roots'][env]:
if prefix:
path = os.path.join(path, prefix)
for root, dirs, files in os.walk(path, followlinks=True): for root, dirs, files in os.walk(path, followlinks=True):
ret.append(os.path.relpath(root, path)) ret.append(os.path.relpath(root, os.path.join(prefix, path)))
return ret return ret
def hash_file(self, path, env='base'): def hash_file(self, path, env='base'):
@ -667,11 +676,12 @@ class RemoteClient(Client):
fn_.close() fn_.close()
return dest return dest
def file_list(self, env='base'): def file_list(self, env='base', prefix=''):
''' '''
List the files on the master List the files on the master
''' '''
load = {'env': env, load = {'env': env,
'prefix': prefix,
'cmd': '_file_list'} 'cmd': '_file_list'}
try: try:
return self.auth.crypticle.loads( return self.auth.crypticle.loads(
@ -683,11 +693,12 @@ class RemoteClient(Client):
except SaltReqTimeoutError: except SaltReqTimeoutError:
return '' return ''
def file_list_emptydirs(self, env='base'): def file_list_emptydirs(self, env='base', prefix=''):
''' '''
List the empty dirs on the master List the empty dirs on the master
''' '''
load = {'env': env, load = {'env': env,
'prefix': prefix,
'cmd': '_file_list_emptydirs'} 'cmd': '_file_list_emptydirs'}
try: try:
return self.auth.crypticle.loads( return self.auth.crypticle.loads(
@ -699,11 +710,12 @@ class RemoteClient(Client):
except SaltReqTimeoutError: except SaltReqTimeoutError:
return '' return ''
def dir_list(self, env='base'): def dir_list(self, env='base', prefix=''):
''' '''
List the dirs on the master List the dirs on the master
''' '''
load = {'env': env, load = {'env': env,
'prefix': prefix,
'cmd': '_dir_list'} 'cmd': '_dir_list'}
try: try:
return self.auth.crypticle.loads( return self.auth.crypticle.loads(

View File

@ -99,10 +99,11 @@ def file_list(load):
return ret return ret
for path in __opts__['file_roots'][load['env']]: for path in __opts__['file_roots'][load['env']]:
path = os.path.join(path, load['prefix'])
for root, dirs, files in os.walk(path, followlinks=True): for root, dirs, files in os.walk(path, followlinks=True):
for fname in files: for fname in files:
rel_fn = os.path.relpath( rel_fn = os.path.relpath(
os.path.join(root, fname), os.path.join(root, load['prefix'], fname),
path path
) )
if not salt.fileserver.is_file_ignored(__opts__, rel_fn): if not salt.fileserver.is_file_ignored(__opts__, rel_fn):
@ -118,11 +119,12 @@ def file_list_emptydirs(load):
if load['env'] not in __opts__['file_roots']: if load['env'] not in __opts__['file_roots']:
return ret return ret
for path in __opts__['file_roots'][load['env']]: for path in __opts__['file_roots'][load['env']]:
path = os.path.join(path, load['prefix'])
for root, dirs, files in os.walk(path, followlinks=True): for root, dirs, files in os.walk(path, followlinks=True):
if len(dirs) == 0 and len(files) == 0: if len(dirs) == 0 and len(files) == 0:
rel_fn = os.path.relpath(root, path) rel_fn = os.path.relpath(root, path)
if not salt.fileserver.is_file_ignored(__opts__, rel_fn): if not salt.fileserver.is_file_ignored(__opts__, rel_fn):
ret.append(rel_fn) ret.append(os.path.join(load['prefix'], rel_fn))
return ret return ret
@ -134,6 +136,7 @@ def dir_list(load):
if load['env'] not in __opts__['file_roots']: if load['env'] not in __opts__['file_roots']:
return ret return ret
for path in __opts__['file_roots'][load['env']]: for path in __opts__['file_roots'][load['env']]:
path = os.path.join(path, load['prefix'])
for root, dirs, files in os.walk(path, followlinks=True): for root, dirs, files in os.walk(path, followlinks=True):
ret.append(os.path.relpath(root, path)) ret.append(os.path.relpath(root, os.path.join(load['prefix'], path)))
return ret return ret

View File

@ -743,10 +743,10 @@ class AESFuncs(object):
# The minion is not who it says it is! # The minion is not who it says it is!
# We don't want to listen to it! # We don't want to listen to it!
log.warn( log.warn(
('Minion id {0} is not who it says it is and is attempting ' (
'to issue a peer command').format( 'Minion id {0} is not who it says it is and is attempting '
clear_load['id'] 'to issue a peer command'
) ).format(clear_load['id'])
) )
return False return False
perms = [] perms = []
@ -1324,10 +1324,10 @@ class AESFuncs(object):
# The minion is not who it says it is! # The minion is not who it says it is!
# We don't want to listen to it! # We don't want to listen to it!
log.warn( log.warn(
('Minion id {0} is not who it says it is and is attempting ' (
'to revoke the key for {0}').format( 'Minion id {0} is not who it says it is and is attempting '
load['id'] 'to revoke the key for {0}'
) ).format(load['id'])
) )
return False return False
keyapi = salt.key.Key(self.opts) keyapi = salt.key.Key(self.opts)
@ -1753,6 +1753,94 @@ class ClearFuncs(object):
self.event.fire_event(eload, 'auth') self.event.fire_event(eload, 'auth')
return ret return ret
def runner(self, clear_load):
'''
Send a master control function back to the wheel system
'''
# All wheel ops pass through eauth
if 'token' in clear_load:
try:
token = self.loadauth.get_tok(clear_load['token'])
except Exception as exc:
log.error(
'Exception occurred when generating auth token: {0}'.format(
exc
)
)
return ''
if not token:
log.warning('Authentication failure of type "token" occurred.')
return ''
if token['eauth'] not in self.opts['external_auth']:
log.warning('Authentication failure of type "token" occurred.')
return ''
if token['name'] not in self.opts['external_auth'][token['eauth']]:
log.warning('Authentication failure of type "token" occurred.')
return ''
good = self.ckminions.runner_check(
self.opts['external_auth'][clear_load['eauth']][token['name']] if token['name'] in self.opts['external_auth'][clear_load['eauth']] else self.opts['external_auth'][token['eauth']]['*'],
clear_load['fun'])
if not good:
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
try:
fun = clear_load.pop('fun')
return self.wheel_.call_func(fun, **clear_load)
except Exception as exc:
log.error('Exception occurred while '
'introspecting {0}: {1}'.format(fun, exc))
return ''
if 'eauth' not in clear_load:
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
if clear_load['eauth'] not in self.opts['external_auth']:
# The eauth system is not enabled, fail
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
try:
name = self.loadauth.load_name(clear_load)
if not ((name in self.opts['external_auth'][clear_load['eauth']]) | ('*' in self.opts['external_auth'][clear_load['eauth']])):
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
if not self.loadauth.time_auth(clear_load):
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
good = self.ckminions.runner_check(
self.opts['external_auth'][clear_load['eauth']][name] if name in self.opts['external_auth'][clear_load['eauth']] else self.opts['external_auth'][token['eauth']]['*'],
clear_load['fun'])
if not good:
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
try:
fun = clear_load.pop('fun')
return self.wheel_.call_func(fun, **clear_load)
except Exception as exc:
log.error('Exception occurred while '
'introspecting {0}: {1}'.format(fun, exc))
return ''
except Exception as exc:
log.error(
'Exception occurred in the wheel system: {0}'.format(exc)
)
return ''
def wheel(self, clear_load): def wheel(self, clear_load):
''' '''
Send a master control function back to the wheel system Send a master control function back to the wheel system
@ -1777,6 +1865,14 @@ class ClearFuncs(object):
if token['name'] not in self.opts['external_auth'][token['eauth']]: if token['name'] not in self.opts['external_auth'][token['eauth']]:
log.warning('Authentication failure of type "token" occurred.') log.warning('Authentication failure of type "token" occurred.')
return '' return ''
good = self.ckminions.wheel_check(
self.opts['external_auth'][clear_load['eauth']][token['name']] if token['name'] in self.opts['external_auth'][clear_load['eauth']] else self.opts['external_auth'][token['eauth']]['*'],
clear_load['fun'])
if not good:
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
log.warning(msg)
return ''
try: try:
fun = clear_load.pop('fun') fun = clear_load.pop('fun')
@ -2060,7 +2156,7 @@ class ClearFuncs(object):
self.opts['hash_type'] self.opts['hash_type']
) )
# Announce the job on the event bus # Announce the job on the event bus
self.event.fire_event(clear_load, 'new_job') self.event.fire_event(clear_load, 'new_job')
# Verify the jid dir # Verify the jid dir

View File

@ -150,8 +150,13 @@ def latest_version(*names, **kwargs):
# If there are no installed versions that are greater than or equal # If there are no installed versions that are greater than or equal
# to the install candidate, then the candidate is an upgrade, so # to the install candidate, then the candidate is an upgrade, so
# add it to the return dict # add it to the return dict
if not any([compare(pkg1=x, oper='>=', pkg2=candidate) if not any(
for x in installed]): (salt.utils.compare_versions(ver1=x,
oper='>=',
ver2=candidate,
cmp_func=version_cmp)
for x in installed)
):
ret[name] = candidate ret[name] = candidate
# Return a string if only one package name passed # Return a string if only one package name passed
@ -629,7 +634,7 @@ def upgrade_available(name):
return latest_version(name) != '' return latest_version(name) != ''
def perform_cmp(pkg1='', pkg2=''): def version_cmp(pkg1, pkg2):
''' '''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
@ -637,8 +642,7 @@ def perform_cmp(pkg1='', pkg2=''):
CLI Example:: CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0ubuntu1' '0.2.4.1-0ubuntu1' salt '*' pkg.version_cmp '0.2.4-0ubuntu1' '0.2.4.1-0ubuntu1'
salt '*' pkg.perform_cmp pkg1='0.2.4-0ubuntu1' pkg2='0.2.4.1-0ubuntu1'
''' '''
try: try:
for oper, ret in (('lt', -1), ('eq', 0), ('gt', 1)): for oper, ret in (('lt', -1), ('eq', 0), ('gt', 1)):
@ -651,18 +655,6 @@ def perform_cmp(pkg1='', pkg2=''):
return None return None
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)
def _split_repo_str(repo): def _split_repo_str(repo):
split = sourceslist.SourceEntry(repo) split = sourceslist.SourceEntry(repo)
return split.type, split.uri, split.dist, split.comps return split.type, split.uri, split.dist, split.comps

View File

@ -278,29 +278,3 @@ def upgrade_available(pkg):
salt '*' pkg.upgrade_available <package name> salt '*' pkg.upgrade_available <package name>
''' '''
return pkg in list_upgrades() return pkg in list_upgrades()
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -312,7 +312,7 @@ def list_states(env='base'):
return __context__['cp.fileclient'].list_states(env) return __context__['cp.fileclient'].list_states(env)
def list_master(env='base'): def list_master(env='base', prefix=''):
''' '''
List all of the files stored on the master List all of the files stored on the master
@ -321,10 +321,10 @@ def list_master(env='base'):
salt '*' cp.list_master salt '*' cp.list_master
''' '''
_mk_client() _mk_client()
return __context__['cp.fileclient'].file_list(env) return __context__['cp.fileclient'].file_list(env, prefix)
def list_master_dirs(env='base'): def list_master_dirs(env='base', prefix=''):
''' '''
List all of the directories stored on the master List all of the directories stored on the master
@ -333,7 +333,7 @@ def list_master_dirs(env='base'):
salt '*' cp.list_master_dirs salt '*' cp.list_master_dirs
''' '''
_mk_client() _mk_client()
return __context__['cp.fileclient'].dir_list(env) return __context__['cp.fileclient'].dir_list(env, prefix)
def list_minion(env='base'): def list_minion(env='base'):
@ -379,12 +379,12 @@ def push(path):
''' '''
Push a file from the minion up to the master, the file will be saved to Push a file from the minion up to the master, the file will be saved to
the salt master in the master's minion files cachedir the salt master in the master's minion files cachedir
(defaults to /var/cache/salt/master/minions/files) (defaults to ``/var/cache/salt/master/minions/minion-id/files``)
Since this feature allows a minion to push a file up to the master server Since this feature allows a minion to push a file up to the master server
it is disabled by default for security purposes. To enable add the option: it is disabled by default for security purposes. To enable, set
file_recv: True ``file_recv`` to ``True`` in the master configuration file, and restart the
to the master configuration and restart the master master.
CLI Example:: CLI Example::

View File

@ -132,7 +132,11 @@ def latest_version(*names, **kwargs):
installed = _cpv_to_version(_vartree().dep_bestmatch(name)) installed = _cpv_to_version(_vartree().dep_bestmatch(name))
avail = _cpv_to_version(_porttree().dep_bestmatch(name)) avail = _cpv_to_version(_porttree().dep_bestmatch(name))
if avail: if avail:
if not installed or compare(pkg1=installed, oper='<', pkg2=avail): if not installed \
or salt.utils.compare_versions(ver1=installed,
oper='<',
ver2=avail,
cmp_func=version_cmp):
ret[name] = avail ret[name] = avail
# Return a string if only one package name passed # Return a string if only one package name passed
@ -221,8 +225,8 @@ def porttree_matches(name):
''' '''
matches = [] matches = []
for category in _porttree().dbapi.categories: for category in _porttree().dbapi.categories:
if _porttree().dbapi.cp_list(category+"/"+name): if _porttree().dbapi.cp_list(category + "/" + name):
matches.append(category+"/"+name) matches.append(category + "/" + name)
return matches return matches
@ -276,7 +280,8 @@ def refresh_db():
if 'makeconf.features_contains'in __salt__ and __salt__['makeconf.features_contains']('webrsync-gpg'): if 'makeconf.features_contains'in __salt__ and __salt__['makeconf.features_contains']('webrsync-gpg'):
# GPG sign verify is supported only for "webrsync" # GPG sign verify is supported only for "webrsync"
cmd = 'emerge-webrsync -q' cmd = 'emerge-webrsync -q'
if salt.utils.which('emerge-delta-webrsync'): # We prefer 'delta-webrsync' to 'webrsync' # We prefer 'delta-webrsync' to 'webrsync'
if salt.utils.which('emerge-delta-webrsync'):
cmd = 'emerge-delta-webrsync -q' cmd = 'emerge-delta-webrsync -q'
return __salt__['cmd.retcode'](cmd) == 0 return __salt__['cmd.retcode'](cmd) == 0
else: else:
@ -284,7 +289,8 @@ def refresh_db():
return True return True
# We fall back to "webrsync" if "rsync" fails for some reason # We fall back to "webrsync" if "rsync" fails for some reason
cmd = 'emerge-webrsync -q' cmd = 'emerge-webrsync -q'
if salt.utils.which('emerge-delta-webrsync'): # We prefer 'delta-webrsync' to 'webrsync' # We prefer 'delta-webrsync' to 'webrsync'
if salt.utils.which('emerge-delta-webrsync'):
cmd = 'emerge-delta-webrsync -q' cmd = 'emerge-delta-webrsync -q'
return __salt__['cmd.retcode'](cmd) == 0 return __salt__['cmd.retcode'](cmd) == 0
@ -413,7 +419,7 @@ def install(name=None,
for param, version_num in pkg_params.iteritems(): for param, version_num in pkg_params.iteritems():
original_param = param original_param = param
param = _p_to_cp(param) param = _p_to_cp(param)
if param == None: if param is None:
raise portage.dep.InvalidAtom(original_param) raise portage.dep.InvalidAtom(original_param)
if version_num is None: if version_num is None:
@ -442,12 +448,12 @@ def install(name=None,
__salt__['portage_config.append_use_flags'](target[1:-1]) __salt__['portage_config.append_use_flags'](target[1:-1])
new = __salt__['portage_config.get_flags_from_package_conf']('use', target[1:-1]) new = __salt__['portage_config.get_flags_from_package_conf']('use', target[1:-1])
if old != new: if old != new:
changes[param+'-USE'] = {'old': old, 'new': new} changes[param + '-USE'] = {'old': old, 'new': new}
target = target[:target.rfind('[')] + '"' target = target[:target.rfind('[')] + '"'
if keyword != None: if keyword is not None:
__salt__['portage_config.append_to_package_conf']('accept_keywords', target[1:-1], ['~ARCH']) __salt__['portage_config.append_to_package_conf']('accept_keywords', target[1:-1], ['~ARCH'])
changes[param+'-ACCEPT_KEYWORD'] = {'old': '', 'new': '~ARCH'} changes[param + '-ACCEPT_KEYWORD'] = {'old': '', 'new': '~ARCH'}
targets.append(target) targets.append(target)
else: else:
@ -571,7 +577,7 @@ def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs):
targets = ['{0}:{1}'.format(fullatom, slot)] targets = ['{0}:{1}'.format(fullatom, slot)]
if fromrepo is not None: if fromrepo is not None:
targets = ['{0}::{1}'.format(fullatom, fromrepo)] targets = ['{0}::{1}'.format(fullatom, fromrepo)]
targets = [ fullatom ] targets = [fullatom]
else: else:
targets = [x for x in pkg_params if x in old] targets = [x for x in pkg_params if x in old]
@ -656,7 +662,7 @@ def depclean(name=None, slot=None, fromrepo=None, pkgs=None):
targets = ['{0}:{1}'.format(fullatom, slot)] targets = ['{0}:{1}'.format(fullatom, slot)]
if fromrepo is not None: if fromrepo is not None:
targets = ['{0}::{1}'.format(fullatom, fromrepo)] targets = ['{0}::{1}'.format(fullatom, fromrepo)]
targets = [ fullatom ] targets = [fullatom]
else: else:
targets = [x for x in pkg_params if x in old] targets = [x for x in pkg_params if x in old]
@ -667,7 +673,7 @@ def depclean(name=None, slot=None, fromrepo=None, pkgs=None):
return __salt__['pkg_resource.find_changes'](old, new) return __salt__['pkg_resource.find_changes'](old, new)
def perform_cmp(pkg1='', pkg2=''): def version_cmp(pkg1, pkg2):
''' '''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
@ -675,8 +681,7 @@ def perform_cmp(pkg1='', pkg2=''):
CLI Example:: CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0' salt '*' pkg.version_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
''' '''
regex = r'^~?([^:\[]+):?[^\[]*\[?.*$' regex = r'^~?([^:\[]+):?[^\[]*\[?.*$'
ver1 = re.match(regex, pkg1) ver1 = re.match(regex, pkg1)
@ -687,18 +692,6 @@ def perform_cmp(pkg1='', pkg2=''):
return None return None
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)
def version_clean(version): def version_clean(version):
''' '''
Clean the version string removing extra data. Clean the version string removing extra data.
@ -752,7 +745,8 @@ def check_extra_requirements(pkgname, pkgver):
des_uses = set(portage.dep.dep_getusedeps(atom)) des_uses = set(portage.dep.dep_getusedeps(atom))
cur_use = cur_use.split() cur_use = cur_use.split()
if len([ x for x in des_uses.difference(cur_use) if x[0]!='-' or x[1:] in cur_use ]) > 0: if len([x for x in des_uses.difference(cur_use)
if x[0] != '-' or x[1:] in cur_use]) > 0:
return False return False
if keyword: if keyword:

View File

@ -391,32 +391,6 @@ def rehash():
__salt__['cmd.run_all']('rehash') __salt__['cmd.run_all']('rehash')
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)
def file_list(*packages): def file_list(*packages):
''' '''
List the files that belong to a package. Not specifying any packages will List the files that belong to a package. Not specifying any packages will

View File

@ -216,3 +216,30 @@ def ls(): # pylint: disable=C0103
salt '*' grains.ls salt '*' grains.ls
''' '''
return sorted(__grains__) return sorted(__grains__)
def filter_by(lookup_dict, grain='os_family'):
'''
Look up the given grain in a given dictionary for the current OS and return
the result
Although this may occasionally be useful at the CLI, the primary intent of
this function is for use in Jinja to make short work of creating lookup
tables for OS-specific data. For example::
{% set pkg_table = {
'Debian': {'name': 'apache2'},
'RedHat': {'name': 'httpd'},
} %}
{% set pkg = salt['grains.filter_by'](pkg_table) %}
myapache:
pkg:
- installed
- name: {{ pkg.name }}
CLI Example::
salt '*' grains.filter_by '{Debian: Debheads rule, RedHat: I love my hat}'
'''
return lookup_dict.get(__grains__[grain], None)

View File

@ -222,29 +222,3 @@ def purge(name=None, pkgs=None, **kwargs):
salt '*' pkg.purge pkgs='["foo", "bar"]' salt '*' pkg.purge pkgs='["foo", "bar"]'
''' '''
return remove(name=name, pkgs=pkgs) return remove(name=name, pkgs=pkgs)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4' '0.2.4.1'
salt '*' pkg.perform_cmp pkg1='0.2.4' pkg2='0.2.4.1'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4' '<' '0.2.4.1'
salt '*' pkg.compare pkg1='0.2.4' oper='<' pkg2='0.2.4.1'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -396,32 +396,6 @@ def purge(name=None, pkgs=None, **kwargs):
return _uninstall(action='purge', name=name, pkgs=pkgs) return _uninstall(action='purge', name=name, pkgs=pkgs)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)
def file_list(*packages): def file_list(*packages):
''' '''
List the files that belong to a package. Not specifying any packages will List the files that belong to a package. Not specifying any packages will

View File

@ -3,7 +3,6 @@ Resources needed by pkg providers
''' '''
# Import python libs # Import python libs
import distutils.version # pylint: disable=E0611
import fnmatch import fnmatch
import logging import logging
import os import os
@ -97,7 +96,8 @@ def _parse_pkg_meta(path):
except AttributeError: except AttributeError:
continue continue
if arch: if arch:
name += ':{0}'.format(arch) if cpuarch == 'x86_64' and arch != 'amd64':
name += ':{0}'.format(arch)
return name, version return name, version
if __grains__['os_family'] in ('Suse', 'RedHat', 'Mandriva'): if __grains__['os_family'] in ('Suse', 'RedHat', 'Mandriva'):
@ -418,62 +418,10 @@ def find_changes(old=None, new=None):
return pkgs return pkgs
def perform_cmp(pkg1='', pkg2=''):
'''
Compares two version strings using distutils.version.LooseVersion. This is
a fallback for providers which don't have a version comparison utility
built into them. Return -1 if version1 < version2, 0 if version1 ==
version2, and 1 if version1 > version2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg_resource.perform_cmp
'''
try:
if distutils.version.LooseVersion(pkg1) < \
distutils.version.LooseVersion(pkg2):
return -1
elif distutils.version.LooseVersion(pkg1) == \
distutils.version.LooseVersion(pkg2):
return 0
elif distutils.version.LooseVersion(pkg1) > \
distutils.version.LooseVersion(pkg2):
return 1
except Exception as e:
log.exception(e)
return None
def compare(pkg1='', oper='==', pkg2=''):
'''
Package version comparison function.
CLI Example::
salt '*' pkg_resource.compare
'''
cmp_map = {'<': (-1,), '<=': (-1, 0), '==': (0,),
'>=': (0, 1), '>': (1,)}
if oper not in ['!='] + cmp_map.keys():
log.error('Invalid operator "{0}" for package '
'comparison'.format(oper))
return False
cmp_result = __salt__['pkg.perform_cmp'](pkg1, pkg2)
if cmp_result is None:
return False
if oper == '!=':
return cmp_result not in cmp_map['==']
else:
return cmp_result in cmp_map[oper]
def version_clean(version): def version_clean(version):
''' '''
Clean the version string removing extra data. Clean the version string removing extra data.
This function will simply try to call "pkg.version_clean". This function will simply try to call ``pkg.version_clean``.
CLI Example:: CLI Example::

View File

@ -423,32 +423,6 @@ def rehash():
__salt__['cmd.run']('rehash') __salt__['cmd.run']('rehash')
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)
def file_list(package): def file_list(package):
''' '''
List the files that belong to a package. List the files that belong to a package.

View File

@ -857,29 +857,3 @@ def updating(pkg_name, filedate=None, filename=None):
cmd = 'pkg updating {0} {1}'.format(opts, pkg_name) cmd = 'pkg updating {0} {1}'.format(opts, pkg_name)
return __salt__['cmd.run'](cmd) return __salt__['cmd.run'](cmd)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -194,7 +194,9 @@ def latest_version(*names, **kwargs):
if name in names: if name in names:
cver = pkgs.get(name, '') cver = pkgs.get(name, '')
nver = version_rev.split(',')[0] nver = version_rev.split(',')[0]
if not cver or compare(pkg1=cver, oper='<', pkg2=nver): if not cver or salt.utils.compare_versions(ver1=cver,
oper='<',
ver2=nver):
# Remove revision for version comparison # Remove revision for version comparison
ret[name] = version_rev ret[name] = version_rev
@ -326,29 +328,3 @@ def purge(name=None, pkgs=None, **kwargs):
salt '*' pkg.purge pkgs='["foo", "bar"]' salt '*' pkg.purge pkgs='["foo", "bar"]'
''' '''
return remove(name=name, pkgs=pkgs) return remove(name=name, pkgs=pkgs)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -436,29 +436,3 @@ def purge(name=None, pkgs=None, **kwargs):
salt '*' pkg.purge pkgs='["foo", "bar"]' salt '*' pkg.purge pkgs='["foo", "bar"]'
''' '''
return remove(name=name, pkgs=pkgs, **kwargs) return remove(name=name, pkgs=pkgs, **kwargs)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -706,29 +706,3 @@ def _get_latest_pkg_version(pkginfo):
return pkginfo.keys().pop() return pkginfo.keys().pop()
pkgkeys = pkginfo.keys() pkgkeys = pkginfo.keys()
return sorted(pkgkeys, cmp=_reverse_cmp_pkg_versions).pop() return sorted(pkgkeys, cmp=_reverse_cmp_pkg_versions).pop()
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -1039,32 +1039,6 @@ def _parse_repo_file(filename):
return (header, repos) return (header, repos)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)
def file_list(*packages): def file_list(*packages):
''' '''
List the files that belong to a package. Not specifying any packages will List the files that belong to a package. Not specifying any packages will

View File

@ -505,29 +505,3 @@ def purge(name=None, pkgs=None, **kwargs):
salt '*' pkg.purge pkgs='["foo", "bar"]' salt '*' pkg.purge pkgs='["foo", "bar"]'
''' '''
return remove(name=name, pkgs=pkgs) return remove(name=name, pkgs=pkgs)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -419,29 +419,3 @@ def purge(name=None, pkgs=None, **kwargs):
salt '*' pkg.purge pkgs='["foo", "bar"]' salt '*' pkg.purge pkgs='["foo", "bar"]'
''' '''
return _uninstall(action='purge', name=name, pkgs=pkgs) return _uninstall(action='purge', name=name, pkgs=pkgs)
def perform_cmp(pkg1='', pkg2=''):
'''
Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if
pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem
making the comparison.
CLI Example::
salt '*' pkg.perform_cmp '0.2.4-0' '0.2.4.1-0'
salt '*' pkg.perform_cmp pkg1='0.2.4-0' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.perform_cmp'](pkg1=pkg1, pkg2=pkg2)
def compare(pkg1='', oper='==', pkg2=''):
'''
Compare two version strings.
CLI Example::
salt '*' pkg.compare '0.2.4-0' '<' '0.2.4.1-0'
salt '*' pkg.compare pkg1='0.2.4-0' oper='<' pkg2='0.2.4.1-0'
'''
return __salt__['pkg_resource.compare'](pkg1=pkg1, oper=oper, pkg2=pkg2)

View File

@ -58,49 +58,6 @@ def _get_ref(repo, short):
return ref return ref
return False return False
def _wait_lock(lk_fn, dest):
'''
If the write lock is there, check to see if the file is actually being
written. If there is no change in the file size after a short sleep,
remove the lock and move forward.
'''
if not os.path.isfile(lk_fn):
return False
if not os.path.isfile(dest):
# The dest is not here, sleep for a bit, if the dest is not here yet
# kill the lockfile and start the write
time.sleep(1)
if not os.path.isfile(dest):
try:
os.remove(lk_fn)
except (OSError, IOError):
pass
return False
# There is a lock file, the dest is there, stat the dest, sleep and check
# that the dest is being written, if it is not being written kill the lock
# file and continue. Also check if the lock file is gone.
s_count = 0
s_size = os.stat(dest).st_size
while True:
time.sleep(1)
if not os.path.isfile(lk_fn):
return False
size = os.stat(dest).st_size
if size == s_size:
s_count += 1
if s_count >= 3:
# The file is not being written to, kill the lock and proceed
try:
os.remove(lk_fn)
except (OSError, IOError):
pass
return False
else:
s_size = size
return False
def init(branch, repo_location): def init(branch, repo_location):
''' '''
Return the git repo object for this session Return the git repo object for this session
@ -125,6 +82,7 @@ def init(branch, repo_location):
repo.create_remote('origin', repo_location) repo.create_remote('origin', repo_location)
except Exception: except Exception:
pass pass
repo.git.fetch()
return repo return repo
@ -141,14 +99,7 @@ def update(branch, repo_location):
except git.exc.GitCommandError as e: except git.exc.GitCommandError as e:
logging.error('Unable to checkout branch {0}: {1}'.format(branch, e)) logging.error('Unable to checkout branch {0}: {1}'.format(branch, e))
return False return False
lk_fn = os.path.join(repo.working_dir, 'update.lk')
with salt.utils.fopen(lk_fn, 'w+') as fp_:
fp_.write(str(pid))
repo.git.pull() repo.git.pull()
try:
os.remove(lk_fn)
except (OSError, IOError):
pass
return True return True

View File

@ -2,11 +2,16 @@
Execute salt convenience routines Execute salt convenience routines
''' '''
# Import python libs
import multiprocessing
import datetime
# Import salt libs # Import salt libs
import salt.loader import salt.loader
import salt.exceptions import salt.exceptions
import salt.utils import salt.utils
import salt.minion import salt.minion
import salt.utils.event
class RunnerClient(object): class RunnerClient(object):
@ -17,6 +22,23 @@ class RunnerClient(object):
self.opts = opts self.opts = opts
self.functions = salt.loader.runner(opts) self.functions = salt.loader.runner(opts)
def _proc_runner(self, tag, fun, arg, kwarg):
'''
Run this method in a multiprocess target to execute the runner in a
multiprocess and fire the return data on the event bus
'''
salt.utils.daemonize()
data = {}
try:
data['ret'] = self.cmd(fun, arg, kwarg)
except Exception as exc:
data['ret'] = 'Exception occured in runner {0}: {1}'.format(
fun,
exc,
)
event = salt.utils.event.MasterEvent(self.opts['sock_dir'])
event.fire_event(data, tag)
def _verify_fun(self, fun): def _verify_fun(self, fun):
''' '''
Check that the function passed really exists Check that the function passed really exists
@ -57,6 +79,19 @@ class RunnerClient(object):
ret = l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {})) ret = l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {}))
return ret return ret
def async(self, fun, arg, kwarg=None):
'''
Execute the runner in a multiprocess and return the event tag to use
to watch for the return
'''
tag = '{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.now())
tag[20] = 'r'
proc = multiprocessing.Process(
target=self._proc_runner,
args=(tag, fun, arg, kwarg))
proc.start()
return tag
class Runner(RunnerClient): class Runner(RunnerClient):
''' '''

View File

@ -21,7 +21,7 @@ import salt.minion
def decode_list(data): def decode_list(data):
''' '''
JSON decodes as unicode, Jinja needs raw strings... JSON decodes as unicode, Jinja needs bytes...
''' '''
rv = [] rv = []
for item in data: for item in data:
@ -37,25 +37,25 @@ def decode_list(data):
def decode_dict(data): def decode_dict(data):
''' '''
JSON decodes as unicode, Jinja needs raw strings... JSON decodes as unicode, Jinja needs bytes...
''' '''
rv = {} rv = {}
for key, value in data.iteritems(): for key, value in data.iteritems():
if isinstance(key, unicode): if isinstance(key, unicode):
key = key.encode('utf-8') key = key.encode('utf-8')
if isinstance(value, unicode): if isinstance(value, unicode):
value = value.encode('utf-8') value = value.encode('utf-8')
elif isinstance(value, list): elif isinstance(value, list):
value = decode_list(value) value = decode_list(value)
elif isinstance(value, dict): elif isinstance(value, dict):
value = decode_dict(value) value = decode_dict(value)
rv[key] = value rv[key] = value
return rv return rv
class SSH(object): class SSH(object):
''' '''
Create an ssh execution system Create an SSH execution system
''' '''
def __init__(self, opts): def __init__(self, opts):
self.opts = opts self.opts = opts
@ -85,7 +85,7 @@ class SSH(object):
def get_pubkey(self): def get_pubkey(self):
''' '''
Return the keystring for the ssh public key Return the keystring for the SSH public key
''' '''
priv = self.opts.get( priv = self.opts.get(
'ssh_priv', 'ssh_priv',
@ -101,7 +101,7 @@ class SSH(object):
def key_deploy(self, host, ret): def key_deploy(self, host, ret):
''' '''
Deploy the ssh key if the minions don't auth Deploy the SSH key if the minions don't auth
''' '''
if not isinstance(ret[host], basestring): if not isinstance(ret[host], basestring):
return ret return ret
@ -405,7 +405,7 @@ class Single():
class FunctionWrapper(dict): class FunctionWrapper(dict):
''' '''
Create an object that acts like the salt function dict and makes function Create an object that acts like the salt function dict and makes function
calls remotely via the ssh shell system calls remotely via the SSH shell system
''' '''
def __init__( def __init__(
self, self,
@ -440,7 +440,7 @@ class FunctionWrapper(dict):
class SSHState(salt.state.State): class SSHState(salt.state.State):
''' '''
Create a State object which wraps the ssh functions for state operations Create a State object which wraps the SSH functions for state operations
''' '''
def __init__(self, opts, pillar=None, wrapper=None): def __init__(self, opts, pillar=None, wrapper=None):
self.wrapper = wrapper self.wrapper = wrapper

View File

@ -1241,7 +1241,7 @@ def recurse(name,
if not _src_path: if not _src_path:
pass pass
elif _src_path.strip('/') not in __salt__['cp.list_master_dirs'](env): elif _src_path.strip('/') not in __salt__['cp.list_master_dirs'](env, _src_path):
ret['result'] = False ret['result'] = False
ret['comment'] = ( ret['comment'] = (
'The source: {0} does not exist on the master'.format(source) 'The source: {0} does not exist on the master'.format(source)
@ -1353,11 +1353,9 @@ def recurse(name,
#we're searching for things that start with this *directory*. #we're searching for things that start with this *directory*.
# use '/' since #master only runs on POSIX # use '/' since #master only runs on POSIX
srcpath = srcpath + '/' srcpath = srcpath + '/'
for fn_ in __salt__['cp.list_master'](env): for fn_ in __salt__['cp.list_master'](env, srcpath):
if not fn_.strip(): if not fn_.strip():
continue continue
if not fn_.startswith(srcpath):
continue
# fn_ here is the absolute (from file_roots) source path of # fn_ here is the absolute (from file_roots) source path of
# the file to copy from; it is either a normal file or an # the file to copy from; it is either a normal file or an
@ -1393,10 +1391,8 @@ def recurse(name,
manage_file(dest, src) manage_file(dest, src)
if include_empty: if include_empty:
mdirs = __salt__['cp.list_master_dirs'](env) mdirs = __salt__['cp.list_master_dirs'](env, srcpath)
for mdir in mdirs: for mdir in mdirs:
if not mdir.startswith(srcpath):
continue
if not _check_include_exclude(os.path.relpath(mdir, srcpath), if not _check_include_exclude(os.path.relpath(mdir, srcpath),
include_pat, include_pat,
exclude_pat): exclude_pat):

View File

@ -19,6 +19,7 @@ requisite to a pkg.installed state for the package which provides pip
''' '''
# Import python libs # Import python libs
import re
import urlparse import urlparse
# Import salt libs # Import salt libs
@ -33,6 +34,34 @@ def __virtual__():
return 'pip' if 'pip.list' in __salt__ else False return 'pip' if 'pip.list' in __salt__ else False
def _find_key(prefix, pip_list):
'''
Does a case-insensitive match in the pip_list for the desired package.
'''
try:
match = next(
iter(x for x in pip_list if x.lower() == prefix.lower())
)
except StopIteration:
return None
else:
return match
def _fulfills_version_spec(version, version_spec):
'''
Check version number against version specification info and return a
boolean value based on whether or not the version number meets the
specified version.
'''
for oper, spec in (version_spec[0:2], version_spec[2:4]):
if oper is None:
continue
if not salt.utils.compare_versions(ver1=version, oper=oper, ver2=spec):
return False
return True
def installed(name, def installed(name,
pip_bin=None, pip_bin=None,
requirements=None, requirements=None,
@ -87,17 +116,38 @@ def installed(name,
elif env and not bin_env: elif env and not bin_env:
bin_env = env bin_env = env
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
scheme, netloc, path, query, fragment = urlparse.urlsplit(name) scheme, netloc, path, query, fragment = urlparse.urlsplit(name)
if scheme and netloc: if scheme and netloc:
# parse as VCS url # parse as VCS url
prefix = path.lstrip('/').split('@', 1)[0] prefix = path.lstrip('/').split('@', 1)[0]
if scheme.startswith("git+"): if scheme.startswith('git+'):
prefix = prefix.rstrip(".git") prefix = prefix.rstrip('.git')
else: else:
# Pull off any requirements specifiers # Split the passed string into the prefix and version
prefix = name.split('=')[0].split('<')[0].split('>')[0].strip() try:
version_spec = list(re.match(
(r'([^=<>]+)(?:(?:([<>]=?|==?)([^<>=,]+))'
r'(?:,([<>]=?|==?)([^<>=]+))?)?$'),
name
).groups())
prefix = version_spec.pop(0)
except AttributeError:
ret['result'] = False
ret['comment'] = 'Invalidly-formatted package {0}'.format(name)
return ret
else:
# Check to see if '=' was used instead of '=='. version_spec will
# contain two sets of comparison operators and version numbers, so
# we are checking elements 0 and 2 of this list.
if any((version_spec[x] == '=' for x in (0, 2))):
ret['result'] = False
ret['comment'] = ('Invalid version specification in '
'package {0}. \'=\' is not supported, use '
'\'==\' instead.'.format(name))
return ret
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
if runas is not None: if runas is not None:
# The user is using a deprecated argument, warn! # The user is using a deprecated argument, warn!
msg = ( msg = (
@ -107,41 +157,48 @@ def installed(name,
salt.utils.warn_until((0, 18), msg) salt.utils.warn_until((0, 18), msg)
ret.setdefault('warnings', []).append(msg) ret.setdefault('warnings', []).append(msg)
# "There can only be one" # "There can only be one"
if runas is not None and user: if user:
raise CommandExecutionError( raise CommandExecutionError(
'The \'runas\' and \'user\' arguments are mutually exclusive. ' 'The \'runas\' and \'user\' arguments are mutually exclusive. '
'Please use \'user\' as \'runas\' is being deprecated.' 'Please use \'user\' as \'runas\' is being deprecated.'
) )
# Support deprecated 'runas' arg # Support deprecated 'runas' arg
elif runas is not None and not user: else:
user = runas user = runas
try: try:
pip_list = __salt__['pip.list'](prefix, bin_env, user=user, cwd=cwd) pip_list = __salt__['pip.list'](prefix, bin_env=bin_env,
user=user, cwd=cwd)
prefix_realname = _find_key(prefix, pip_list)
except (CommandNotFoundError, CommandExecutionError) as err: except (CommandNotFoundError, CommandExecutionError) as err:
ret['result'] = False ret['result'] = False
ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err) ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err)
return ret return ret
if ignore_installed is False and prefix.lower() in (p.lower() if ignore_installed is False and prefix_realname is not None:
for p in pip_list): if force_reinstall is False and not upgrade:
if force_reinstall is False and upgrade is False: # Check desired version (if any) against currently-installed
ret['result'] = True if (
ret['comment'] = 'Package already installed' any(version_spec) and
return ret _fulfills_version_spec(pip_list[prefix_realname],
version_spec)
) or (not any(version_spec)):
ret['result'] = True
ret['comment'] = ('Python package {0} already '
'installed'.format(name))
return ret
if __opts__['test']: if __opts__['test']:
ret['result'] = None ret['result'] = None
ret['comment'] = 'Python package {0} is set to be installed'.format( ret['comment'] = \
name) 'Python package {0} is set to be installed'.format(name)
return ret return ret
# Replace commas (used for version ranges) with semicolons (which are not # Replace commas (used for version ranges) with semicolons (which are not
# supported) in name so it does not treat them as multiple packages. Comma # supported) in name so it does not treat them as multiple packages. Comma
# will be re-added in pip.install call. Wrap in double quotes to allow for # will be re-added in pip.install call.
# version ranges name = name.replace(',', ';')
name = '"' + name.replace(',', ';') + '"'
if repo: if repo:
name = repo name = repo
@ -153,7 +210,7 @@ def installed(name,
name = '' name = ''
pip_install_call = __salt__['pip.install']( pip_install_call = __salt__['pip.install'](
pkgs=name, pkgs='"{0}"'.format(name),
requirements=requirements, requirements=requirements,
bin_env=bin_env, bin_env=bin_env,
log=log, log=log,

View File

@ -48,8 +48,10 @@ if salt.utils.is_windows():
from salt.modules.win_pkg import _reverse_cmp_pkg_versions from salt.modules.win_pkg import _reverse_cmp_pkg_versions
_get_package_info = namespaced_function(_get_package_info, globals()) _get_package_info = namespaced_function(_get_package_info, globals())
get_repo_data = namespaced_function(get_repo_data, globals()) get_repo_data = namespaced_function(get_repo_data, globals())
_get_latest_pkg_version = namespaced_function(_get_latest_pkg_version, globals()) _get_latest_pkg_version = \
_reverse_cmp_pkg_versions = namespaced_function(_reverse_cmp_pkg_versions, globals()) namespaced_function(_get_latest_pkg_version, globals())
_reverse_cmp_pkg_versions = \
namespaced_function(_reverse_cmp_pkg_versions, globals())
# The following imports are used by the namespaced win_pkg funcs # The following imports are used by the namespaced win_pkg funcs
# and need to be included in their globals. # and need to be included in their globals.
import msgpack import msgpack
@ -65,13 +67,16 @@ def __gen_rtag():
return os.path.join(__opts__['cachedir'], 'pkg_refresh') return os.path.join(__opts__['cachedir'], 'pkg_refresh')
def _fulfills_version_spec(versions, oper, desired_version): def _fulfills_version_spec(version, oper, desired_version):
''' '''
Returns True if any of the installed versions match the specified version, Returns True if any of the installed versions match the specified version,
otherwise returns False otherwise returns False
''' '''
for ver in versions: for ver in version:
if __salt__['pkg.compare'](pkg1=ver, oper=oper, pkg2=desired_version): if salt.utils.compare_versions(ver1=version,
oper=oper,
ver2=desired_version,
cmp_func=__salt__.get('version_cmp')):
return True return True
return False return False
@ -153,7 +158,8 @@ def _find_install_targets(name=None, version=None, pkgs=None, sources=None):
if not cver: if not cver:
targets[pkgname] = pkgver targets[pkgname] = pkgver
continue continue
elif not __salt__['pkg_resource.check_extra_requirements'](pkgname, pkgver): elif not __salt__['pkg_resource.check_extra_requirements'](pkgname,
pkgver):
targets[pkgname] = pkgver targets[pkgname] = pkgver
continue continue
# No version specified and pkg is installed, do not add to targets # No version specified and pkg is installed, do not add to targets
@ -170,7 +176,7 @@ def _find_install_targets(name=None, version=None, pkgs=None, sources=None):
comparison = gt_lt or '' comparison = gt_lt or ''
comparison += eq or '' comparison += eq or ''
# A comparison operator of "=" is redundant, but possible. # A comparison operator of "=" is redundant, but possible.
# Change it to "==" so that it works in pkg.compare. # Change it to "==" so that the version comparison works
if comparison in ['=', '']: if comparison in ['=', '']:
comparison = '==' comparison = '=='
if not _fulfills_version_spec(cver, comparison, verstr): if not _fulfills_version_spec(cver, comparison, verstr):
@ -215,7 +221,7 @@ def _verify_install(desired, new_pkgs):
comparison = gt_lt or '' comparison = gt_lt or ''
comparison += eq or '' comparison += eq or ''
# A comparison operator of "=" is redundant, but possible. # A comparison operator of "=" is redundant, but possible.
# Change it to "==" so that it works in pkg.compare. # Change it to "==" so that the version comparison works.
if comparison in ('=', ''): if comparison in ('=', ''):
comparison = '==' comparison = '=='
if _fulfills_version_spec(cver, comparison, verstr): if _fulfills_version_spec(cver, comparison, verstr):
@ -557,10 +563,12 @@ def latest(
msg = 'No information found for "{0}".'.format(pkg) msg = 'No information found for "{0}".'.format(pkg)
log.error(msg) log.error(msg)
problems.append(msg) problems.append(msg)
elif not cur[pkg] or \ elif not cur[pkg] \
__salt__['pkg.compare'](pkg1=cur[pkg], or salt.utils.compare_versions(
oper='<', ver1=cur[pkg],
pkg2=avail[pkg]): oper='<',
ver2=avail[pkg],
cmp_func=__salt__.get('version_cmp')):
targets[pkg] = avail[pkg] targets[pkg] = avail[pkg]
if problems: if problems:
@ -608,7 +616,8 @@ def latest(
if changes: if changes:
# Find failed and successful updates # Find failed and successful updates
failed = [x for x in targets if changes[x]['new'] != targets[x]] failed = [x for x in targets
if not changes.get(x) or changes[x]['new'] != targets[x]]
successful = [x for x in targets if x not in failed] successful = [x for x in targets if x not in failed]
comments = [] comments = []

View File

@ -5,6 +5,7 @@ from __future__ import absolute_import
# Import python libs # Import python libs
import datetime import datetime
import distutils.version # pylint: disable=E0611
import fnmatch import fnmatch
import hashlib import hashlib
import imp import imp
@ -1389,3 +1390,51 @@ def warn_until(version_info,
if _dont_call_warnings is False: if _dont_call_warnings is False:
warnings.warn(message, category, stacklevel=stacklevel) warnings.warn(message, category, stacklevel=stacklevel)
def version_cmp(pkg1, pkg2):
'''
Compares two version strings using distutils.version.LooseVersion. This is
a fallback for providers which don't have a version comparison utility
built into them. Return -1 if version1 < version2, 0 if version1 ==
version2, and 1 if version1 > version2. Return None if there was a problem
making the comparison.
'''
try:
if distutils.version.LooseVersion(pkg1) < \
distutils.version.LooseVersion(pkg2):
return -1
elif distutils.version.LooseVersion(pkg1) == \
distutils.version.LooseVersion(pkg2):
return 0
elif distutils.version.LooseVersion(pkg1) > \
distutils.version.LooseVersion(pkg2):
return 1
except Exception as e:
log.exception(e)
return None
def compare_versions(ver1='', oper='==', ver2='', cmp_func=None):
'''
Compares two version numbers. Accepts a custom function to perform the
cmp-style version comparison, otherwise uses version_cmp().
'''
cmp_map = {'<': (-1,), '<=': (-1, 0), '==': (0,),
'>=': (0, 1), '>': (1,)}
if oper not in ['!='] + cmp_map.keys():
log.error('Invalid operator "{0}" for version '
'comparison'.format(oper))
return False
if cmp_func is None:
cmp_func = version_cmp
cmp_result = cmp_func(ver1, ver2)
if cmp_result is None:
return False
if oper == '!=':
return cmp_result not in cmp_map['==']
else:
return cmp_result in cmp_map[oper]