mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Merge branch 'develop' of git://github.com/saltstack/salt into develop
This commit is contained in:
commit
c23c5f902a
@ -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
|
||||
|
@ -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
11
pkg/rpm/README.fedora
Normal 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
|
@ -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
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
# pip requirements file for Salt
|
||||
Jinja2
|
||||
pyzmq
|
||||
msgpack-python
|
||||
M2Crypto
|
||||
pycrypto
|
||||
msgpack-python
|
||||
PyCrypto
|
||||
PyYAML
|
||||
pyzmq >= 2.1.9
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
117
salt/cli/key.py
117
salt/cli/key.py
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
'''
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user