Merge branch 'develop' of git://github.com/saltstack/salt into develop

This commit is contained in:
Eric Poelke 2012-01-25 13:56:32 -08:00
commit c23c5f902a
16 changed files with 390 additions and 119 deletions

View File

@ -28,8 +28,8 @@ Options
.. option:: -L, --list-all
List all public keys on this salt master, both accepted and pending
acceptance.
List all public keys on this salt master: accepted, pending,
and rejected.
.. option:: -a ACCEPT, --accept=ACCEPT
@ -39,6 +39,14 @@ Options
Accepts all pending public keys.
.. option:: -r REJECT, --reject=REJECT
Reject the named minion public key.
.. option:: -R, --reject-all
Rejects all pending public keys.
.. option:: -c CONFIG, --config=CONFIG
The master configuration file needs to be read to determine where the salt

View File

@ -16,6 +16,7 @@ running and the Salt :term:`minions <minion>` point to the master.
* `pyzmq`_ >= 2.1.9 — ZeroMQ Python bindings
* `M2Crypto`_ — Python OpenSSL wrapper
* `PyCrypto`_ — The Python cryptography toolkit
* `msgpack-python`_ — High-performance message interchange format
* `YAML`_ — Python YAML bindings
Optional Dependencies:

11
pkg/rpm/README.fedora Normal file
View File

@ -0,0 +1,11 @@
These packages are *optional* dependencies for salt. By default, they are not included in the salt RPMs.
Install any of these packages to enable the functionality within salt.
MySQL-python
libvirt-python
python-mako
pymongo
python-redis / redis
A semi-canonical list of the optional salt modules can be found at
https://github.com/saltstack/salt/blob/develop/doc/conf.py#L30

View File

@ -9,8 +9,8 @@
%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
Name: salt
Version: 0.9.4
Release: 6%{?dist}
Version: 0.9.6
Release: 2%{?dist}
Summary: A parallel remote execution system
Group: System Environment/Daemons
@ -23,6 +23,7 @@ Source3: %{name}-minion
Source4: %{name}-master.service
Source5: %{name}-syndic.service
Source6: %{name}-minion.service
Source7: README.fedora
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
@ -33,6 +34,7 @@ BuildRequires: python26-crypto
BuildRequires: python26-devel
BuildRequires: python26-PyYAML
BuildRequires: python26-m2crypto
BuildRequires: python26-msgpack
Requires: python26-crypto
Requires: python26-zmq
@ -40,6 +42,7 @@ Requires: python26-jinja2
Requires: python26-PyYAML
Requires: python26-m2crypto
Requires: python26-PyXML
Requires: python26-msgpack
%else
@ -48,6 +51,7 @@ BuildRequires: python-crypto
BuildRequires: python-devel
BuildRequires: PyYAML
BuildRequires: m2crypto
BuildRequires: python-msgpack
Requires: python-crypto
Requires: python-zmq
@ -55,6 +59,7 @@ Requires: python-jinja2
Requires: PyYAML
Requires: m2crypto
Requires: PyXML
Requires: python-msgpack
%endif
@ -71,7 +76,7 @@ BuildRequires: systemd-units
%endif
Requires: MySQL-python libvirt-python yum
#Requires: MySQL-python libvirt-python yum
%description
Salt is a distributed remote execution system used to execute commands and
@ -119,6 +124,11 @@ install -p -m 0644 %{SOURCE4} $RPM_BUILD_ROOT%{_unitdir}/
install -p -m 0644 %{SOURCE5} $RPM_BUILD_ROOT%{_unitdir}/
install -p -m 0644 %{SOURCE6} $RPM_BUILD_ROOT%{_unitdir}/
%endif
install -p %{SOURCE7} .
install -p -m 0640 $RPM_BUILD_ROOT%{_sysconfdir}/salt/minion.template $RPM_BUILD_ROOT%{_sysconfdir}/salt/minion
install -p -m 0640 $RPM_BUILD_ROOT%{_sysconfdir}/salt/master.template $RPM_BUILD_ROOT%{_sysconfdir}/salt/master
%clean
rm -rf $RPM_BUILD_ROOT
@ -129,6 +139,7 @@ rm -rf $RPM_BUILD_ROOT
%{python_sitelib}/%{name}/*
%{python_sitelib}/%{name}-%{version}-py?.?.egg-info
%doc %{_mandir}/man7/salt.7.*
%doc README.fedora
%files -n salt-minion
%defattr(-,root,root)
@ -143,7 +154,8 @@ rm -rf $RPM_BUILD_ROOT
%{_unitdir}/salt-minion.service
%endif
%config(noreplace) /etc/salt/minion
%config(noreplace) %{_sysconfdir}/salt/minion
%config %{_sysconfdir}/salt/minion.template
%files -n salt-master
%defattr(-,root,root)
@ -166,7 +178,8 @@ rm -rf $RPM_BUILD_ROOT
%{_unitdir}/salt-master.service
%{_unitdir}/salt-syndic.service
%endif
%config(noreplace) /etc/salt/master
%config(noreplace) %{_sysconfdir}/salt/master
%config %{_sysconfdir}/salt/master.template
%if ! (0%{?rhel} >= 7 || 0%{?fedora} >= 15)
@ -242,6 +255,12 @@ fi
%endif
%changelog
* Tue Jan 24 2012 Clint Savage <herlo1@gmail.com> - 0.9.6-2
- Added README.fedora and removed deps for optional modules
* Sat Jan 21 2012 Clint Savage <herlo1@gmail.com> - 0.9.6-1
- New upstream release
* Sun Jan 8 2012 Clint Savage <herlo1@gmail.com> - 0.9.4-6
- Missed some critical elements for SysV and rpmlint cleanup

View File

@ -1,6 +1,7 @@
# pip requirements file for Salt
Jinja2
pyzmq
msgpack-python
M2Crypto
pycrypto
msgpack-python
PyCrypto
PyYAML
pyzmq >= 2.1.9

View File

@ -19,6 +19,22 @@ except ImportError as e:
if e.message != 'No module named _msgpack':
raise
def set_pidfile(pidfile):
'''
Save the pidfile
'''
pdir = os.path.dirname(pidfile)
if not os.path.isdir(pdir):
os.makedirs(pdir)
try:
open(pidfile, 'w+').write(str(os.getpid()))
except IOError:
err = ('Failed to commit the pid file to location {0}, please verify'
' that the location is available').format(pidfile)
log.error(err)
def verify_env(dirs):
'''
Verify that the named directories are in place and that the environment
@ -103,6 +119,11 @@ class Master(object):
'--user',
dest='user',
help='Specify user to run minion')
parser.add_option('--pid-file',
dest='pidfile',
default='/var/run/salt-master.pid',
help=('Specify the location of the pidfile. Default'
' %default'))
parser.add_option('-l',
'--log-level',
dest='log_level',
@ -118,7 +139,8 @@ class Master(object):
cli = {'daemon': options.daemon,
'config': options.config,
'user': options.user}
'user': options.user,
'pidfile': options.pidfile}
return cli
@ -128,6 +150,7 @@ class Master(object):
'''
verify_env([os.path.join(self.opts['pki_dir'], 'minions'),
os.path.join(self.opts['pki_dir'], 'minions_pre'),
os.path.join(self.opts['pki_dir'], 'minions_rejected'),
os.path.join(self.opts['cachedir'], 'jobs'),
os.path.dirname(self.opts['log_file']),
self.opts['sock_dir'],
@ -143,6 +166,7 @@ class Master(object):
# Late import so logging works correctly
if check_user(self.opts['user'], log):
import salt.master
set_pidfile(self.cli['pidfile'])
master = salt.master.Master(self.opts)
if self.cli['daemon']:
# Late import so logging works correctly

View File

@ -447,6 +447,19 @@ class SaltKey(object):
action='store_true',
help='Accept all pending keys')
parser.add_option('-r',
'--reject',
dest='reject',
default='',
help='Reject the specified public key')
parser.add_option('-R',
'--reject-all',
dest='reject_all',
default=False,
action='store_true',
help='Reject all pending keys')
parser.add_option('-p',
'--print',
dest='print_',
@ -500,6 +513,8 @@ class SaltKey(object):
opts['list_all'] = options.list_all
opts['accept'] = options.accept
opts['accept_all'] = options.accept_all
opts['reject'] = options.reject
opts['reject_all'] = options.reject_all
opts['print'] = options.print_
opts['print_all'] = options.print_all
opts['delete'] = options.delete

View File

@ -43,7 +43,7 @@ class Caller(object):
)
except (TypeError, CommandExecutionError) as exc:
msg = 'Error running \'{0}\': {1}\n'
sys.stderr.write(msg.format(yellow, end, fun, str(exc)))
sys.stderr.write(msg.format(fun, str(exc)))
sys.exit(1)
except CommandNotFoundError as exc:
msg = 'Command not found in \'{0}\': {1}\n'

View File

@ -28,6 +28,8 @@ class Key(object):
subdir = ''
if key_type == 'pre':
subdir = 'minions_pre'
elif key_type == 'rej':
subdir = 'minions_rejected'
elif key_type == 'acc':
subdir = 'minions'
dir_ = os.path.join(self.opts['pki_dir'], subdir)
@ -60,12 +62,21 @@ class Key(object):
for key in sorted(self._keys('acc')):
print utils.GREEN + key + utils.ENDC
def _list_rejected(self):
'''
List the unaccepted keys
'''
print utils.LIGHT_BLUE + 'Rejected:' + utils.ENDC
for key in sorted(self._keys('rej')):
print utils.BLUE + key + utils.ENDC
def _list_all(self):
'''
List all keys
'''
self._list_pre()
self._list_accepted()
self._list_rejected()
def _print_key(self, name):
'''
@ -88,74 +99,94 @@ class Key(object):
for key in sorted(self._keys('acc', True)):
print ' ' + utils.GREEN + os.path.basename(key) + utils.ENDC
print open(key, 'r').read()
print utils.LIGHT_BLUE + 'Rejected keys:' + utils.ENDC
for key in sorted(self._keys('pre', True)):
print ' ' + utils.BLUE + os.path.basename(key) + utils.ENDC
print open(key, 'r').read()
def _accept(self, key):
'''
Accept a specified host's public key
'''
pre_dir = os.path.join(self.opts['pki_dir'], 'minions_pre')
minions = os.path.join(self.opts['pki_dir'], 'minions')
if not os.path.isdir(minions):
err = ('The minions directory is not present, ensure that the '
'master server has been started')
sys.stderr.write(err + '\n')
sys.exit(42)
if not os.path.isdir(pre_dir):
err = ('The minions_pre directory is not present, ensure '
'that the master server has been started')
sys.stderr.write(err + '\n')
sys.exit(42)
pre = os.listdir(pre_dir)
(minions_accepted,
minions_pre,
minions_rejected) = self._check_minions_directories()
pre = os.listdir(minions_pre)
if not pre.count(key):
err = ('The named host is unavailable, please accept an '
'available key')
sys.stderr.write(err + '\n')
sys.exit(43)
shutil.move(os.path.join(pre_dir, key), os.path.join(minions, key))
shutil.move(os.path.join(minions_pre, key),
os.path.join(minions_accepted, key))
def _accept_all(self):
'''
Accept all keys in pre
'''
pre_dir = os.path.join(self.opts['pki_dir'], 'minions_pre')
minions = os.path.join(self.opts['pki_dir'], 'minions')
if not os.path.isdir(minions):
err = ('The minions directory is not present, ensure that the '
'master server has been started')
sys.stderr.write(err + '\n')
sys.exit(42)
if not os.path.isdir(pre_dir):
err = ('The minions_pre directory is not present, ensure that the '
'master server has been started')
sys.stderr.write(err + '\n')
sys.exit(42)
for key in os.listdir(pre_dir):
(minions_accepted,
minions_pre,
minions_rejected) = self._check_minions_directories()
for key in os.listdir(minions_pre):
self._accept(key)
def _delete_key(self):
'''
Delete a key
'''
pre_dir = os.path.join(self.opts['pki_dir'], 'minions_pre')
minions = os.path.join(self.opts['pki_dir'], 'minions')
if not os.path.isdir(minions):
err = ('The minions directory is not present, ensure that the '
'master server has been started')
sys.stderr.write(err + '\n')
sys.exit(42)
if not os.path.isdir(pre_dir):
err = ('The minions_pre directory is not present, ensure that the '
'master server has been started')
sys.stderr.write(err + '\n')
sys.exit(42)
pre = os.path.join(pre_dir, self.opts['delete'])
acc = os.path.join(minions, self.opts['delete'])
(minions_accepted,
minions_pre,
minions_rejected) = self._check_minions_directories()
pre = os.path.join(minions_pre, self.opts['delete'])
acc = os.path.join(minions_accepted, self.opts['delete'])
rej= os.path.join(minions_rejected, self.opts['delete'])
if os.path.exists(pre):
os.remove(pre)
print 'Removed pending key %s' % self.opts['delete']
if os.path.exists(acc):
os.remove(acc)
print 'Removed accepted key %s' % self.opts['delete']
if os.path.exists(rej):
os.remove(rej)
print 'Removed rejected key %s' % self.opts['delete']
def _reject(self, key):
'''
Reject a specified host's public key
'''
(minions_accepted,
minions_pre,
minions_rejected) = self._check_minions_directories()
pre = os.listdir(minions_pre)
if not pre.count(key):
err = ('The named host is unavailable, please accept an '
'available key')
sys.stderr.write(err + '\n')
sys.exit(43)
shutil.move(os.path.join(minions_pre, key),
os.path.join(minions_rejected, key))
def _reject_all(self):
'''
Reject all keys in pre
'''
(minions_accepted,
minions_pre,
minions_rejected) = self._check_minions_directories()
for key in os.listdir(minions_pre):
self._reject(key)
def _check_minions_directories(self):
minions_accepted = os.path.join(self.opts['pki_dir'], 'minions')
minions_pre = os.path.join(self.opts['pki_dir'], 'minions_pre')
minions_rejected = os.path.join(self.opts['pki_dir'], 'minions_rejected')
for dir in [minions_accepted, minions_pre, minions_rejected]:
if not os.path.isdir(dir):
err = ('The minions directory {0} is not present, ensure '
'that the master server has been started'.format(dir))
sys.stderr.write(err + '\n')
sys.exit(42)
return minions_accepted, minions_pre, minions_rejected
def run(self):
'''
@ -179,6 +210,10 @@ class Key(object):
self._accept(self.opts['accept'])
elif self.opts['accept_all']:
self._accept_all()
elif self.opts['reject']:
self._reject(self.opts['reject'])
elif self.opts['reject_all']:
self._reject_all()
elif self.opts['delete']:
self._delete_key()
else:

View File

@ -42,7 +42,7 @@ def prep_jid(opts, load):
os.makedirs(jid_dir)
serial.dump(load, open(os.path.join(jid_dir, '.load.p'), 'w+'))
else:
return prep_jid(cachedir, load)
return prep_jid(opts['cachedir'], load)
return jid
@ -646,6 +646,9 @@ class ClearFuncs(object):
pubfn_pend = os.path.join(self.opts['pki_dir'],
'minions_pre',
load['id'])
pubfn_rejected = os.path.join(self.opts['pki_dir'],
'minions_rejected',
load['id'])
if self.opts['open_mode']:
# open mode is turned on, nuts to checks and overwrite whatever
# is there
@ -661,6 +664,12 @@ class ClearFuncs(object):
ret = {'enc': 'clear',
'load': {'ret': False}}
return ret
elif os.path.isfile(pubfn_rejected):
# The key has been rejected, don't place it in pending
log.info('Public key rejected for %(id)s', load)
ret = {'enc': 'clear',
'load': {'ret': False}}
return ret
elif not os.path.isfile(pubfn_pend)\
and not self.opts['auto_accept']:
# This is a new key, stick it in pre

View File

@ -208,8 +208,10 @@ class Minion(object):
minion side execution.
'''
if self.opts['multiprocessing']:
fn_ = os.path.join(self.proc_dir, str(os.getpid()))
open(fn_, 'w+').write(self.serial.dumps(data))
fn_ = os.path.join(self.proc_dir, data['jid'])
sdata = {'pid': os.getpid()}
sdata.update(data)
open(fn_, 'w+').write(self.serial.dumps(sdata))
ret = {}
for ind in range(0, len(data['arg'])):
try:
@ -313,7 +315,7 @@ class Minion(object):
Return the data from the executed command to the master server
'''
if self.opts['multiprocessing']:
fn_ = os.path.join(self.proc_dir, str(os.getpid()))
fn_ = os.path.join(self.proc_dir, ret['jid'])
if os.path.isfile(fn_):
os.remove(fn_)
log.info('Returning information for job: {0}'.format(ret['jid']))
@ -383,19 +385,10 @@ class Minion(object):
Check to see if the salt refresh file has been laid down, if it has,
refresh the functions and returners.
'''
if os.path.isfile(
os.path.join(
self.opts['cachedir'],
'.module_refresh'
)
):
fn_ = os.path.join(self.opts['cachedir'], 'module_refresh')
if os.path.isfile(fn_):
os.remove(fn_)
self.functions, self.returners = self.__load_modules()
os.remove(
os.path.join(
self.opts['cachedir'],
'.module_refresh'
)
)
def tune_in(self):
'''

View File

@ -10,6 +10,7 @@ import os
import subprocess
import tempfile
import salt.utils
from salt.exceptions import CommandExecutionError
try:
import pwd
except:
@ -19,16 +20,12 @@ except:
# Set up logging
log = logging.getLogger(__name__)
# Set the default working directory to the home directory
# of the user salt-minion is running as. Default: /root
DEFAULT_CWD = os.path.expanduser('~')
# Set up the default outputters
__outputter__ = {
'run': 'txt',
}
def _run(cmd,
cwd=DEFAULT_CWD,
cwd=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
quiet=False,
@ -37,38 +34,49 @@ def _run(cmd,
'''
Do the DRY thing and only call subprocess.Popen() once
'''
# Don't use runas in Windows
# Set the default working directory to the home directory
# of the user salt-minion is running as. Default: /root
if not cwd:
cwd = os.path.expanduser('~{0}'.format('' if not runas else runas))
# TODO: Figure out the proper way to do this in windows
disable_runas = [
'Windows',
]
ret = {}
if runas and __grains__['os'] not in disable_runas:
if runas and __grains__['os'] in disable_runas:
msg = 'Sorry, {0} does not support runas functionality'
raise CommandExecutionError(msg.format(__grains__['os']))
if runas:
# Save the original command before munging it
orig_cmd = cmd
try:
p = pwd.getpwnam(runas)
except KeyError:
stderr_str = 'The user {0} is not available'.format(runas)
if stderr == subprocess.STDOUT:
ret['stdout'] = stderr_str
else:
ret['stdout'] = ''
ret['stderr'] = stderr_str
ret['retcode'] = 1
return ret
cmd_prefix = 'su '
msg = 'User \'{0}\' is not available'.format(runas)
raise CommandExecutionError(msg)
cmd_prefix = 'su'
# Load the 'nix environment
if with_env:
cmd_prefix += '- '
cmd_prefix += ' - '
cmd_prefix += runas + ' -c'
cmd = '%s "%s"' % (cmd_prefix, cmd)
cmd = '{0} "{1}"'.format(cmd_prefix, cmd)
if not quiet:
if runas and __grains__['os'] not in disable_runas:
log.info('Executing command {0} as user {1} in directory {2}'.format(
cmd, runas, cwd))
else:
# Put the most common case first
if not runas:
log.info('Executing command {0} in directory {1}'.format(cmd, cwd))
else:
log.info('Executing command {0} as user {1} in directory {2}'.format(
orig_cmd, runas, cwd))
# This is where the magic happens
proc = subprocess.Popen(cmd,
cwd=cwd,
shell=True,
@ -77,21 +85,21 @@ def _run(cmd,
)
out = proc.communicate()
ret['stdout'] = out[0]
ret['stderr'] = out[1]
ret['stdout'] = out[0]
ret['stderr'] = out[1]
ret['pid'] = proc.pid
ret['retcode'] = proc.returncode
ret['pid'] = proc.pid
return ret
def _run_quiet(cmd, cwd=DEFAULT_CWD, runas=None):
def _run_quiet(cmd, cwd=None, runas=None):
'''
Helper for running commands quietly for minion startup
'''
return _run(cmd, runas=runas, cwd=cwd, stderr=subprocess.STDOUT, quiet=True)['stdout']
def run(cmd, cwd=DEFAULT_CWD, runas=None):
def run(cmd, cwd=None, runas=None):
'''
Execute the passed command and return the output as a string
@ -104,7 +112,7 @@ def run(cmd, cwd=DEFAULT_CWD, runas=None):
return out
def run_stdout(cmd, cwd=DEFAULT_CWD, runas=None):
def run_stdout(cmd, cwd=None, runas=None):
'''
Execute a command, and only return the standard out
@ -117,7 +125,7 @@ def run_stdout(cmd, cwd=DEFAULT_CWD, runas=None):
return stdout
def run_stderr(cmd, cwd=DEFAULT_CWD, runas=None):
def run_stderr(cmd, cwd=None, runas=None):
'''
Execute a command and only return the standard error
@ -130,7 +138,7 @@ def run_stderr(cmd, cwd=DEFAULT_CWD, runas=None):
return stderr
def run_all(cmd, cwd=DEFAULT_CWD, runas=None):
def run_all(cmd, cwd=None, runas=None):
'''
Execute the passed command and return a dict of return data
@ -150,7 +158,7 @@ def run_all(cmd, cwd=DEFAULT_CWD, runas=None):
return ret
def retcode(cmd, cwd=DEFAULT_CWD, runas=None):
def retcode(cmd, cwd=None, runas=None):
'''
Execute a shell command and return the command's return code.
@ -181,7 +189,7 @@ def which(cmd):
'''
return salt.utils.which(cmd)
def exec_code(lang, code, cwd=DEFAULT_CWD):
def exec_code(lang, code, cwd=None):
'''
Pass in two strings, the first naming the executable language, aka -
python2, python3, ruby, perl, lua, etc. the second string containing

View File

@ -19,30 +19,47 @@ def _check_puppet():
return __salt__['cmd.has_exec']('puppetd')
def run():
def run(tags=None):
'''
Execute a puppet run and return a dict with the stderr,stdout,return code
etc.
Execute a puppet run and return a dict with the stderr, stdout,
return code, etc. If an argument is specified, it is treated as
a comma separated list of tags passed to puppetd --test --tags:
http://projects.puppetlabs.com/projects/1/wiki/Using_Tags
CLI Example::
CLI Examples::
salt '*' puppet.run
'''
if _check_puppet():
return __salt__['cmd.run_all']('puppetd --test')
else:
raise CommandNotFoundError("puppetd not available")
def noop():
salt '*' puppet.run basefiles::edit,apache::server
'''
Execute a puppet noop run and return a dict with the stderr,stdout,return code
etc.
if not tags:
cmd = 'puppetd --test'
else:
cmd = 'puppetd --test --tags "{0}"'.format(tags)
if _check_puppet():
return __salt__['cmd.run_all'](cmd)
else:
raise CommandNotFoundError('puppetd not available')
def noop(tags=None):
'''
Execute a puppet noop run and return a dict with the stderr, stdout,
return code, etc. If an argument is specified, it is treated as a
comma separated list of tags passed to puppetd --test --noop --tags
CLI Example::
salt '*' puppet.noop
salt '*' puppet.noop web::server,django::base
'''
if _check_puppet():
return __salt__['cmd.run_all']('puppetd --test --noop')
if not tags:
cmd = 'puppetd --test --noop'
else:
raise CommandNotFoundError("puppetd not available")
cmd = 'puppetd --test --tags "{0}" --noop'.format(tags)
if _check_puppet():
return __salt__['cmd.run_all'](cmd)
else:
raise CommandNotFoundError('puppetd not available')

View File

@ -3,11 +3,16 @@ The Saltutil module is used to manage the state of the salt minion itself. It is
used to manage minion modules as well as automate updates to the salt minion
'''
# Import Python libs
import os
import hashlib
import shutil
import signal
import logging
# Import Salt libs
import salt.payload
log = logging.getLogger(__name__)
def _sync(form, env):
@ -42,7 +47,7 @@ def _sync(form, env):
shutil.copyfile(fn_, dest)
ret.append('{0}.{1}'.format(form, os.path.basename(fn_)))
if ret:
open(os.path.join(__opts__['cachedir'], '.module_refresh'), 'w+').write('')
open(os.path.join(__opts__['cachedir'], 'module_refresh'), 'w+').write('')
if __opts__.get('clean_dynamic_modules', True):
current = set(os.listdir(mod_dir))
for fn_ in current.difference(remote):
@ -138,3 +143,94 @@ def sync_all(env='base'):
ret.append(sync_renderers(env))
ret.append(sync_returners(env))
return ret
def running():
'''
Return the data on all running processes salt on the minion
CLI Example::
salt '*' saltutil.running
'''
procs = __salt__['status.procs']()
ret = []
serial = salt.payload.Serial(__opts__)
pid = os.getpid()
proc_dir = os.path.join(__opts__['cachedir'], 'proc')
if not os.path.isdir(proc_dir):
return []
for fn_ in os.listdir(proc_dir):
path = os.path.join(proc_dir, fn_)
data = serial.loads(open(path, 'rb').read())
if not procs.get(str(data['pid'])):
# The process is no longer running, clear out the file and
# continue
os.remove(path)
continue
if data.get('pid') == pid:
continue
ret.append(data)
return ret
def find_job(jid):
'''
Return the data for a specific job id
CLI Example::
salt '*' saltutil.find_job <job id>
'''
for data in running():
if data['jid'] == jid:
return data
return {}
def signal_job(jid, sig):
'''
Sends a signal to the named salt job's process
CLI Example::
salt '*' saltutil.signal_job <job id> 15
'''
for data in running():
if data['jid'] == jid:
try:
os.kill(int(data['pid']), sig)
return 'Signal {0} sent to job {1} at pid {2}'.format(
int(sig),
jid,
data['pid']
)
except OSError:
path = os.path.join(__opts__['cachedir'], 'proc', str(jid))
if os.path.isfile(path):
os.remove(path)
return ('Job {0} was not running and job data has been '
' cleaned up').format(jid)
return ''
def term_job(jid):
'''
Sends a termination signal (SIGTERM 15) to the named salt job's process
CLI Example::
salt '*' saltutil.term_job <job id>
'''
return signal_job(jid, signal.SIGTERM)
def kill_job(jid):
'''
Sends a termination signal (SIGTERM 15) to the named salt job's process
CLI Example::
salt '*' saltutil.kill_job <job id>
'''
return signal_job(jid, signal.SIGKILL)

View File

@ -26,6 +26,40 @@ def _number(text):
return text
def procs():
'''
Return the process data
CLI Example::
salt '*' status.procs
'''
# Get the user, pid and cmd
ret = {}
uind = 0
pind = 0
cind = 0
plines = __salt__['cmd.run'](__grains__['ps']).split('\n')
guide = plines.pop(0).split()
if 'USER' in guide:
uind = guide.index('USER')
elif 'UID' in guide:
uind = guide.index('UID')
if 'PID' in guide:
pind = guide.index('PID')
if 'COMMAND' in guide:
cind = guide.index('COMMAND')
elif 'CMD' in guide:
cind = guide.index('CMD')
for line in plines:
if not line:
continue
comps = line.split()
ret[comps[pind]] = {'user': comps[uind],
'cmd': ' '.join(comps[cind:])}
return ret
def custom():
'''
Return a custom composite of status data and info for this minon,

View File

@ -125,13 +125,13 @@ class State(object):
self.load_modules()
open(os.path.join(
self.opts['cachedir'],
'.module_refresh'),
'module_refresh'),
'w+').write('')
elif data['fun'] == 'recurse':
self.load_modules()
open(os.path.join(
self.opts['cachedir'],
'.module_refresh'),
'module_refresh'),
'w+').write('')
def format_verbosity(self, returns):