mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 16:45:27 +00:00
Merge branch 'develop' of https://github.com/saltstack/salt into develop
resolution of documentation merge with upstream develop branch
This commit is contained in:
commit
607dab0e71
@ -14,7 +14,7 @@ before_install:
|
||||
- "if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi"
|
||||
|
||||
install: pip install -r requirements.txt --use-mirrors
|
||||
script: sudo -E python setup.py test --runtests-opts='--run-destructive'
|
||||
script: sudo -E python setup.py test --runtests-opts='--run-destructive -v'
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
|
@ -154,6 +154,10 @@ If it is less than 2047, you should increase it with::
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
You will need a recent version of ``virtualenv``::
|
||||
|
||||
pip install "virtualenv>=1.8.2"
|
||||
|
||||
You will need ``mock`` to run the tests::
|
||||
|
||||
pip install mock
|
||||
|
@ -7,7 +7,6 @@ include tests/*.py
|
||||
recursive-include tests *.py
|
||||
include tests/integration/modules/files/*
|
||||
include tests/integration/files/*
|
||||
include tests/integration/tmp/_README
|
||||
include tests/unit/templates/files/*
|
||||
recursive-include doc *
|
||||
recursive-include scripts *
|
||||
|
@ -13,8 +13,8 @@
|
||||
#publish_port: 4505
|
||||
|
||||
# Refresh the publisher connections when sending out commands, this is a fix
|
||||
# for zeromq losing some minion connections. Default: True
|
||||
#pub_refresh: True
|
||||
# for zeromq losing some minion connections. Default: False
|
||||
#pub_refresh: False
|
||||
|
||||
# The user to run the salt-master as. Salt will update all permissions to
|
||||
# allow the specified user to run the master. If the modified files cause
|
||||
@ -144,7 +144,14 @@
|
||||
# larry:
|
||||
# - test.ping
|
||||
# - network.*
|
||||
|
||||
#
|
||||
# The external auth system uses the Salt auth modules to authenticate and
|
||||
# validate users to access areas of the Salt system
|
||||
#
|
||||
# external_auth:
|
||||
# pam:
|
||||
# fred:
|
||||
# - test.*
|
||||
|
||||
##### Master Module Management #####
|
||||
##########################################
|
||||
@ -164,6 +171,17 @@
|
||||
# root of the base environment as defined in "File Server settings" below.
|
||||
#state_top: top.sls
|
||||
#
|
||||
# The master_tops option replaces the external_nodes option by creating
|
||||
# a plugable system for the generation of external top data. The external_nodes
|
||||
# option is deprecated by the master_tops option.
|
||||
# To gain the capabilities of the classic external_nodes system use the
|
||||
# following configuration
|
||||
#
|
||||
# master_tops:
|
||||
# ext_nodes: <Shell command which returns yaml>
|
||||
#
|
||||
#master_tops: {}
|
||||
#
|
||||
# The external_nodes option allows Salt to gather data that would normally be
|
||||
# placed in a top file. The external_nodes option is the executable that will
|
||||
# return the ENC data. Remember that Salt will look for external nodes AND top
|
||||
@ -238,6 +256,10 @@
|
||||
# - hiera: /etc/hiera.yaml
|
||||
# - cmd_yaml: cat /etc/salt/yaml
|
||||
#
|
||||
# The pillar_opts option adds the master configuration file data to a dict in
|
||||
# the pillar called "master". This is used to set simple configurations in the
|
||||
# master config file that can then be used on minions.
|
||||
#pillar_opts: True
|
||||
|
||||
##### Syndic settings #####
|
||||
##########################################
|
||||
@ -342,6 +364,6 @@
|
||||
|
||||
##### Range Cluster settings #####
|
||||
##########################################
|
||||
# The range server (and optional port) that
|
||||
# serves your cluster information
|
||||
# The range server (and optional port) that serves your cluster information
|
||||
# https://github.com/grierj/range/wiki/Introduction-to-Range-with-YAML-files
|
||||
#range_server: range:80
|
||||
|
@ -164,12 +164,6 @@
|
||||
# failure detected in the state execution, defaults to False
|
||||
#failhard: False
|
||||
#
|
||||
# state_verbose allows for the data returned from the minion to be more
|
||||
# verbose. Normally only states that fail or states that have changes are
|
||||
# returned, but setting state_verbose to True will return all states that
|
||||
# were checked
|
||||
#state_verbose: False
|
||||
#
|
||||
# autoload_dynamic_modules Turns on automatic loading of modules found in the
|
||||
# environments on the master. This is turned on by default, to turn of
|
||||
# autoloading modules when states run set this value to False
|
||||
|
2
debian/changelog
vendored
2
debian/changelog
vendored
@ -2,7 +2,7 @@ salt (0.10.3) precise; urgency=low
|
||||
|
||||
* New upstream version
|
||||
|
||||
-- Thomas S Hatch <thatch@saltstack.com> Sun, 30 Aug 2012 13:34:10 -0700
|
||||
-- Tom Vaughan <thomas.david.vaughan@gmail.com> Sun, 30 Aug 2012 13:34:10 -0700
|
||||
|
||||
salt (0.10.2) precise; urgency=low
|
||||
|
||||
|
2
debian/copyright
vendored
2
debian/copyright
vendored
@ -1,7 +1,7 @@
|
||||
Format: http://dep.debian.net/deps/dep5
|
||||
Upstream-Name: salt
|
||||
Upstream-Contact: salt-users@googlegroups.com
|
||||
Source: https://github.com/downloads/saltstack/salt/salt-0.9.9.tar.gz
|
||||
Source: https://github.com/downloads/saltstack/salt/salt-0.10.3.tar.gz
|
||||
|
||||
Files: *
|
||||
Copyright: 2012 Thomas S Hatch <thatch45@gmail.com>
|
||||
|
41
debian/salt-common.postrm
vendored
Normal file
41
debian/salt-common.postrm
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# Purge config files, logs, and directories created after package install.
|
||||
# Note that user-specified alternate locations for these are not affected.
|
||||
|
||||
clean_common() {
|
||||
# remove shared job cache and other runtime directories
|
||||
rm -rf /var/cache/salt /var/run/salt /etc/salt /var/log/salt 2> /dev/null
|
||||
}
|
||||
|
||||
clean_conf() {
|
||||
# remove config and log file for master, minion, or syndic
|
||||
rm -f /etc/salt/$1 /var/log/salt/$1 2> /dev/null
|
||||
# XXX add more specific files to purge here XXX #
|
||||
}
|
||||
|
||||
purgefiles() {
|
||||
case "$pkg" in
|
||||
master|minion|syndic)
|
||||
clean_conf $pkg ;;
|
||||
common)
|
||||
clean_common ;;
|
||||
*)
|
||||
echo "$0 unknown package \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
pkg=`echo $0 | cut -f1 -d. | cut -f2 -d-`
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
;;
|
||||
purge)
|
||||
purgefiles ;;
|
||||
upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
|
||||
;;
|
||||
*)
|
||||
echo "$0 unknown action \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
exit 0
|
41
debian/salt-master.postrm
vendored
Normal file
41
debian/salt-master.postrm
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# Purge config files, logs, and directories created after package install.
|
||||
# Note that user-specified alternate locations for these are not affected.
|
||||
|
||||
clean_common() {
|
||||
# remove shared job cache and other runtime directories
|
||||
rm -rf /var/cache/salt /var/run/salt /etc/salt /var/log/salt 2> /dev/null
|
||||
}
|
||||
|
||||
clean_conf() {
|
||||
# remove config and log file for master, minion, or syndic
|
||||
rm -f /etc/salt/$1 /var/log/salt/$1 2> /dev/null
|
||||
# XXX add more specific files to purge here XXX #
|
||||
}
|
||||
|
||||
purgefiles() {
|
||||
case "$pkg" in
|
||||
master|minion|syndic)
|
||||
clean_conf $pkg ;;
|
||||
common)
|
||||
clean_common ;;
|
||||
*)
|
||||
echo "$0 unknown package \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
pkg=`echo $0 | cut -f1 -d. | cut -f2 -d-`
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
;;
|
||||
purge)
|
||||
purgefiles ;;
|
||||
upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
|
||||
;;
|
||||
*)
|
||||
echo "$0 unknown action \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
exit 0
|
3
debian/salt-master.upstart
vendored
3
debian/salt-master.upstart
vendored
@ -5,7 +5,4 @@ start on (net-device-up
|
||||
and runlevel [2345])
|
||||
stop on runlevel [!2345]
|
||||
|
||||
respawn limit 10 5
|
||||
respawn
|
||||
|
||||
exec /usr/bin/salt-master >/dev/null 2>&1
|
||||
|
41
debian/salt-minion.postrm
vendored
Normal file
41
debian/salt-minion.postrm
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# Purge config files, logs, and directories created after package install.
|
||||
# Note that user-specified alternate locations for these are not affected.
|
||||
|
||||
clean_common() {
|
||||
# remove shared job cache and other runtime directories
|
||||
rm -rf /var/cache/salt /var/run/salt /etc/salt /var/log/salt 2> /dev/null
|
||||
}
|
||||
|
||||
clean_conf() {
|
||||
# remove config and log file for master, minion, or syndic
|
||||
rm -f /etc/salt/$1 /var/log/salt/$1 2> /dev/null
|
||||
# XXX add more specific files to purge here XXX #
|
||||
}
|
||||
|
||||
purgefiles() {
|
||||
case "$pkg" in
|
||||
master|minion|syndic)
|
||||
clean_conf $pkg ;;
|
||||
common)
|
||||
clean_common ;;
|
||||
*)
|
||||
echo "$0 unknown package \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
pkg=`echo $0 | cut -f1 -d. | cut -f2 -d-`
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
;;
|
||||
purge)
|
||||
purgefiles ;;
|
||||
upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
|
||||
;;
|
||||
*)
|
||||
echo "$0 unknown action \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
exit 0
|
3
debian/salt-minion.upstart
vendored
3
debian/salt-minion.upstart
vendored
@ -5,7 +5,4 @@ start on (net-device-up
|
||||
and runlevel [2345])
|
||||
stop on runlevel [!2345]
|
||||
|
||||
respawn limit 10 5
|
||||
respawn
|
||||
|
||||
exec /usr/bin/salt-minion >/dev/null 2>&1
|
||||
|
41
debian/salt-syndic.postrm
vendored
Normal file
41
debian/salt-syndic.postrm
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# Purge config files, logs, and directories created after package install.
|
||||
# Note that user-specified alternate locations for these are not affected.
|
||||
|
||||
clean_common() {
|
||||
# remove shared job cache and other runtime directories
|
||||
rm -rf /var/cache/salt /var/run/salt /etc/salt /var/log/salt 2> /dev/null
|
||||
}
|
||||
|
||||
clean_conf() {
|
||||
# remove config and log file for master, minion, or syndic
|
||||
rm -f /etc/salt/$1 /var/log/salt/$1 2> /dev/null
|
||||
# XXX add more specific files to purge here XXX #
|
||||
}
|
||||
|
||||
purgefiles() {
|
||||
case "$pkg" in
|
||||
master|minion|syndic)
|
||||
clean_conf $pkg ;;
|
||||
common)
|
||||
clean_common ;;
|
||||
*)
|
||||
echo "$0 unknown package \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
pkg=`echo $0 | cut -f1 -d. | cut -f2 -d-`
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
;;
|
||||
purge)
|
||||
purgefiles ;;
|
||||
upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
|
||||
;;
|
||||
*)
|
||||
echo "$0 unknown action \`$1'" 1>&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
exit 0
|
38
doc/conf.py
38
doc/conf.py
@ -1,5 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# pylint: disable=C0103,W0622
|
||||
'''
|
||||
Sphinx documentation for Salt
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
import types
|
||||
@ -7,6 +10,7 @@ import types
|
||||
from sphinx.directives import TocTree
|
||||
|
||||
|
||||
# pylint: disable=R0903
|
||||
class Mock(object):
|
||||
'''
|
||||
Mock out specified imports
|
||||
@ -30,34 +34,36 @@ class Mock(object):
|
||||
return type(name, (), {})
|
||||
else:
|
||||
return Mock()
|
||||
# pylint: enable=R0903
|
||||
|
||||
MOCK_MODULES = [
|
||||
# salt core
|
||||
'yaml',
|
||||
'yaml.nodes',
|
||||
'yaml.constructor',
|
||||
'msgpack',
|
||||
'zmq',
|
||||
'Crypto',
|
||||
'Crypto.Cipher',
|
||||
'Crypto.Hash',
|
||||
'Crypto.PublicKey',
|
||||
'Crypto.Random',
|
||||
'M2Crypto',
|
||||
'msgpack',
|
||||
'yaml',
|
||||
'yaml.constructor',
|
||||
'yaml.nodes',
|
||||
'zmq',
|
||||
# modules, renderers, states, returners, et al
|
||||
'django',
|
||||
'libvirt',
|
||||
'mako',
|
||||
'mako.template',
|
||||
'MySQLdb',
|
||||
'MySQLdb.cursors',
|
||||
'psutil',
|
||||
'libvirt',
|
||||
'yum',
|
||||
'mako',
|
||||
'mako.template',
|
||||
'pycassa',
|
||||
'pymongo',
|
||||
'redis',
|
||||
'rpm',
|
||||
'rpmUtils',
|
||||
'rpmUtils.arch',
|
||||
'pycassa',
|
||||
'yum',
|
||||
]
|
||||
|
||||
for mod_name in MOCK_MODULES:
|
||||
@ -92,8 +98,12 @@ master_doc = 'contents'
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
extensions = ['saltdocs', 'sphinx.ext.autodoc', 'sphinx.ext.extlinks',
|
||||
'sphinx.ext.autosummary']
|
||||
extensions = [
|
||||
'saltdocs',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.autosummary',
|
||||
'sphinx.ext.extlinks',
|
||||
]
|
||||
|
||||
modindex_common_prefix = ['salt.']
|
||||
|
||||
@ -144,7 +154,7 @@ html_context = {
|
||||
'github_downloads': 'https://github.com/saltstack/salt/downloads',
|
||||
}
|
||||
|
||||
html_use_index = False
|
||||
html_use_index = True
|
||||
html_last_updated_fmt = '%b %d, %Y'
|
||||
html_show_sourcelink = False
|
||||
html_show_sphinx = True
|
||||
|
@ -14,11 +14,14 @@ Full Table of Contents
|
||||
topics/tutorials/modules
|
||||
topics/tutorials/starting_states
|
||||
topics/tutorials/states*
|
||||
topics/eauth/*
|
||||
topics/tutorials/firewall
|
||||
topics/tutorials/bootstrap_ec2
|
||||
topics/tutorials/esky
|
||||
topics/tutorials/preseed_key
|
||||
topics/tutorials/standalone_minion
|
||||
topics/pillar/index
|
||||
topics/master_tops/index
|
||||
topics/jobs/index
|
||||
topics/nonroot
|
||||
topics/troubleshooting/index
|
||||
@ -26,6 +29,7 @@ Full Table of Contents
|
||||
topics/community
|
||||
topics/projects/index
|
||||
topics/event/index
|
||||
topics/tests/*
|
||||
|
||||
ref/index
|
||||
ref/modules/*
|
||||
@ -38,7 +42,9 @@ Full Table of Contents
|
||||
ref/renderers/all/index
|
||||
ref/pillar/*
|
||||
ref/pillar/all/index
|
||||
ref/runners
|
||||
ref/tops/*
|
||||
ref/topstops/all/index
|
||||
ref/runners/index
|
||||
ref/peer
|
||||
ref/clientacl
|
||||
ref/syndic
|
||||
|
@ -70,7 +70,6 @@ truly make it work for you.
|
||||
.. sidebar:: More tutorials!
|
||||
|
||||
* :doc:`Bootstraping Salt on EC2 <topics/tutorials/bootstrap_ec2>`
|
||||
* :doc:`Installing Salt on FreeBSD <topics/installation/freebsd>`
|
||||
* :doc:`Preseeding Minions with Accepted Keys <topics/tutorials/preseed_key>`
|
||||
|
||||
.. contents:: The components of Salt
|
||||
@ -197,11 +196,14 @@ Salt is many splendid things.
|
||||
management. The possibilities are endless and Salt's future looks
|
||||
bright.
|
||||
|
||||
:doc:`Testing Salt <topics/tests/index>`
|
||||
A howto for writing unit tests and integration tests.
|
||||
|
||||
:doc:`Python API interface <ref/python-api>`
|
||||
Use Salt programmatically from your own scripts and programs easily and
|
||||
simply via ``import salt``.
|
||||
|
||||
:doc:`Automatic Updates and Frozen Deployments <ref/esky>`
|
||||
:doc:`Automatic Updates and Frozen Binary Deployments <topics/tutorials/esky>`
|
||||
Use a frozen install to make deployments easier (Even on Windows!). Or
|
||||
take advantage of automatic updates to keep your minions running your
|
||||
latest builds.
|
||||
|
@ -135,6 +135,22 @@ Calling the Function
|
||||
The function to call on the specified target is placed after the target
|
||||
specification.
|
||||
|
||||
.. versionadded:: 0.9.8
|
||||
|
||||
Functions may also accept arguments, space-delimited:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' cmd.exec_code python 'import sys; print sys.version'
|
||||
|
||||
Optional, keyword arguments are also supported:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pip.install salt timeout=5 upgrade=True
|
||||
|
||||
They are always in the form of ``kwarg=argument``.
|
||||
|
||||
Finding available minion functions
|
||||
``````````````````````````````````
|
||||
|
||||
|
@ -49,7 +49,8 @@ Options
|
||||
|
||||
.. option:: -d DELETE, --delete=DELETE
|
||||
|
||||
Delete the named minion key for command execution.
|
||||
Delete the named minion key or minion keys matching a glob for command
|
||||
execution.
|
||||
|
||||
.. option:: -D, --delete-all
|
||||
|
||||
|
@ -94,7 +94,11 @@ Options
|
||||
.. option:: -N, --nodegroup
|
||||
|
||||
Use a predefined compound target defined in the Salt master configuration
|
||||
file
|
||||
file.
|
||||
|
||||
.. option:: -S, --ipcidr
|
||||
|
||||
Match based on Subnet (CIDR notation) or IPv4 address.
|
||||
|
||||
.. option:: -R, --range
|
||||
|
||||
|
@ -36,5 +36,5 @@ existing user keys and re-start the Salt master:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
rm /var/cache/salt/.*keys
|
||||
rm /var/cache/salt/.*key
|
||||
service salt-master restart
|
||||
|
@ -42,6 +42,22 @@ The network port to set up the publication interface
|
||||
|
||||
publish_port: 4505
|
||||
|
||||
|
||||
.. conf_master:: pub_refresh
|
||||
|
||||
``pub_refresh``
|
||||
---------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
The pub_refresh system manually refreshed the master ZeroMQ publisher. It is
|
||||
used in some cases where the minions loose connection to the master and it
|
||||
is solved by restarting the master.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pub_refresh: False
|
||||
|
||||
.. conf_master:: user
|
||||
|
||||
``user``
|
||||
@ -55,6 +71,32 @@ The user to run the Salt processes
|
||||
|
||||
user: root
|
||||
|
||||
.. conf_master:: max_open_files
|
||||
|
||||
``max_open_files``
|
||||
------------------
|
||||
|
||||
Default: ``max_open_files``
|
||||
|
||||
Each minion connecting to the master uses AT LEAST one file descriptor, the
|
||||
master subscription connection. If enough minions connect you might start
|
||||
seeing on the console(and then salt-master crashes):
|
||||
Too many open files (tcp_listener.cpp:335)
|
||||
Aborted (core dumped)
|
||||
|
||||
By default this value will be the one of `ulimit -Hn`, ie, the hard limit for
|
||||
max open files.
|
||||
|
||||
If you wish to set a different value than the default one, uncomment and
|
||||
configure this setting. Remember that this value CANNOT be higher than the
|
||||
hard limit. Raising the hard limit depends on your OS and/or distribution,
|
||||
a good way to find the limit is to search the internet for(for example):
|
||||
raise max open files hard limit debian
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
max_open_files: 100000
|
||||
|
||||
.. conf_master:: worker_threads
|
||||
|
||||
``worker_threads``
|
||||
@ -84,6 +126,19 @@ execution returns and command executions.
|
||||
|
||||
ret_port: 4506
|
||||
|
||||
.. conf_master:: pidfile
|
||||
|
||||
``pidfile``
|
||||
-----------
|
||||
|
||||
Default: ``/var/run/salt-master.pid``
|
||||
|
||||
Specify the location of the master pidfile
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pidfile: /var/run/salt-master.pid
|
||||
|
||||
.. conf_master:: root_dir
|
||||
|
||||
``root_dir``
|
||||
|
@ -112,7 +112,7 @@ The location for minion cache data.
|
||||
|
||||
cachedir: /var/cache/salt
|
||||
|
||||
.. conf_minion:: cachedir
|
||||
.. conf_minion:: backup_mode
|
||||
|
||||
``backup_mode``
|
||||
---------------
|
||||
@ -263,6 +263,17 @@ This setting requires that ``gcc`` and ``cython`` are installed on the minion
|
||||
|
||||
cython_enable: False
|
||||
|
||||
.. conf_minion:: providers
|
||||
|
||||
``providers``
|
||||
-------------
|
||||
|
||||
Default: (empty)
|
||||
|
||||
A module provider can be statically overwritten or extended for the minion via
|
||||
the providers option. This can be done on an individual basis in an SLS file or
|
||||
globally here in the minion config.
|
||||
|
||||
State Management Settings
|
||||
-------------------------
|
||||
|
||||
|
@ -5,16 +5,23 @@ Dynamic Module Distribution
|
||||
.. versionadded:: 0.9.5
|
||||
|
||||
Salt Python modules can be distributed automatically via the Salt file server.
|
||||
Under the root of any environment defined via the file_roots option on the
|
||||
master server directories corresponding to the type of module can be used.
|
||||
Under the root of any environment defined via the :conf_master:`file_roots`
|
||||
option on the master server directories corresponding to the type of module can
|
||||
be used.
|
||||
|
||||
.. glossary::
|
||||
|
||||
Module sync
|
||||
Automatically transfer and load modules, grains, renderers, returners,
|
||||
states, etc from the master to the minions.
|
||||
|
||||
The directories are prepended with an underscore:
|
||||
|
||||
1. _modules
|
||||
2. _grains
|
||||
3. _renderers
|
||||
4. _returners
|
||||
5. _states
|
||||
1. :file:`_modules`
|
||||
2. :file:`_grains`
|
||||
3. :file:`_renderers`
|
||||
4. :file:`_returners`
|
||||
5. :file:`_states`
|
||||
|
||||
The contents of these directories need to be synced over to the minions after
|
||||
Python modules have been created in them. There are a number of ways to sync
|
||||
|
@ -21,6 +21,7 @@ Full list of builtin modules
|
||||
apache
|
||||
apt
|
||||
archive
|
||||
at
|
||||
augeas_cfg
|
||||
bluez
|
||||
brew
|
||||
@ -29,13 +30,14 @@ Full list of builtin modules
|
||||
cassandra
|
||||
cluster
|
||||
cmdmod
|
||||
config
|
||||
cp
|
||||
cron
|
||||
data
|
||||
debconfmod
|
||||
debian_service
|
||||
disk
|
||||
django
|
||||
djangomod
|
||||
ebuild
|
||||
event
|
||||
file
|
||||
@ -54,8 +56,10 @@ Full list of builtin modules
|
||||
kmod
|
||||
kvm_hyper
|
||||
launchctl
|
||||
ldap
|
||||
linux_sysctl
|
||||
mdadm
|
||||
mongodb
|
||||
monit
|
||||
moosefs
|
||||
mount
|
||||
@ -67,6 +71,7 @@ Full list of builtin modules
|
||||
openbsdservice
|
||||
osxdesktop
|
||||
pacman
|
||||
pecl
|
||||
pillar
|
||||
pip
|
||||
pkgng
|
||||
@ -86,11 +91,13 @@ Full list of builtin modules
|
||||
selinux
|
||||
service
|
||||
shadow
|
||||
smf
|
||||
solr
|
||||
sqlite3
|
||||
ssh
|
||||
state
|
||||
status
|
||||
supervisord
|
||||
systemd
|
||||
test
|
||||
tomcat
|
||||
@ -106,7 +113,7 @@ Full list of builtin modules
|
||||
win_service
|
||||
win_shadow
|
||||
win_useradd
|
||||
yumpkg5
|
||||
yumpkg
|
||||
zfs
|
||||
yumpkg5
|
||||
zpool
|
||||
zypper
|
||||
|
6
doc/ref/modules/all/salt.modules.at.rst
Normal file
6
doc/ref/modules/all/salt.modules.at.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===============
|
||||
salt.modules.at
|
||||
===============
|
||||
|
||||
.. automodule:: salt.modules.at
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.config.rst
Normal file
6
doc/ref/modules/all/salt.modules.config.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===================
|
||||
salt.modules.config
|
||||
===================
|
||||
|
||||
.. automodule:: salt.modules.config
|
||||
:members:
|
@ -1,6 +0,0 @@
|
||||
===================
|
||||
salt.modules.django
|
||||
===================
|
||||
|
||||
.. automodule:: salt.modules.django
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.djangomod.rst
Normal file
6
doc/ref/modules/all/salt.modules.djangomod.rst
Normal file
@ -0,0 +1,6 @@
|
||||
======================
|
||||
salt.modules.djangomod
|
||||
======================
|
||||
|
||||
.. automodule:: salt.modules.djangomod
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.ldap.rst
Normal file
6
doc/ref/modules/all/salt.modules.ldap.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=================
|
||||
salt.modules.ldap
|
||||
=================
|
||||
|
||||
.. automodule:: salt.modules.ldap
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.mongodb.rst
Normal file
6
doc/ref/modules/all/salt.modules.mongodb.rst
Normal file
@ -0,0 +1,6 @@
|
||||
====================
|
||||
salt.modules.mongodb
|
||||
====================
|
||||
|
||||
.. automodule:: salt.modules.mongodb
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.pecl.rst
Normal file
6
doc/ref/modules/all/salt.modules.pecl.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=================
|
||||
salt.modules.pecl
|
||||
=================
|
||||
|
||||
.. automodule:: salt.modules.pecl
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.smf.rst
Normal file
6
doc/ref/modules/all/salt.modules.smf.rst
Normal file
@ -0,0 +1,6 @@
|
||||
================
|
||||
salt.modules.smf
|
||||
================
|
||||
|
||||
.. automodule:: salt.modules.smf
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.supervisord.rst
Normal file
6
doc/ref/modules/all/salt.modules.supervisord.rst
Normal file
@ -0,0 +1,6 @@
|
||||
========================
|
||||
salt.modules.supervisord
|
||||
========================
|
||||
|
||||
.. automodule:: salt.modules.supervisord
|
||||
:members:
|
@ -1,6 +0,0 @@
|
||||
================
|
||||
salt.modules.zfs
|
||||
================
|
||||
|
||||
.. automodule:: salt.modules.zfs
|
||||
:members:
|
6
doc/ref/modules/all/salt.modules.zpool.rst
Normal file
6
doc/ref/modules/all/salt.modules.zpool.rst
Normal file
@ -0,0 +1,6 @@
|
||||
==================
|
||||
salt.modules.zpool
|
||||
==================
|
||||
|
||||
.. automodule:: salt.modules.zpool
|
||||
:members:
|
@ -13,3 +13,4 @@ Full list of builtin pillars
|
||||
cmd_yaml
|
||||
hiera
|
||||
mongo
|
||||
pillar_ldap
|
||||
|
6
doc/ref/pillar/all/salt.pillar.pillar_ldap.rst
Normal file
6
doc/ref/pillar/all/salt.pillar.pillar_ldap.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=================
|
||||
salt.pillar.pillar_ldap
|
||||
=================
|
||||
|
||||
.. automodule:: salt.pillar.pillar_ldap
|
||||
:members:
|
@ -11,5 +11,5 @@ Salt includes a number of built-in external pillars, listed at
|
||||
You may also wish to look at the standard pillar documentation, at
|
||||
:ref:`pillar-configuration`
|
||||
|
||||
The source for the built-in Salt returners can be found here:
|
||||
The source for the built-in Salt pillars can be found here:
|
||||
:blob:`salt/pillar`
|
||||
|
16
doc/ref/runners/all/index.rst
Normal file
16
doc/ref/runners/all/index.rst
Normal file
@ -0,0 +1,16 @@
|
||||
.. _all-salt.runners:
|
||||
|
||||
=========================
|
||||
Full list of Salt runners
|
||||
=========================
|
||||
|
||||
.. currentmodule:: salt.runners
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
:template: autosummary.rst.tmpl
|
||||
|
||||
jobs
|
||||
launchd
|
||||
manage
|
||||
network
|
6
doc/ref/runners/all/salt.runners.jobs.rst
Normal file
6
doc/ref/runners/all/salt.runners.jobs.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=================
|
||||
salt.runners.jobs
|
||||
=================
|
||||
|
||||
.. automodule:: salt.runners.jobs
|
||||
:members:
|
6
doc/ref/runners/all/salt.runners.launchd.rst
Normal file
6
doc/ref/runners/all/salt.runners.launchd.rst
Normal file
@ -0,0 +1,6 @@
|
||||
====================
|
||||
salt.runners.launchd
|
||||
====================
|
||||
|
||||
.. automodule:: salt.runners.launchd
|
||||
:members:
|
6
doc/ref/runners/all/salt.runners.manage.rst
Normal file
6
doc/ref/runners/all/salt.runners.manage.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===================
|
||||
salt.runners.manage
|
||||
===================
|
||||
|
||||
.. automodule:: salt.runners.manage
|
||||
:members:
|
6
doc/ref/runners/all/salt.runners.network.rst
Normal file
6
doc/ref/runners/all/salt.runners.network.rst
Normal file
@ -0,0 +1,6 @@
|
||||
====================
|
||||
salt.runners.network
|
||||
====================
|
||||
|
||||
.. automodule:: salt.runners.network
|
||||
:members:
|
@ -2,6 +2,8 @@
|
||||
Salt Runners
|
||||
============
|
||||
|
||||
.. seealso:: :ref:`The full list of runners <all-salt.runners>`
|
||||
|
||||
Salt runners are convenience applications executed with the salt-run command.
|
||||
|
||||
A Salt runner can be a simple client call, or a complex application.
|
@ -17,17 +17,21 @@ Full list of builtin states
|
||||
gem
|
||||
git
|
||||
group
|
||||
hg
|
||||
host
|
||||
kmod
|
||||
module
|
||||
mongodb_database
|
||||
mongodb_user
|
||||
mount
|
||||
mysql_database
|
||||
mysql_grants
|
||||
mysql_user
|
||||
network
|
||||
pecl
|
||||
pip
|
||||
pkgng
|
||||
pkg
|
||||
pkgng
|
||||
postgres_database
|
||||
postgres_user
|
||||
rvm
|
||||
@ -35,6 +39,7 @@ Full list of builtin states
|
||||
service
|
||||
ssh_auth
|
||||
ssh_known_hosts
|
||||
supervisord
|
||||
sysctl
|
||||
user
|
||||
virtualenv
|
||||
|
6
doc/ref/states/all/salt.states.hg.rst
Normal file
6
doc/ref/states/all/salt.states.hg.rst
Normal file
@ -0,0 +1,6 @@
|
||||
==============
|
||||
salt.states.hg
|
||||
==============
|
||||
|
||||
.. automodule:: salt.states.hg
|
||||
:members:
|
6
doc/ref/states/all/salt.states.mongodb_database.rst
Normal file
6
doc/ref/states/all/salt.states.mongodb_database.rst
Normal file
@ -0,0 +1,6 @@
|
||||
============================
|
||||
salt.states.mongodb_database
|
||||
============================
|
||||
|
||||
.. automodule:: salt.states.mongodb_database
|
||||
:members:
|
6
doc/ref/states/all/salt.states.mongodb_user.rst
Normal file
6
doc/ref/states/all/salt.states.mongodb_user.rst
Normal file
@ -0,0 +1,6 @@
|
||||
========================
|
||||
salt.states.mongodb_user
|
||||
========================
|
||||
|
||||
.. automodule:: salt.states.mongodb_user
|
||||
:members:
|
6
doc/ref/states/all/salt.states.pecl.rst
Normal file
6
doc/ref/states/all/salt.states.pecl.rst
Normal file
@ -0,0 +1,6 @@
|
||||
================
|
||||
salt.states.pecl
|
||||
================
|
||||
|
||||
.. automodule:: salt.states.pecl
|
||||
:members:
|
6
doc/ref/states/all/salt.states.supervisord.rst
Normal file
6
doc/ref/states/all/salt.states.supervisord.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=======================
|
||||
salt.states.supervisord
|
||||
=======================
|
||||
|
||||
.. automodule:: salt.states.supervisord
|
||||
:members:
|
@ -339,7 +339,7 @@ components.
|
||||
|
||||
<ID Declaration>:
|
||||
<State Declaration>:
|
||||
- <Function>:
|
||||
- <Function>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
|
@ -20,9 +20,15 @@ module detected for Arch Linux, the systemd module can be used:
|
||||
- enable: True
|
||||
- provider: systemd
|
||||
|
||||
In this instance the systemd module will replace the service virtual module
|
||||
which is used by default on Arch Linux, and the httpd service will be set up
|
||||
using systemd.
|
||||
In this instance the :py:mod:`~salt.modules.systemd` module will replace the
|
||||
:py:mod:`~salt.modules.service` basic module which is used by default on Arch
|
||||
Linux, and the :program:`httpd` service will be set up using
|
||||
:program:`systemd`.
|
||||
|
||||
.. note::
|
||||
|
||||
You can also set a provider globally in the minion config
|
||||
:conf_minion:`providers`.
|
||||
|
||||
Arbitrary Module Redirects
|
||||
==========================
|
||||
@ -39,7 +45,8 @@ module can be used to provide certain functionality.
|
||||
- pkg: yumpkg5
|
||||
- cmd: customcmd
|
||||
|
||||
In this example the default pkg module is being redirected to use the *yumpkg5*
|
||||
module (*yum* via shelling out instead of via the yum API), but is also using
|
||||
a custom module to invoke commands. This could be used to dramatically change
|
||||
the behavior of a given state.
|
||||
In this example the default :py:mod:`~salt.modules.pkg` module is being
|
||||
redirected to use the :py:mod:`~salt.modules.yumpkg5` module (:program:`yum`
|
||||
via shelling out instead of via the :program:`yum` Python API), but is also
|
||||
using a custom module to invoke commands. This could be used to dramatically
|
||||
change the behavior of a given state.
|
||||
|
@ -10,10 +10,17 @@ matches systems should draw from.
|
||||
Environments
|
||||
============
|
||||
|
||||
.. glossary::
|
||||
|
||||
Environment
|
||||
A configuration that allows conceptually organizing state tree
|
||||
directories. Environments can be made to be self-contained or state
|
||||
trees can be made to bleed through environments.
|
||||
|
||||
The environments in the top file corresponds with the environments defined in
|
||||
the file_roots variable. In a simple, single environment setup you only have
|
||||
the base environment, and therefore only one state tree. Here is a simple
|
||||
example of file_roots in the master configuration:
|
||||
the :conf_master:`file_roots` variable. In a simple, single environment setup
|
||||
you only have the ``base`` environment, and therefore only one state tree. Here
|
||||
is a simple example of :conf_master:`file_roots` in the master configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
@ -31,9 +38,9 @@ here is a simple, single environment top file:
|
||||
- core
|
||||
- edit
|
||||
|
||||
This also means that /srv/salt has a state tree. But if you want to use
|
||||
This also means that :file:`/srv/salt` has a state tree. But if you want to use
|
||||
multiple environments, or partition the file server to serve more than
|
||||
just the state tree, then the file_roots option can be expanded:
|
||||
just the state tree, then the :conf_master:`file_roots` option can be expanded:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
@ -67,12 +74,12 @@ Then our top file could reference the environments:
|
||||
'db*prod*':
|
||||
- db
|
||||
|
||||
In this setup we have state trees in 3 of the 4 environments, and no state
|
||||
tree in the base environment. Notice that the targets for the minions
|
||||
In this setup we have state trees in three of the four environments, and no
|
||||
state tree in the ``base`` environment. Notice that the targets for the minions
|
||||
specify environment data. In Salt the master determines who is in what
|
||||
environment, and many environments can be crossed together. For instance,
|
||||
a separate global state tree could be added to the base environment if
|
||||
it suits your deployment:
|
||||
environment, and many environments can be crossed together. For instance, a
|
||||
separate global state tree could be added to the ``base`` environment if it
|
||||
suits your deployment:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -24,7 +24,7 @@ The commands sent out via the salt client are broadcast out to the minions via
|
||||
ZeroMQ PUB/SUB. This is done by allowing the minions to maintain a connection
|
||||
back to the Salt Master and then all connecions are informed to download the
|
||||
command data at once. The command data is kept extreamly small (usually less
|
||||
than 1K) so it is bnto a burden on the network.
|
||||
than 1K) so it is not a burden on the network.
|
||||
|
||||
Return
|
||||
======
|
||||
|
13
doc/ref/tops/all/index.rst
Normal file
13
doc/ref/tops/all/index.rst
Normal file
@ -0,0 +1,13 @@
|
||||
.. _all-salt.tops:
|
||||
|
||||
================================
|
||||
Full list of builtin master tops
|
||||
================================
|
||||
|
||||
.. currentmodule:: salt.tops
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
:template: autosummary.rst.tmpl
|
||||
|
||||
ext_nodes
|
6
doc/ref/tops/all/salt.tops.ext_nodes.rst
Normal file
6
doc/ref/tops/all/salt.tops.ext_nodes.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===================
|
||||
salt.tops.ext_nodes
|
||||
===================
|
||||
|
||||
.. automodule:: salt.tops.ext_nodes
|
||||
:members:
|
13
doc/ref/tops/index.rst
Normal file
13
doc/ref/tops/index.rst
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
.. _salt-top:
|
||||
|
||||
===========
|
||||
Master Tops
|
||||
===========
|
||||
|
||||
Salt includes a number of built-in subsystems to generate top file data, they
|
||||
are listed listed at
|
||||
:ref:`all-salt.tops`.
|
||||
|
||||
The source for the built-in Salt master tops can be found here:
|
||||
:blob:`salt/tops`
|
@ -58,7 +58,7 @@ Running Salt
|
||||
There is also a full :doc:`troubleshooting guide</topics/troubleshooting/index>`
|
||||
available.
|
||||
|
||||
Manage Salt public keys
|
||||
Manage Salt Public Keys
|
||||
=======================
|
||||
|
||||
Salt manages authentication with RSA public keys. The keys are managed on the
|
||||
|
51
doc/topics/eauth/access_control.rst
Normal file
51
doc/topics/eauth/access_control.rst
Normal file
@ -0,0 +1,51 @@
|
||||
=====================
|
||||
Access Control System
|
||||
=====================
|
||||
|
||||
.. versionadded:: 0.10.4
|
||||
|
||||
Salt maintains a standard system used to open granular control to non
|
||||
administrative users to execute Salt commands. The access control system
|
||||
has been applied to all systems used to configure access to non administrative
|
||||
control interfaces in Salt.These interfaces include, the ``peer`` system, the
|
||||
``external auth`` system and the ``client acl`` system.
|
||||
|
||||
The access control system mandated a standard configuration syntax used in
|
||||
all of the three aforementioned systems. While this adds functionality to the
|
||||
configuration in 0.10.4, it does not negate the old configuration.
|
||||
|
||||
Now specific functions can be opened up to specific minions from specific users
|
||||
in the case of external auth and client acls, and for specific minions in the
|
||||
case of the peer system.
|
||||
|
||||
The access controls are manifest using matchers in these configurations:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
client_acl:
|
||||
fred:
|
||||
- web\*:
|
||||
- pkg.list_pkgs
|
||||
- test.*
|
||||
- apache.*
|
||||
|
||||
In the above example, fred is able to send commands only to minions which match
|
||||
the specifieed glob target. This can be expanded to include other functions for
|
||||
other minions based on standard targets.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
external_auth:
|
||||
pam:
|
||||
dave:
|
||||
- mongo\*:
|
||||
- network.*
|
||||
- log\*:
|
||||
- network.*
|
||||
- pkg.*
|
||||
- 'G@os:RedHat':
|
||||
- kmod.*
|
||||
- test.ping
|
||||
|
||||
The above allows for all minions to be hit by test.ping by dave, and adds a
|
||||
few functions for hitting other minions.
|
52
doc/topics/eauth/index.rst
Normal file
52
doc/topics/eauth/index.rst
Normal file
@ -0,0 +1,52 @@
|
||||
==============================
|
||||
External Authentication System
|
||||
==============================
|
||||
|
||||
Salt 0.10.4 comes with a fantastic new way to open up running Salt commands
|
||||
to users. This system allows for Salt itself to pass through authentication to
|
||||
any authentication system (The Unix PAM system was the first) to determine
|
||||
if a user has permission to execute a Salt command.
|
||||
|
||||
The external authentication system allows for specific users to be granted
|
||||
access to execute specific functions on specific minions. Access is configured
|
||||
in the master configuration file, and uses the new access control system:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
external_auth:
|
||||
pam:
|
||||
thatch:
|
||||
- 'web*':
|
||||
- test.*
|
||||
- network.*
|
||||
|
||||
So, the above allows the user thatch to execute functions in the test and
|
||||
network modules on the minions that match the web* target.
|
||||
|
||||
The external authentication system can then be used from the command line by
|
||||
any user on the same system as the master with the `-a` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ salt -a pam web\* test.ping
|
||||
|
||||
The system will ask the user for the credentials required buy the
|
||||
authentication system and then publish the command.
|
||||
|
||||
Tokens
|
||||
------
|
||||
|
||||
With external authentication alone the authentication credentials will be
|
||||
required with every call to Salt. This can be alleviated with Salt tokens.
|
||||
|
||||
The tokens are short term authorizations and can be easily created by just
|
||||
adding a capital T option when authenticating:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ salt -T -a pam web\* test.ping
|
||||
|
||||
Now a token will be created that has a expiration of, by default, 12 hours.
|
||||
This token is stored in the active user's home directory and is now sent with
|
||||
all subsequent communications, so the authentication does not need to be
|
||||
declared again until the token expires.
|
@ -1 +0,0 @@
|
||||
../../HACKING.rst
|
171
doc/topics/hacking.rst
Normal file
171
doc/topics/hacking.rst
Normal file
@ -0,0 +1,171 @@
|
||||
Developing Salt
|
||||
===============
|
||||
|
||||
If you want to help develop Salt there is a great need and your patches are
|
||||
welcome!
|
||||
|
||||
To assist in Salt development, you can help in a number of ways.
|
||||
|
||||
Setting a Github pull request
|
||||
-----------------------------
|
||||
|
||||
This is the preferred method for contributions, simply create a Github
|
||||
fork, commit your changes to the fork, and then open up a pull request.
|
||||
|
||||
Posting patches to the mailing list
|
||||
-----------------------------------
|
||||
|
||||
If you have a patch for Salt, please format it via :command:`git format-patch`
|
||||
and send it to the Salt users mailing list. This allows the patch to give you
|
||||
the contributor the credit for your patch, and gives the Salt community an
|
||||
archive of the patch and a place for discussion.
|
||||
|
||||
Contributions Welcome!
|
||||
----------------------
|
||||
|
||||
The goal here is to make contributions clear, make sure there is a trail for
|
||||
where the code has come from, but most importantly, to give credit where credit
|
||||
is due!
|
||||
|
||||
The `Open Comparison Contributing Docs`__ explains the workflow for forking,
|
||||
cloning, branching, committing, and sending a pull request for the git
|
||||
repository.
|
||||
|
||||
``git pull upstream develop`` is a shorter way to update your local repository
|
||||
to the latest version.
|
||||
|
||||
.. __: http://opencomparison.readthedocs.org/en/latest/contributing.html
|
||||
|
||||
Editing and Previewing the Docs
|
||||
-------------------------------
|
||||
You need ``sphinx-build`` to build the docs. In Debian/Ubuntu this is provided
|
||||
in the ``python-sphinx`` package.
|
||||
|
||||
Then::
|
||||
|
||||
cd doc; make html
|
||||
|
||||
- The docs then are built in the ``docs/_build/html/`` folder. If you make
|
||||
changes and want to see the results, ``make html`` again.
|
||||
- The docs use ``reStructuredText`` for markup. See a live demo at
|
||||
http://rst.ninjs.org/
|
||||
- The help information on each module or state is culled from the python code
|
||||
that runs for that piece. Find them in ``salt/modules/`` or ``salt/states/``
|
||||
|
||||
Installing Salt for development
|
||||
-------------------------------
|
||||
|
||||
Clone the repository using::
|
||||
|
||||
git clone https://github.com/saltstack/salt
|
||||
|
||||
Create a new `virtualenv`_::
|
||||
|
||||
virtualenv /path/to/your/virtualenv
|
||||
|
||||
.. note:: site packages
|
||||
|
||||
If you wish to use installed packages rather than have pip download and
|
||||
compile new ones into this environment, add "--system-site-packages".
|
||||
|
||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||
|
||||
Activate the virtualenv::
|
||||
|
||||
source /path/to/your/virtualenv/bin/activate
|
||||
|
||||
Install Salt (and dependencies) into the virtualenv::
|
||||
|
||||
pip install -e ./salt # the path to the salt git clone from above
|
||||
|
||||
.. note:: Installing M2Crypto
|
||||
|
||||
You may need ``swig`` and ``libssl-dev`` to build M2Crypto. If you
|
||||
encounter the error ``command 'swig' failed with exit status 1``
|
||||
while installing M2Crypto, try installing it with the following command::
|
||||
|
||||
env SWIG_FEATURES="-cpperraswarn -includeall -D__`uname -m`__ -I/usr/include/openssl" pip install M2Crypto
|
||||
|
||||
Debian and Ubuntu systems have modified openssl libraries and mandate that
|
||||
a patched version of M2Crypto be installed. This means that M2Crypto
|
||||
needs to be installed via apt:
|
||||
|
||||
apt-get install python-m2crypto
|
||||
|
||||
Running a self-contained development version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
During development it is easiest to be able to run the Salt master and minion
|
||||
that are installed in the virtualenv you created above, and also to have all
|
||||
the configuration, log, and cache files contained in the virtualenv as well.
|
||||
|
||||
Copy the master and minion config files into your virtualenv::
|
||||
|
||||
mkdir -p /path/to/your/virtualenv/etc/salt
|
||||
cp ./salt/conf/master.template /path/to/your/virtualenv/etc/salt/master
|
||||
cp ./salt/conf/minion.template /path/to/your/virtualenv/etc/salt/minion
|
||||
|
||||
Edit the master config file:
|
||||
|
||||
1. Uncomment and change the ``user: root`` value to your own user.
|
||||
2. Uncomment and change the ``root_dir: /`` value to point to
|
||||
``/path/to/your/virtualenv``.
|
||||
3. If you are also running a non-development version of Salt you will have to
|
||||
change the ``publish_port`` and ``ret_port`` values as well.
|
||||
|
||||
Edit the minion config file:
|
||||
|
||||
1. Repeat the edits you made in the master config for the ``user`` and
|
||||
``root_dir`` values as well as any port changes.
|
||||
2. Uncomment and change the ``master: salt`` value to point at ``localhost``.
|
||||
3. Uncomment and change the ``id:`` value to something descriptive like
|
||||
"saltdev". This isn't strictly necessary but it will serve as a reminder of
|
||||
which Salt installation you are working with.
|
||||
|
||||
.. note:: Using `salt-call` with a :doc:`Standalone Minion </topics/tutorials/standalone_minion>`
|
||||
|
||||
If you plan to run `salt-call` with this self-contained development
|
||||
environment in a masterless setup, you should invoke `salt-call` with
|
||||
``-c /path/to/your/virtualenv/etc/salt`` so that salt can find the minion
|
||||
config file. Without the ``-c`` option, Salt finds its config files in `/etc/salt`.
|
||||
|
||||
Start the master and minion, accept the minon's key, and verify your local Salt
|
||||
installation is working::
|
||||
|
||||
salt-master -c ./etc/salt -d
|
||||
salt-minion -c ./etc/salt -d
|
||||
salt-key -c ./etc/salt -L
|
||||
salt-key -c ./etc/salt -A
|
||||
salt -c ./etc/salt '*' test.ping
|
||||
|
||||
File descriptor limit
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Check your file descriptor limit with::
|
||||
|
||||
ulimit -n
|
||||
|
||||
If it is less than 2047, you should increase it with::
|
||||
|
||||
ulimit -n 2047
|
||||
(or "limit descriptors 2047" for c-shell)
|
||||
|
||||
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
You will need ``mock`` to run the tests::
|
||||
|
||||
pip install mock
|
||||
|
||||
If you are on Python < 2.7 then you will also need unittest2::
|
||||
|
||||
pip install unittest2
|
||||
|
||||
Finally you use setup.py to run the tests with the following command::
|
||||
|
||||
./setup.py test
|
||||
|
||||
For greater control while running the tests, please try::
|
||||
|
||||
./tests/runtests.py -h
|
@ -1,3 +1,5 @@
|
||||
.. _installation:
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
@ -2,9 +2,15 @@
|
||||
Solaris
|
||||
=======
|
||||
|
||||
Salt was added to the OpenCSW package repository in September of 2012 by Romeo Theriault <romeot@hawaii.edu> at version 0.10.2 of Salt. It has mainly been tested on Solaris 10 (sparc), though it is built for, and should work fine on Solaris 10 (x86), Solaris 9 (sparc/x86) and 11 (sparc/x86) also. Most of the testing has also just focused on the minion, though it has verified that the master starts up successfully on Solaris 10.
|
||||
Salt was added to the OpenCSW package repository in September of 2012 by Romeo Theriault <romeot@hawaii.edu> at version 0.10.2 of Salt. It has mainly been tested on Solaris 10 (sparc), though it is built for and has been tested minimally on Solaris 10 (x86), Solaris 9 (sparc/x86) and 11 (sparc/x86). (Please let me know if you're using it on these platforms!) Most of the testing has also just focused on the minion, though it has verified that the master starts up successfully on Solaris 10.
|
||||
|
||||
Comments and patches for better support on these platforms is very welcome. Currently at version 0.10.2 of salt, grain detection is weak but patches that very much improve the grain detection will be released in 0.10.3. Work is also underway to include support for services and packages in Solaris.
|
||||
Comments and patches for better support on these platforms is very welcome. Currently at version 0.10.3 of salt, remote execution works good, grain detection is good and service control with SMF is supported. Work is underway to fill in the remaining gaps by adding support for the following:
|
||||
|
||||
1. 'pkg' states with pkgadd and pkgutil modules
|
||||
2. support for solaris cron
|
||||
3. support for user and group management
|
||||
|
||||
We hope to have all of the above included in v0.10.4 of Salt.
|
||||
|
||||
Salt is dependent on the following additional packages. These will automatically be installed as
|
||||
dependencies of the ``py_salt`` package. ::
|
||||
@ -52,7 +58,7 @@ Ok, time to install salt.
|
||||
root> /opt/csw/bin/pkgutil -i -y py_salt
|
||||
|
||||
Minion Configuration
|
||||
=============
|
||||
====================
|
||||
|
||||
Now that salt is installed you can find it's configuration files in:
|
||||
|
||||
@ -99,7 +105,7 @@ Run a simple test against the minion:
|
||||
salt '<your-salt-minion>' test.ping
|
||||
|
||||
Troubleshooting
|
||||
=============
|
||||
===============
|
||||
|
||||
Logs are in ``/var/log/salt``
|
||||
|
||||
|
@ -32,8 +32,7 @@ Install on Windows XP 32bit
|
||||
|
||||
git clone git://github.com/saltstack/salt.git
|
||||
|
||||
2. Install `Microsoft Visual Studio 2008 Express`_ with the web installer.
|
||||
Or `download a full iso with the installer`_ .
|
||||
2. Install `Microsoft Visual Studio 2008 Express`_.
|
||||
You must use Visual Studio 2008 Express, **not** Visual Studio 2010 Express.
|
||||
|
||||
3. Install `Python 2.7.x`_
|
||||
@ -130,8 +129,7 @@ Install on Windows XP 32bit
|
||||
|
||||
|
||||
.. _msysgit: http://code.google.com/p/msysgit/downloads/list?can=3
|
||||
.. _Microsoft Visual Studio 2008 Express: http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express
|
||||
.. _download a full iso with the installer: http://www.microsoft.com/download/en/details.aspx?id=20682
|
||||
.. _Microsoft Visual Studio 2008 Express: http://www.microsoft.com/en-gb/download/details.aspx?id=20682
|
||||
.. _Python 2.7.x: http://www.python.org
|
||||
.. _vcredist_x86: http://www.microsoft.com/download/en/details.aspx?id=5582
|
||||
.. _Win32OpenSSL-1_0_0e.exe: http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
|
20
doc/topics/master_tops/index.rst
Normal file
20
doc/topics/master_tops/index.rst
Normal file
@ -0,0 +1,20 @@
|
||||
==================
|
||||
Master Tops System
|
||||
==================
|
||||
|
||||
In 0.10.4 the `external_nodes` system was upgraded to allow for modular
|
||||
subsystems to be used to generate the top file data for a highstate run on
|
||||
the master.
|
||||
|
||||
The old `external_nodes` option still works, but will be removed in the
|
||||
future in favor of the new `master_tops` option which uses the modular
|
||||
system instead. The master tops system contains a number of subsystems that
|
||||
are loaded via the Salt loader interfaces like modules, states, returners,
|
||||
runners, etc.
|
||||
|
||||
Using the new `master_tops` option is simple:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
master_tops:
|
||||
ext_nodes: cobbler-external-nodes
|
@ -49,7 +49,7 @@ The ability to generate fingerprints of keys used by Salt has been added to
|
||||
``salt-key``. The new option `finger` accepts the name of the key to generate
|
||||
and display a fingerprint for.
|
||||
|
||||
.. code-block:: base
|
||||
.. code-block:: bash
|
||||
|
||||
salt-key -F master
|
||||
|
||||
|
40
doc/topics/releases/0.10.4.rst
Normal file
40
doc/topics/releases/0.10.4.rst
Normal file
@ -0,0 +1,40 @@
|
||||
=========================
|
||||
Salt 0.10.4 Release Notes
|
||||
=========================
|
||||
|
||||
The latest taste of Salt has come, this release has many fixes and feature
|
||||
additions. Modifications have been made to make ZeroMQ connections more
|
||||
reliable, the begining of the ACL system is in place, a new command line
|
||||
parsing system has been added, dynamic module distribution has become more
|
||||
environment aware, the new `master_finger` option and many more!
|
||||
|
||||
Major Features
|
||||
==============
|
||||
|
||||
External Authentication System
|
||||
------------------------------
|
||||
|
||||
Access Control System
|
||||
---------------------
|
||||
|
||||
Master Tops System
|
||||
------------------
|
||||
|
||||
Next Level Solaris Support
|
||||
--------------------------
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
A vulnerability in the handshake was found and has been repaired, old minions
|
||||
should be able to connect to a new master, so as usual the master should be
|
||||
updated first and then the minions.
|
||||
|
||||
Pillar Updates
|
||||
--------------
|
||||
|
||||
The pillar communication has been updated to add some extra levels of
|
||||
verification that the intended minion is the only one allowed to gather the
|
||||
data. Once all minions and the master are updated to salt 0.10.4 please
|
||||
activate pillar 2 by changing the `pillar_version` in the master config to
|
||||
`2`. This will be set to `2` by default in a future release.
|
@ -29,7 +29,7 @@ Here s the md5sum:
|
||||
|
||||
7d5aca4633bc22f59045f59e82f43b56
|
||||
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
For instructions on how to set up Salt please see the :ref:`installation`
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
|
@ -24,7 +24,7 @@ Here is the md5sum:
|
||||
|
||||
9a925da04981e65a0f237f2e77ddab37
|
||||
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
For instructions on how to set up Salt please see the :ref:`installation`
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
|
@ -23,7 +23,7 @@ Or from PiPy:
|
||||
|
||||
http://pypi.python.org/packages/source/s/salt/salt-0.9.2.tar.gz
|
||||
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
For instructions on how to set up Salt please see the :ref:`installation`
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
|
@ -22,7 +22,7 @@ Or from PiPy:
|
||||
|
||||
http://pypi.python.org/packages/source/s/salt/salt-0.9.3.tar.gz
|
||||
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
For instructions on how to set up Salt please see the :ref:`installation`
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
|
@ -24,7 +24,7 @@ Or from PiPy:
|
||||
|
||||
http://pypi.python.org/packages/source/s/salt/salt-0.9.4.tar.gz
|
||||
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
For instructions on how to set up Salt please see the :ref:`installation`
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
|
@ -21,6 +21,7 @@ E PCRE Minion id match ``E@web\d+\.(dev|qa|prod)\.loc``
|
||||
P Grains PCRE match ``P@os:(RedHat|Fedora|CentOS)``
|
||||
L List of minions ``L@minion1.example.com,minion3.domain.com and bl*.domain.com``
|
||||
I Pillar glob match ``I@pdata:foobar``
|
||||
S Subnet/IP addr match ``S@192.168.1.0/24`` or ``S@192.168.1.100``
|
||||
====== ==================== ===============================================================
|
||||
|
||||
Matchers can be joined using boolean ``and``, ``or``, and ``not`` operators.
|
||||
|
@ -6,6 +6,12 @@ Salt comes with an interface to derive information about the underlying system.
|
||||
This is called the grains interface, because it presents salt with grains of
|
||||
information.
|
||||
|
||||
.. glossary::
|
||||
|
||||
Grains
|
||||
Static bits of information that a minion collects about the system when
|
||||
the minion first starts.
|
||||
|
||||
The grains interface is made available to Salt modules and components so that
|
||||
the right salt minion commands are automatically available on the right
|
||||
systems.
|
||||
|
@ -11,7 +11,7 @@ Node groups
|
||||
Nodegroups are declared using a compound target specification. The compount
|
||||
target documentation can be found here:
|
||||
|
||||
:doc:`Compound Matchers <topics/targeting/compound>`
|
||||
:doc:`Compound Matchers <compound>`
|
||||
|
||||
For example, in the master config file :conf_master:`nodegroups` setting::
|
||||
|
||||
|
@ -23,12 +23,13 @@ Here is a sample script::
|
||||
# Install saltstack
|
||||
add-apt-repository ppa:saltstack/salt -y
|
||||
apt-get update -y
|
||||
apt-get install salt -y
|
||||
apt-get install salt-minion -y
|
||||
apt-get install salt-master -y
|
||||
apt-get upgrade -y
|
||||
|
||||
# Set salt master location and start minion
|
||||
cp /etc/salt/minion.template /etc/salt/minion
|
||||
sed -i '' -e 's/#master: salt/master: [salt_master_fqdn]' /etc/salt/minion
|
||||
sed -i 's/#master: salt/master: [salt_master_fqdn]/' /etc/salt/minion
|
||||
salt-minion -d
|
||||
|
||||
First the script adds the saltstack ppa and installs the package. Then
|
||||
|
@ -57,7 +57,7 @@ Gotchas
|
||||
|
||||
My Windows minion isn't responding
|
||||
----------------------------------
|
||||
The process dispatch on Windows is slower than it is on *nix. You may need to
|
||||
The process dispatch on Windows is slower than it is on \*nix. You may need to
|
||||
add '-t 15' to your salt calls to give them plenty of time to return.
|
||||
|
||||
Windows and the Visual Studio Redist
|
@ -54,10 +54,10 @@ Follow these instructions: http://wiki.debian.org/iptables
|
||||
Once you've found your firewall rules, you'll need to add the two lines below
|
||||
to allow traffic on ``tcp/4505`` and ``tcp/4506``:
|
||||
|
||||
.. code-block:: diff
|
||||
::
|
||||
|
||||
+ -A INPUT -m state --state new -m tcp -p tcp --dport 4505 -j ACCEPT
|
||||
+ -A INPUT -m state --state new -m tcp -p tcp --dport 4506 -j ACCEPT
|
||||
-A INPUT -m state --state new -m tcp -p tcp --dport 4505 -j ACCEPT
|
||||
-A INPUT -m state --state new -m tcp -p tcp --dport 4506 -j ACCEPT
|
||||
|
||||
**Ubuntu**
|
||||
|
||||
@ -77,10 +77,10 @@ The BSD-family of operating systems uses `packet filter (pf)`_. The following
|
||||
example describes the additions to ``pf.conf`` needed to access the Salt
|
||||
master.
|
||||
|
||||
.. code-block:: diff
|
||||
::
|
||||
|
||||
+ pass in on $int_if proto tcp from any to $int_if port 4505
|
||||
+ pass in on $int_if proto tcp from any to $int_if port 4506
|
||||
pass in on $int_if proto tcp from any to $int_if port 4505
|
||||
pass in on $int_if proto tcp from any to $int_if port 4506
|
||||
|
||||
Once these additions have been made to the ``pf.conf`` the rules will need to
|
||||
be reloaded. This can be done using the ``pfctl`` command.
|
||||
|
@ -65,3 +65,9 @@ arguments
|
||||
Space-delimited arguments to the function::
|
||||
|
||||
salt '*' cmd.exec_code python 'import sys; print sys.version'
|
||||
|
||||
Optional, keyword arguments are also supported::
|
||||
|
||||
salt '*' pip.install salt timeout=5 upgrade=True
|
||||
|
||||
They are always in the form of ``kwarg=argument``.
|
||||
|
@ -21,7 +21,7 @@ value in the master config.
|
||||
.. _`Jinja2`: http://jinja.pocoo.org/
|
||||
|
||||
All states are passed through a templating system when they are initially read.
|
||||
To make use of the templating system, simple add some templating markup.
|
||||
To make use of the templating system, simply add some templating markup.
|
||||
An example of an sls module with templating markup may look like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
@ -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.10.2
|
||||
Release: 2%{?dist}
|
||||
Version: 0.10.3
|
||||
Release: 1%{?dist}
|
||||
Summary: A parallel remote execution system
|
||||
|
||||
Group: System Environment/Daemons
|
||||
@ -24,7 +24,6 @@ Source4: %{name}-master.service
|
||||
Source5: %{name}-syndic.service
|
||||
Source6: %{name}-minion.service
|
||||
Source7: README.fedora
|
||||
Patch0: 0001-Only-expect-args-if-they-are-actually-passed-in.patch
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
BuildArch: noarch
|
||||
@ -74,6 +73,11 @@ Requires(preun): initscripts
|
||||
Requires(postun): initscripts
|
||||
|
||||
%else
|
||||
%if 0%{?systemd_preun:1}
|
||||
Requires(post): systemd-units
|
||||
Requires(preun): systemd-units
|
||||
Requires(postun): systemd-units
|
||||
%endif
|
||||
|
||||
BuildRequires: systemd-units
|
||||
|
||||
@ -107,7 +111,6 @@ Salt minion is queried and controlled from the master.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%patch0 -p1
|
||||
|
||||
%build
|
||||
|
||||
@ -187,80 +190,109 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%config(noreplace) %{_sysconfdir}/salt/master
|
||||
%config %{_sysconfdir}/salt/master.template
|
||||
|
||||
# less than RHEL 8 / Fedora 16
|
||||
# not sure if RHEL 7 will use systemd yet
|
||||
%if ! (0%{?rhel} >= 7 || 0%{?fedora} >= 15)
|
||||
|
||||
%preun -n salt-master
|
||||
if [ $1 -eq 0 ] ; then
|
||||
/sbin/service salt-master stop >/dev/null 2>&1
|
||||
/sbin/service salt-syndic stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del salt-master
|
||||
/sbin/chkconfig --del salt-syndic
|
||||
fi
|
||||
if [ $1 -eq 0 ] ; then
|
||||
/sbin/service salt-master stop >/dev/null 2>&1
|
||||
/sbin/service salt-syndic stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del salt-master
|
||||
/sbin/chkconfig --del salt-syndic
|
||||
fi
|
||||
|
||||
%preun -n salt-minion
|
||||
if [ $1 -eq 0 ] ; then
|
||||
/sbin/service salt-minion stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del salt-minion
|
||||
fi
|
||||
if [ $1 -eq 0 ] ; then
|
||||
/sbin/service salt-minion stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del salt-minion
|
||||
fi
|
||||
|
||||
%post -n salt-master
|
||||
/sbin/chkconfig --add salt-master
|
||||
/sbin/chkconfig --add salt-syndic
|
||||
/sbin/chkconfig --add salt-master
|
||||
/sbin/chkconfig --add salt-syndic
|
||||
|
||||
%post -n salt-minion
|
||||
/sbin/chkconfig --add salt-minion
|
||||
/sbin/chkconfig --add salt-minion
|
||||
|
||||
%postun -n salt-master
|
||||
if [ "$1" -ge "1" ] ; then
|
||||
/sbin/service salt-master condrestart >/dev/null 2>&1 || :
|
||||
/sbin/service salt-syndic condrestart >/dev/null 2>&1 || :
|
||||
fi
|
||||
if [ "$1" -ge "1" ] ; then
|
||||
/sbin/service salt-master condrestart >/dev/null 2>&1 || :
|
||||
/sbin/service salt-syndic condrestart >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
%postun -n salt-minion
|
||||
if [ "$1" -ge "1" ] ; then
|
||||
/sbin/service salt-master condrestart >/dev/null 2>&1 || :
|
||||
/sbin/service salt-syndic condrestart >/dev/null 2>&1 || :
|
||||
fi
|
||||
if [ "$1" -ge "1" ] ; then
|
||||
/sbin/service salt-master condrestart >/dev/null 2>&1 || :
|
||||
/sbin/service salt-syndic condrestart >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
%else
|
||||
|
||||
%preun -n salt-master
|
||||
if [ $1 -eq 0 ] ; then
|
||||
%if 0%{?systemd_preun:1}
|
||||
%systemd_preun salt-master.service
|
||||
%else
|
||||
if [ $1 -eq 0 ] ; then
|
||||
# Package removal, not upgrade
|
||||
/bin/systemctl --no-reload disable salt-master.service > /dev/null 2>&1 || :
|
||||
/bin/systemctl stop salt-master.service > /dev/null 2>&1 || :
|
||||
|
||||
/bin/systemctl --no-reload disable salt-syndic.service > /dev/null 2>&1 || :
|
||||
/bin/systemctl stop salt-syndic.service > /dev/null 2>&1 || :
|
||||
fi
|
||||
fi
|
||||
%endif
|
||||
|
||||
%preun -n salt-minion
|
||||
if [ $1 -eq 0 ] ; then
|
||||
# Package removal, not upgrade
|
||||
/bin/systemctl --no-reload disable salt-master.service > /dev/null 2>&1 || :
|
||||
/bin/systemctl stop salt-master.service > /dev/null 2>&1 || :
|
||||
|
||||
fi
|
||||
%if 0%{?systemd_preun:1}
|
||||
%systemd_preun salt-minion.service
|
||||
%else
|
||||
if [ $1 -eq 0 ] ; then
|
||||
# Package removal, not upgrade
|
||||
/bin/systemctl --no-reload disable salt-master.service > /dev/null 2>&1 || :
|
||||
/bin/systemctl stop salt-master.service > /dev/null 2>&1 || :
|
||||
fi
|
||||
%endif
|
||||
|
||||
%post -n salt-master
|
||||
/bin/systemctl daemon-reload &>/dev/null || :
|
||||
%if 0%{?systemd_post:1}
|
||||
%systemd_post salt-master.service
|
||||
%else
|
||||
/bin/systemctl daemon-reload &>/dev/null || :
|
||||
%endif
|
||||
|
||||
%post -n salt-minion
|
||||
/bin/systemctl daemon-reload &>/dev/null || :
|
||||
%if 0%{?systemd_post:1}
|
||||
%systemd_post salt-minion.service
|
||||
%else
|
||||
/bin/systemctl daemon-reload &>/dev/null || :
|
||||
%endif
|
||||
|
||||
%postun -n salt-master
|
||||
/bin/systemctl daemon-reload &>/dev/null
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-master.service &>/dev/null || :
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-syndic.service &>/dev/null || :
|
||||
%if 0%{?systemd_post:1}
|
||||
%systemd_postun salt-master.service
|
||||
%else
|
||||
/bin/systemctl daemon-reload &>/dev/null
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-master.service &>/dev/null || :
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-syndic.service &>/dev/null || :
|
||||
%endif
|
||||
|
||||
%postun -n salt-minion
|
||||
/bin/systemctl daemon-reload &>/dev/null
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-master.service &>/dev/null || :
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-syndic.service &>/dev/null || :
|
||||
%if 0%{?systemd_post:1}
|
||||
%systemd_postun salt-minion.service
|
||||
%else
|
||||
/bin/systemctl daemon-reload &>/dev/null
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-master.service &>/dev/null || :
|
||||
[ $1 -gt 0 ] && /bin/systemctl try-restart salt-syndic.service &>/dev/null || :
|
||||
%endif
|
||||
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Oct 2 2012 Clint Savage <herlo1@gmail.com> - 0.10.3-1
|
||||
- Moved to upstream release 0.10.3
|
||||
- Added systemd scriplets (RHBZ#850408)
|
||||
|
||||
* Thu Aug 2 2012 Clint Savage <herlo1@gmail.com> - 0.10.2-2
|
||||
- Fix upstream bug #1730 per RHBZ#845295
|
||||
|
||||
|
@ -13,7 +13,6 @@ import getpass
|
||||
from salt.version import __version__
|
||||
|
||||
try:
|
||||
import salt.config
|
||||
from salt.utils import parsers
|
||||
from salt.utils.verify import check_user, verify_env, verify_socket
|
||||
except ImportError as e:
|
||||
@ -43,6 +42,7 @@ class Master(parsers.MasterOptionParser):
|
||||
os.path.join(self.config['cachedir'], 'jobs'),
|
||||
os.path.dirname(self.config['log_file']),
|
||||
self.config['sock_dir'],
|
||||
self.config['token_dir'],
|
||||
],
|
||||
self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'],
|
||||
@ -99,7 +99,8 @@ class Minion(parsers.MinionOptionParser):
|
||||
sys.exit(err.errno)
|
||||
|
||||
self.setup_logfile_logger()
|
||||
logging.getLogger(__name__).warn(
|
||||
log = logging.getLogger(__name__)
|
||||
log.warn(
|
||||
'Setting up the Salt Minion "{0}"'.format(
|
||||
self.config['id']
|
||||
)
|
||||
@ -146,7 +147,8 @@ class Syndic(parsers.SyndicOptionParser):
|
||||
sys.exit(err.errno)
|
||||
|
||||
self.setup_logfile_logger()
|
||||
logging.getLogger(__name__).warn(
|
||||
log = logging.getLogger(__name__)
|
||||
log.warn(
|
||||
'Setting up the Salt Syndic Minion "{0}"'.format(
|
||||
self.config['id']
|
||||
)
|
||||
|
205
salt/auth/__init__.py
Normal file
205
salt/auth/__init__.py
Normal file
@ -0,0 +1,205 @@
|
||||
'''
|
||||
Salt's pluggable authentication system
|
||||
|
||||
This sysetm allows for authentication to be managed in a module pluggable way
|
||||
so that any external authentication system can be used inside of Salt
|
||||
'''
|
||||
|
||||
# 1. Create auth loader instance
|
||||
# 2. Accept arguments as a dict
|
||||
# 3. Verify with function introspection
|
||||
# 4. Execute auth function
|
||||
# 5. Cache auth token with relative data opts['token_dir']
|
||||
# 6. Interface to verify tokens
|
||||
|
||||
# Import Python libs
|
||||
import os
|
||||
import hashlib
|
||||
import time
|
||||
import logging
|
||||
import random
|
||||
import getpass
|
||||
|
||||
# Import Salt libs
|
||||
import salt.loader
|
||||
import salt.utils
|
||||
import salt.payload
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoadAuth(object):
|
||||
'''
|
||||
Wrap the authentication system to handle periphrial components
|
||||
'''
|
||||
def __init__(self, opts):
|
||||
self.opts = opts
|
||||
self.max_fail = 1.0
|
||||
self.serial = salt.payload.Serial(opts)
|
||||
self.auth = salt.loader.auth(opts)
|
||||
|
||||
def load_name(self, load):
|
||||
'''
|
||||
Return the primary name associate with the load, if an empty string
|
||||
is returned then the load does not match the function
|
||||
'''
|
||||
if not 'eauth' in load:
|
||||
return ''
|
||||
fstr = '{0}.auth'.format(load['eauth'])
|
||||
if not fstr in self.auth:
|
||||
return ''
|
||||
fcall = salt.utils.format_call(self.auth[fstr], load)
|
||||
try:
|
||||
return fcall['args'][0]
|
||||
except IndexError:
|
||||
return ''
|
||||
|
||||
def __auth_call(self, load):
|
||||
'''
|
||||
Return the token and set the cache data for use
|
||||
|
||||
Do not call this directly! Use the time_auth method to overcome timing
|
||||
attacks
|
||||
'''
|
||||
if not 'eauth' in load:
|
||||
return False
|
||||
fstr = '{0}.auth'.format(load['eauth'])
|
||||
if not fstr in self.auth:
|
||||
return False
|
||||
fcall = salt.utils.format_call(self.auth[fstr], load)
|
||||
try:
|
||||
if 'kwargs' in fcall:
|
||||
return self.auth[fstr](*fcall['args'], **fcall['kwargs'])
|
||||
else:
|
||||
return self.auth[fstr](*fcall['args'])
|
||||
except Exception as exc:
|
||||
err = 'Authentication module threw an exception: {0}'.format(exc)
|
||||
log.critical(err)
|
||||
return False
|
||||
return False
|
||||
|
||||
def time_auth(self, load):
|
||||
'''
|
||||
Make sure that all failures happen in the same amount of time
|
||||
'''
|
||||
start = time.time()
|
||||
ret = self.__auth_call(load)
|
||||
if ret:
|
||||
return ret
|
||||
f_time = time.time() - start
|
||||
if f_time > self.max_fail:
|
||||
self.max_fail = f_time
|
||||
deviation = self.max_fail / 4
|
||||
r_time = random.uniform(
|
||||
self.max_fail - deviation,
|
||||
self.max_fail + deviation
|
||||
)
|
||||
while start + r_time > time.time():
|
||||
time.sleep(0.001)
|
||||
return False
|
||||
|
||||
def mk_token(self, load):
|
||||
'''
|
||||
Run time_auth and create a token. Return False or the token
|
||||
'''
|
||||
ret = self.time_auth(load)
|
||||
if ret is False:
|
||||
return ret
|
||||
fstr = '{0}.auth'.format(load['eauth'])
|
||||
tok = str(hashlib.md5(os.urandom(512)).hexdigest())
|
||||
t_path = os.path.join(self.opts['token_dir'], tok)
|
||||
while os.path.isfile(t_path):
|
||||
tok = hashlib.md5(os.urandom(512)).hexdigest()
|
||||
t_path = os.path.join(self.opts['token_dir'], tok)
|
||||
fcall = salt.utils.format_call(self.auth[fstr], load)
|
||||
tdata = {'start': time.time(),
|
||||
'expire': time.time() + self.opts['token_expire'],
|
||||
'name': fcall['args'][0],
|
||||
'eauth': load['eauth'],
|
||||
'token': tok}
|
||||
with open(t_path, 'w+') as fp_:
|
||||
fp_.write(self.serial.dumps(tdata))
|
||||
return tdata
|
||||
|
||||
def get_tok(self, tok):
|
||||
'''
|
||||
Return the name associate with the token, or False if the token is
|
||||
not valid
|
||||
'''
|
||||
t_path = os.path.join(self.opts['token_dir'], tok)
|
||||
if not os.path.isfile:
|
||||
return {}
|
||||
with open(t_path, 'r') as fp_:
|
||||
tdata = self.serial.loads(fp_.read())
|
||||
rm_tok = False
|
||||
if not 'expire' in tdata:
|
||||
# invalid token, delete it!
|
||||
rm_tok = True
|
||||
if tdata.get('expire', '0') < time.time():
|
||||
rm_tok = True
|
||||
if rm_tok:
|
||||
try:
|
||||
os.remove(t_path)
|
||||
return {}
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
return tdata
|
||||
|
||||
|
||||
class Resolver(object):
|
||||
'''
|
||||
The class used to resolve options for the command line and for genric
|
||||
interactive interfaces
|
||||
'''
|
||||
def __init__(self, opts):
|
||||
self.opts = opts
|
||||
self.auth = salt.loader.auth(opts)
|
||||
|
||||
def cli(self, eauth):
|
||||
'''
|
||||
Execute the cli options to fill in the extra data needed for the
|
||||
defined eauth system
|
||||
'''
|
||||
ret = {}
|
||||
if not eauth:
|
||||
print 'External authentication system has not been specified'
|
||||
return ret
|
||||
fstr = '{0}.auth'.format(eauth)
|
||||
if not fstr in self.auth:
|
||||
print ('The specified external authentication system "{0}" is '
|
||||
'not available').format(eauth)
|
||||
return ret
|
||||
|
||||
args = salt.utils.arg_lookup(self.auth[fstr])
|
||||
for arg in args['args']:
|
||||
if arg in self.opts:
|
||||
ret[arg] = self.opts[arg]
|
||||
elif arg.startswith('pass'):
|
||||
ret[arg] = getpass.getpass('{0}: '.format(arg))
|
||||
else:
|
||||
ret[arg] = raw_input('{0}: '.format(arg))
|
||||
for kwarg, default in args['kwargs'].items():
|
||||
if kwarg in self.opts:
|
||||
ret['kwarg'] = self.opts[kwarg]
|
||||
else:
|
||||
ret[kwarg] = raw_input('{0} [{1}]: '.format(kwarg, default))
|
||||
|
||||
return ret
|
||||
|
||||
def token_cli(self, eauth, load):
|
||||
'''
|
||||
Create the token from the cli and request the correct data to
|
||||
authenticate via the passed authentication mechanism
|
||||
'''
|
||||
load['cmd'] = 'mk_token'
|
||||
load['eauth'] = eauth
|
||||
sreq = salt.payload.SREQ(
|
||||
'tcp://{0[interface]}:{0[ret_port]}'.format(self.opts),
|
||||
)
|
||||
tdata = sreq.send('clear', load)
|
||||
try:
|
||||
with open(self.opts['token_file'], 'w+') as fp_:
|
||||
fp_.write(tdata['token'])
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
return tdata
|
146
salt/auth/pam.py
Normal file
146
salt/auth/pam.py
Normal file
@ -0,0 +1,146 @@
|
||||
# The pam components have been modified to be salty and have been taken from
|
||||
# the pam module under this licence:
|
||||
# (c) 2007 Chris AtLee <chris@atlee.ca>
|
||||
# Licensed under the MIT license:
|
||||
# http://www.opensource.org/licenses/mit-license.php
|
||||
'''
|
||||
PAM module for python
|
||||
|
||||
Provides an authenticate function that will allow the caller to authenticate
|
||||
a user against the Pluggable Authentication Modules (PAM) on the system.
|
||||
|
||||
Implemented using ctypes, so no compilation is necessary.
|
||||
'''
|
||||
# Import python libs
|
||||
from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
|
||||
from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
|
||||
from ctypes.util import find_library
|
||||
|
||||
LIBPAM = CDLL(find_library('pam'))
|
||||
LIBC = CDLL(find_library('c'))
|
||||
|
||||
CALLOC = LIBC.calloc
|
||||
CALLOC.restype = c_void_p
|
||||
CALLOC.argtypes = [c_uint, c_uint]
|
||||
|
||||
STRDUP = LIBC.strdup
|
||||
STRDUP.argstypes = [c_char_p]
|
||||
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
|
||||
|
||||
# Various constants
|
||||
PAM_PROMPT_ECHO_OFF = 1
|
||||
PAM_PROMPT_ECHO_ON = 2
|
||||
PAM_ERROR_MSG = 3
|
||||
PAM_TEXT_INFO = 4
|
||||
|
||||
|
||||
class PamHandle(Structure):
|
||||
'''
|
||||
Wrapper class for pam_handle_t
|
||||
'''
|
||||
_fields_ = [
|
||||
('handle', c_void_p)
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
Structure.__init__(self)
|
||||
self.handle = 0
|
||||
|
||||
|
||||
class PamMessage(Structure):
|
||||
'''
|
||||
Wrapper class for pam_message structure
|
||||
'''
|
||||
_fields_ = [
|
||||
("msg_style", c_int),
|
||||
("msg", c_char_p),
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
|
||||
|
||||
|
||||
class PamResponse(Structure):
|
||||
'''
|
||||
Wrapper class for pam_response structure
|
||||
'''
|
||||
_fields_ = [
|
||||
('resp', c_char_p),
|
||||
('resp_retcode', c_int),
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
|
||||
|
||||
|
||||
CONV_FUNC = CFUNCTYPE(c_int,
|
||||
c_int, POINTER(POINTER(PamMessage)),
|
||||
POINTER(POINTER(PamResponse)), c_void_p)
|
||||
|
||||
|
||||
class PamConv(Structure):
|
||||
'''
|
||||
Wrapper class for pam_conv structure
|
||||
'''
|
||||
_fields_ = [
|
||||
('conv', CONV_FUNC),
|
||||
('appdata_ptr', c_void_p)
|
||||
]
|
||||
|
||||
|
||||
PAM_START = LIBPAM.pam_start
|
||||
PAM_START.restype = c_int
|
||||
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
|
||||
POINTER(PamHandle)]
|
||||
|
||||
PAM_AUTHENTICATE = LIBPAM.pam_authenticate
|
||||
PAM_AUTHENTICATE.restype = c_int
|
||||
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
|
||||
|
||||
|
||||
def authenticate(username, password, service='login'):
|
||||
'''
|
||||
Returns True if the given username and password authenticate for the
|
||||
given service. Returns False otherwise
|
||||
|
||||
``username``: the username to authenticate
|
||||
|
||||
``password``: the password in plain text
|
||||
|
||||
``service``: the PAM service to authenticate against.
|
||||
Defaults to 'login'
|
||||
'''
|
||||
@CONV_FUNC
|
||||
def my_conv(n_messages, messages, p_response, app_data):
|
||||
'''
|
||||
Simple conversation function that responds to any
|
||||
prompt where the echo is off with the supplied password
|
||||
'''
|
||||
# Create an array of n_messages response objects
|
||||
addr = CALLOC(n_messages, sizeof(PamResponse))
|
||||
p_response[0] = cast(addr, POINTER(PamResponse))
|
||||
for i in range(n_messages):
|
||||
if messages[i].contents.msg_style == PAM_PROMPT_ECHO_OFF:
|
||||
pw_copy = STRDUP(str(password))
|
||||
p_response.contents[i].resp = cast(pw_copy, c_char_p)
|
||||
p_response.contents[i].resp_retcode = 0
|
||||
return 0
|
||||
|
||||
handle = PamHandle()
|
||||
conv = PamConv(my_conv, 0)
|
||||
retval = PAM_START(service, username, pointer(conv), pointer(handle))
|
||||
|
||||
if retval != 0:
|
||||
# TODO: This is not an authentication error, something
|
||||
# has gone wrong starting up PAM
|
||||
return False
|
||||
|
||||
retval = PAM_AUTHENTICATE(handle, 0)
|
||||
return retval == 0
|
||||
|
||||
|
||||
def auth(username, password, **kwargs):
|
||||
'''
|
||||
Authenticate via pam
|
||||
'''
|
||||
return authenticate(username, password, kwargs.get('service', 'login'))
|
@ -4,6 +4,7 @@ The management of salt command line utilities are stored in here
|
||||
|
||||
# Import python libs
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Import salt components
|
||||
import salt.cli.caller
|
||||
@ -13,6 +14,7 @@ import salt.cli.batch
|
||||
import salt.client
|
||||
import salt.output
|
||||
import salt.runner
|
||||
import salt.auth
|
||||
|
||||
from salt.utils import parsers
|
||||
from salt.utils.verify import verify_env
|
||||
@ -50,42 +52,57 @@ class SaltCMD(parsers.SaltCMDOptionParser):
|
||||
if self.options.timeout <= 0:
|
||||
self.options.timeout = local.opts['timeout']
|
||||
|
||||
args = [
|
||||
self.config['tgt'],
|
||||
self.config['fun'],
|
||||
self.config['arg'],
|
||||
self.options.timeout,
|
||||
]
|
||||
kwargs = {
|
||||
'tgt': self.config['tgt'],
|
||||
'fun': self.config['fun'],
|
||||
'arg': self.config['arg'],
|
||||
'timeout': self.options.timeout}
|
||||
|
||||
if 'token' in self.config:
|
||||
kwargs['token'] = self.config['token']
|
||||
|
||||
if self.selected_target_option:
|
||||
args.append(self.selected_target_option)
|
||||
kwargs['expr_form'] = self.selected_target_option
|
||||
else:
|
||||
args.append('glob')
|
||||
kwargs['expr_form'] = 'glob'
|
||||
|
||||
if getattr(self.options, 'return'):
|
||||
args.append(getattr(self.options, 'return'))
|
||||
else:
|
||||
args.append('')
|
||||
kwargs['ret'] = getattr(self.options, 'return')
|
||||
|
||||
print self.options.eauth
|
||||
if self.options.eauth:
|
||||
resolver = salt.auth.Resolver(self.config)
|
||||
res = resolver.cli(self.options.eauth)
|
||||
if self.options.mktoken:
|
||||
kwargs['token'] = resolver.token_cli(
|
||||
self.options.eauth,
|
||||
res
|
||||
)['token']
|
||||
if not res:
|
||||
sys.exit(2)
|
||||
kwargs.update(res)
|
||||
kwargs['eauth'] = self.options.eauth
|
||||
|
||||
try:
|
||||
# local will be None when there was an error
|
||||
if local:
|
||||
if self.options.static:
|
||||
if self.options.verbose:
|
||||
args.append(True)
|
||||
full_ret = local.cmd_full_return(*args)
|
||||
kwargs['verbose'] = True
|
||||
full_ret = local.cmd_full_return(**kwargs)
|
||||
ret, out = self._format_ret(full_ret)
|
||||
self._output_ret(ret, out)
|
||||
elif self.config['fun'] == 'sys.doc':
|
||||
ret = {}
|
||||
out = ''
|
||||
for full_ret in local.cmd_cli(*args):
|
||||
for full_ret in local.cmd_cli(**kwargs):
|
||||
ret_, out = self._format_ret(full_ret)
|
||||
ret.update(ret_)
|
||||
self._output_ret(ret, out)
|
||||
else:
|
||||
if self.options.verbose:
|
||||
args.append(True)
|
||||
for full_ret in local.cmd_cli(*args):
|
||||
kwargs['verbose'] = True
|
||||
for full_ret in local.cmd_cli(**kwargs):
|
||||
ret, out = self._format_ret(full_ret)
|
||||
self._output_ret(ret, out)
|
||||
except SaltInvocationError as exc:
|
||||
|
@ -7,6 +7,7 @@ import os
|
||||
import shutil
|
||||
import sys
|
||||
import logging
|
||||
import glob
|
||||
# Import salt modules
|
||||
import salt.crypt
|
||||
import salt.utils
|
||||
@ -278,9 +279,10 @@ class Key(object):
|
||||
|
||||
def _delete_key(self, delete=None):
|
||||
'''
|
||||
Delete a key
|
||||
Delete a key or keys by glob
|
||||
'''
|
||||
# Don't ask for verification if yes is not set
|
||||
del_ = []
|
||||
yes = self.opts.get('yes', True)
|
||||
(minions_accepted,
|
||||
minions_pre,
|
||||
@ -290,48 +292,25 @@ class Key(object):
|
||||
else:
|
||||
# If a key is explicitly passed then don't ask for verification
|
||||
yes = True
|
||||
pre = os.path.join(minions_pre, delete)
|
||||
acc = os.path.join(minions_accepted, delete)
|
||||
rej = os.path.join(minions_rejected, delete)
|
||||
if os.path.exists(pre):
|
||||
del_.extend(glob.glob(os.path.join(minions_pre, delete)))
|
||||
del_.extend(glob.glob(os.path.join(minions_accepted, delete)))
|
||||
del_.extend(glob.glob(os.path.join(minions_rejected, delete)))
|
||||
if del_:
|
||||
rm_ = True
|
||||
if not yes:
|
||||
msg = ('The following pending key is set to be removed: {0}'
|
||||
'\n[n/Y]').format(delete)
|
||||
veri = raw_input(msg)
|
||||
# Default to Yes
|
||||
msg = 'The following keys are going to be deleted:\n'
|
||||
#for key in sorted(del_):
|
||||
for key in sorted(set(del_)):
|
||||
msg += '{0}\n'.format(key)
|
||||
veri = raw_input('{0}[n/Y]'.format(msg))
|
||||
if veri.lower().startswith('n'):
|
||||
rm_ = False
|
||||
if rm_:
|
||||
os.remove(pre)
|
||||
self._log('Removed pending key {0}'.format(delete),
|
||||
level='info')
|
||||
if os.path.exists(acc):
|
||||
rm_ = True
|
||||
if not yes:
|
||||
msg = ('The following accepted key is set to be removed: {0}'
|
||||
'\n[n/Y]').format(delete)
|
||||
veri = raw_input(msg)
|
||||
# Default to Yes
|
||||
if veri.lower().startswith('n'):
|
||||
rm_ = False
|
||||
if rm_:
|
||||
os.remove(acc)
|
||||
self._log('Removed accepted key {0}'.format(delete),
|
||||
level='info')
|
||||
if os.path.exists(rej):
|
||||
rm_ = True
|
||||
if not yes:
|
||||
msg = ('The following rejected key is set to be removed: {0}'
|
||||
'\n[n/Y]').format(delete)
|
||||
veri = raw_input(msg)
|
||||
# Default to Yes
|
||||
if veri.lower().startswith('n'):
|
||||
rm_ = False
|
||||
if rm_:
|
||||
os.remove(rej)
|
||||
self._log('Removed rejected key {0}'.format(delete),
|
||||
level='info')
|
||||
for key in del_:
|
||||
os.remove(key)
|
||||
filepath, filename = os.path.split(key)
|
||||
self._log('Removed pending key {0}'.format(filename),
|
||||
level='info')
|
||||
|
||||
def _delete_all(self):
|
||||
'''
|
||||
|
414
salt/client.py
414
salt/client.py
@ -29,12 +29,10 @@ The data structure needs to be:
|
||||
# This means that the primary client to build is, the LocalClient
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import glob
|
||||
import time
|
||||
import getpass
|
||||
import fnmatch
|
||||
|
||||
# Import salt modules
|
||||
import salt.config
|
||||
@ -42,7 +40,7 @@ import salt.payload
|
||||
import salt.utils
|
||||
import salt.utils.verify
|
||||
import salt.utils.event
|
||||
from salt.exceptions import SaltClientError, SaltInvocationError
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
# Try to import range from https://github.com/ytoolshed/range
|
||||
RANGE = False
|
||||
@ -69,8 +67,8 @@ class LocalClient(object):
|
||||
'''
|
||||
Connect to the salt master via the local server and via root
|
||||
'''
|
||||
def __init__(self, c_path='/etc/salt/master'):
|
||||
self.opts = salt.config.master_config(c_path)
|
||||
def __init__(self, c_path='/etc/salt'):
|
||||
self.opts = salt.config.client_config(c_path)
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.salt_user = self.__get_user()
|
||||
self.key = self.__read_master_key()
|
||||
@ -93,9 +91,8 @@ class LocalClient(object):
|
||||
with open(keyfile, 'r') as KEY:
|
||||
return KEY.read()
|
||||
except (OSError, IOError):
|
||||
# In theory, this should never get hit. Belt & suspenders baby!
|
||||
raise SaltClientError(('Problem reading the salt root key. Are'
|
||||
' you root?'))
|
||||
# Fall back to eauth
|
||||
return ''
|
||||
|
||||
def __get_user(self):
|
||||
'''
|
||||
@ -104,7 +101,7 @@ class LocalClient(object):
|
||||
user = getpass.getuser()
|
||||
# if our user is root, look for other ways to figure out
|
||||
# who we are
|
||||
if user == 'root':
|
||||
if user == 'root' or 'SUDO_USER' in os.environ:
|
||||
env_vars = ['SUDO_USER', 'USER', 'USERNAME']
|
||||
for evar in env_vars:
|
||||
if evar in os.environ:
|
||||
@ -116,129 +113,6 @@ class LocalClient(object):
|
||||
return user
|
||||
return user
|
||||
|
||||
def _check_glob_minions(self, expr):
|
||||
'''
|
||||
Return the minions found by looking via globs
|
||||
'''
|
||||
cwd = os.getcwd()
|
||||
os.chdir(os.path.join(self.opts['pki_dir'], 'minions'))
|
||||
ret = set(glob.glob(expr))
|
||||
os.chdir(cwd)
|
||||
return ret
|
||||
|
||||
def _check_list_minions(self, expr):
|
||||
'''
|
||||
Return the minions found by looking via a list
|
||||
'''
|
||||
ret = []
|
||||
for fn_ in os.listdir(os.path.join(self.opts['pki_dir'], 'minions')):
|
||||
if fn_ in expr:
|
||||
if fn_ not in ret:
|
||||
ret.append(fn_)
|
||||
return ret
|
||||
|
||||
def _check_pcre_minions(self, expr):
|
||||
'''
|
||||
Return the minions found by looking via regular expressions
|
||||
'''
|
||||
ret = set()
|
||||
cwd = os.getcwd()
|
||||
os.chdir(os.path.join(self.opts['pki_dir'], 'minions'))
|
||||
reg = re.compile(expr)
|
||||
for fn_ in os.listdir('.'):
|
||||
if reg.match(fn_):
|
||||
ret.add(fn_)
|
||||
os.chdir(cwd)
|
||||
return ret
|
||||
|
||||
def _check_grain_minions(self, expr):
|
||||
'''
|
||||
Return the minions found by looking via a list
|
||||
'''
|
||||
minions = set(os.listdir(os.path.join(self.opts['pki_dir'], 'minions')))
|
||||
if self.opts.get('minion_data_cache', False):
|
||||
cdir = os.path.join(self.opts['cachedir'], 'minions')
|
||||
if not os.path.isdir(cdir):
|
||||
return list(minions)
|
||||
for id_ in os.listdir(cdir):
|
||||
if not id_ in minions:
|
||||
continue
|
||||
datap = os.path.join(cdir, id_, 'data.p')
|
||||
if not os.path.isfile(datap):
|
||||
continue
|
||||
grains = self.serial.load(open(datap)).get('grains')
|
||||
comps = expr.split(':')
|
||||
if len(comps) < 2:
|
||||
continue
|
||||
if comps[0] not in grains:
|
||||
minions.remove(id_)
|
||||
continue
|
||||
if isinstance(grains.get(comps[0]), list):
|
||||
# We are matching a single component to a single list member
|
||||
found = False
|
||||
for member in grains[comps[0]]:
|
||||
if fnmatch.fnmatch(str(member).lower(), comps[1].lower()):
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
continue
|
||||
minions.remove(id_)
|
||||
continue
|
||||
if fnmatch.fnmatch(
|
||||
str(grains.get(comps[0], '').lower()),
|
||||
comps[1].lower(),
|
||||
):
|
||||
continue
|
||||
else:
|
||||
minions.remove(id_)
|
||||
return list(minions)
|
||||
|
||||
def _check_grain_pcre_minions(self, expr):
|
||||
'''
|
||||
Return the minions found by looking via a list
|
||||
'''
|
||||
minions = set(os.listdir(os.path.join(self.opts['pki_dir'], 'minions')))
|
||||
if self.opts.get('minion_data_cache', False):
|
||||
cdir = os.path.join(self.opts['cachedir'], 'minions')
|
||||
if not os.path.isdir(cdir):
|
||||
return list(minions)
|
||||
for id_ in os.listdir(cdir):
|
||||
if not id_ in minions:
|
||||
continue
|
||||
datap = os.path.join(cdir, id_, 'data.p')
|
||||
if not os.path.isfile(datap):
|
||||
continue
|
||||
grains = self.serial.load(open(datap)).get('grains')
|
||||
comps = expr.split(':')
|
||||
if len(comps) < 2:
|
||||
continue
|
||||
if comps[0] not in grains:
|
||||
minions.remove(id_)
|
||||
if isinstance(grains[comps[0]], list):
|
||||
# We are matching a single component to a single list member
|
||||
found = False
|
||||
for member in grains[comps[0]]:
|
||||
if re.match(comps[1].lower(), str(member).lower()):
|
||||
found = True
|
||||
if found:
|
||||
continue
|
||||
minions.remove(id_)
|
||||
continue
|
||||
if re.match(
|
||||
comps[1].lower(),
|
||||
str(grains[comps[0]]).lower()
|
||||
):
|
||||
continue
|
||||
else:
|
||||
minions.remove(id_)
|
||||
return list(minions)
|
||||
|
||||
def _all_minions(self, expr=None):
|
||||
'''
|
||||
Return a list of all minions that have auth'd
|
||||
'''
|
||||
return os.listdir(os.path.join(self.opts['pki_dir'], 'minions'))
|
||||
|
||||
def _convert_range_to_list(self, tgt):
|
||||
range = seco.range.Range(self.opts['range_server'])
|
||||
try:
|
||||
@ -247,7 +121,7 @@ class LocalClient(object):
|
||||
print(("Range server exception: {0}".format(e)))
|
||||
return []
|
||||
|
||||
def gather_job_info(self, jid, tgt, tgt_type):
|
||||
def gather_job_info(self, jid, tgt, tgt_type, **kwargs):
|
||||
'''
|
||||
Return the information about a given job
|
||||
'''
|
||||
@ -256,7 +130,56 @@ class LocalClient(object):
|
||||
'saltutil.find_job',
|
||||
[jid],
|
||||
2,
|
||||
tgt_type)
|
||||
tgt_type,
|
||||
**kwargs)
|
||||
|
||||
def _check_pub_data(self, pub_data):
|
||||
'''
|
||||
Common checks on the pub_data data structure returned from running pub
|
||||
'''
|
||||
if not pub_data:
|
||||
err = ('Failed to authenticate, is this user permitted to execute '
|
||||
'commands?\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(4)
|
||||
|
||||
# Failed to connect to the master and send the pub
|
||||
if not 'jid' in pub_data or pub_data['jid'] == '0':
|
||||
return {}
|
||||
|
||||
return pub_data
|
||||
|
||||
def run_job(self,
|
||||
tgt,
|
||||
fun,
|
||||
arg=(),
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
timeout=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Prep the job dir and send minions the pub.
|
||||
Returns a dict of (checked) pub_data or an empty dict.
|
||||
'''
|
||||
try:
|
||||
jid = salt.utils.prep_jid(
|
||||
self.opts['cachedir'],
|
||||
self.opts['hash_type']
|
||||
)
|
||||
except Exception:
|
||||
jid = ''
|
||||
|
||||
pub_data = self.pub(
|
||||
tgt,
|
||||
fun,
|
||||
arg,
|
||||
expr_form,
|
||||
ret,
|
||||
jid=jid,
|
||||
timeout=timeout or self.opts['timeout'],
|
||||
**kwargs)
|
||||
|
||||
return self._check_pub_data(pub_data)
|
||||
|
||||
def cmd(
|
||||
self,
|
||||
@ -266,39 +189,26 @@ class LocalClient(object):
|
||||
timeout=None,
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
kwarg=None):
|
||||
kwarg=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Execute a salt command and return.
|
||||
'''
|
||||
arg = condition_kwarg(arg, kwarg)
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
try:
|
||||
jid = salt.utils.prep_jid(
|
||||
self.opts['cachedir'],
|
||||
self.opts['hash_type']
|
||||
)
|
||||
except Exception:
|
||||
jid = ''
|
||||
pub_data = self.pub(
|
||||
pub_data = self.run_job(
|
||||
tgt,
|
||||
fun,
|
||||
arg,
|
||||
expr_form,
|
||||
ret,
|
||||
jid=jid,
|
||||
timeout=timeout)
|
||||
timeout,
|
||||
**kwargs)
|
||||
|
||||
if not pub_data:
|
||||
err = ('Failed to authenticate, is this user permitted to execute '
|
||||
'commands?\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(4)
|
||||
if pub_data['jid'] == '0':
|
||||
# Failed to connect to the master and send the pub
|
||||
return {}
|
||||
elif not pub_data['jid']:
|
||||
return {}
|
||||
return self.get_returns(pub_data['jid'], pub_data['minions'], timeout)
|
||||
return pub_data
|
||||
|
||||
return self.get_returns(pub_data['jid'], pub_data['minions'],
|
||||
timeout or self.opts['timeout'])
|
||||
|
||||
def cmd_cli(
|
||||
self,
|
||||
@ -309,49 +219,36 @@ class LocalClient(object):
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
verbose=False,
|
||||
kwarg=None):
|
||||
kwarg=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Execute a salt command and return data conditioned for command line
|
||||
output
|
||||
'''
|
||||
arg = condition_kwarg(arg, kwarg)
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
try:
|
||||
jid = salt.utils.prep_jid(
|
||||
self.opts['cachedir'],
|
||||
self.opts['hash_type']
|
||||
)
|
||||
except Exception:
|
||||
jid = ''
|
||||
pub_data = self.pub(
|
||||
pub_data = self.run_job(
|
||||
tgt,
|
||||
fun,
|
||||
arg,
|
||||
expr_form,
|
||||
ret,
|
||||
jid=jid,
|
||||
timeout=timeout)
|
||||
timeout,
|
||||
**kwargs)
|
||||
|
||||
if not pub_data:
|
||||
err = ('Failed to authenticate, is this user permitted to execute '
|
||||
'commands?\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(4)
|
||||
if pub_data['jid'] == '0':
|
||||
print('Failed to connect to the Master, is the Salt Master running?')
|
||||
yield {}
|
||||
elif not pub_data['jid']:
|
||||
print('No minions match the target')
|
||||
yield {}
|
||||
yield pub_data
|
||||
else:
|
||||
for fn_ret in self.get_cli_event_returns(pub_data['jid'],
|
||||
pub_data['minions'],
|
||||
timeout,
|
||||
timeout or self.opts['timeout'],
|
||||
tgt,
|
||||
expr_form,
|
||||
verbose):
|
||||
verbose,
|
||||
**kwargs):
|
||||
|
||||
if not fn_ret:
|
||||
continue
|
||||
|
||||
yield fn_ret
|
||||
|
||||
def cmd_iter(
|
||||
@ -362,36 +259,24 @@ class LocalClient(object):
|
||||
timeout=None,
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
kwarg=None):
|
||||
kwarg=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Execute a salt command and return an iterator to return data as it is
|
||||
received
|
||||
'''
|
||||
arg = condition_kwarg(arg, kwarg)
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid = salt.utils.prep_jid(
|
||||
self.opts['cachedir'],
|
||||
self.opts['hash_type']
|
||||
)
|
||||
pub_data = self.pub(
|
||||
pub_data = self.run_job(
|
||||
tgt,
|
||||
fun,
|
||||
arg,
|
||||
expr_form,
|
||||
ret,
|
||||
jid=jid,
|
||||
timeout=timeout)
|
||||
timeout,
|
||||
**kwargs)
|
||||
|
||||
if not pub_data:
|
||||
err = ('Failed to authenticate, is this user permitted to execute '
|
||||
'commands?\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(4)
|
||||
if pub_data['jid'] == '0':
|
||||
# Failed to connect to the master and send the pub
|
||||
yield {}
|
||||
elif not pub_data['jid']:
|
||||
yield {}
|
||||
yield pub_data
|
||||
else:
|
||||
for fn_ret in self.get_iter_returns(pub_data['jid'],
|
||||
pub_data['minions'],
|
||||
@ -408,35 +293,23 @@ class LocalClient(object):
|
||||
timeout=None,
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
kwarg=None):
|
||||
kwarg=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Execute a salt command and return
|
||||
'''
|
||||
arg = condition_kwarg(arg, kwarg)
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid = salt.utils.prep_jid(
|
||||
self.opts['cachedir'],
|
||||
self.opts['hash_type']
|
||||
)
|
||||
pub_data = self.pub(
|
||||
pub_data = self.run_job(
|
||||
tgt,
|
||||
fun,
|
||||
arg,
|
||||
expr_form,
|
||||
ret,
|
||||
jid=jid,
|
||||
timeout=timeout)
|
||||
timeout,
|
||||
**kwargs)
|
||||
|
||||
if not pub_data:
|
||||
err = ('Failed to authenticate, is this user permitted to execute '
|
||||
'commands?\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(4)
|
||||
if pub_data['jid'] == '0':
|
||||
# Failed to connect to the master and send the pub
|
||||
yield {}
|
||||
elif not pub_data['jid']:
|
||||
yield {}
|
||||
yield pub_data
|
||||
else:
|
||||
for fn_ret in self.get_iter_returns(pub_data['jid'],
|
||||
pub_data['minions'],
|
||||
@ -452,35 +325,24 @@ class LocalClient(object):
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
verbose=False,
|
||||
kwarg=None):
|
||||
kwarg=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Execute a salt command and return
|
||||
'''
|
||||
arg = condition_kwarg(arg, kwarg)
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid = salt.utils.prep_jid(
|
||||
self.opts['cachedir'],
|
||||
self.opts['hash_type']
|
||||
)
|
||||
pub_data = self.pub(
|
||||
pub_data = self.run_job(
|
||||
tgt,
|
||||
fun,
|
||||
arg,
|
||||
expr_form,
|
||||
ret,
|
||||
jid=jid,
|
||||
timeout=timeout)
|
||||
timeout,
|
||||
**kwargs)
|
||||
|
||||
if not pub_data:
|
||||
err = ('Failed to authenticate, is this user permitted to execute '
|
||||
'commands?\n')
|
||||
sys.stderr.write(err)
|
||||
sys.exit(4)
|
||||
if pub_data['jid'] == '0':
|
||||
# Failed to connect to the master and send the pub
|
||||
return {}
|
||||
elif not pub_data['jid']:
|
||||
return {}
|
||||
return pub_data
|
||||
|
||||
return (self.get_cli_static_event_returns(pub_data['jid'],
|
||||
pub_data['minions'],
|
||||
timeout,
|
||||
@ -495,7 +357,8 @@ class LocalClient(object):
|
||||
timeout=None,
|
||||
tgt='*',
|
||||
tgt_type='glob',
|
||||
verbose=False):
|
||||
verbose=False,
|
||||
**kwargs):
|
||||
'''
|
||||
This method starts off a watcher looking at the return data for
|
||||
a specified jid, it returns all of the information for the jid
|
||||
@ -558,7 +421,7 @@ class LocalClient(object):
|
||||
if int(time.time()) > start + timeout:
|
||||
# The timeout has been reached, check the jid to see if the
|
||||
# timeout needs to be increased
|
||||
jinfo = self.gather_job_info(jid, tgt, tgt_type)
|
||||
jinfo = self.gather_job_info(jid, tgt, tgt_type, **kwargs)
|
||||
more_time = False
|
||||
for id_ in jinfo:
|
||||
if jinfo[id_]:
|
||||
@ -753,6 +616,7 @@ class LocalClient(object):
|
||||
'''
|
||||
Get the returns for the command line interface via the event system
|
||||
'''
|
||||
minions = set(minions)
|
||||
if verbose:
|
||||
msg = 'Executing job with jid {0}'.format(jid)
|
||||
print(msg)
|
||||
@ -810,7 +674,8 @@ class LocalClient(object):
|
||||
timeout=None,
|
||||
tgt='*',
|
||||
tgt_type='glob',
|
||||
verbose=False):
|
||||
verbose=False,
|
||||
**kwargs):
|
||||
'''
|
||||
Get the returns for the command line interface via the event system
|
||||
'''
|
||||
@ -865,7 +730,7 @@ class LocalClient(object):
|
||||
if int(time.time()) > start + timeout:
|
||||
# The timeout has been reached, check the jid to see if the
|
||||
# timeout needs to be increased
|
||||
jinfo = self.gather_job_info(jid, tgt, tgt_type)
|
||||
jinfo = self.gather_job_info(jid, tgt, tgt_type, **kwargs)
|
||||
more_time = False
|
||||
for id_ in jinfo:
|
||||
if jinfo[id_]:
|
||||
@ -947,29 +812,15 @@ class LocalClient(object):
|
||||
continue
|
||||
return ret
|
||||
|
||||
def check_minions(self, expr, expr_form='glob'):
|
||||
'''
|
||||
Check the passed regex against the available minions' public keys
|
||||
stored for authentication. This should return a set of ids which
|
||||
match the regex, this will then be used to parse the returns to
|
||||
make sure everyone has checked back in.
|
||||
'''
|
||||
try:
|
||||
minions = {'glob': self._check_glob_minions,
|
||||
'pcre': self._check_pcre_minions,
|
||||
'list': self._check_list_minions,
|
||||
'grain': self._check_grain_minions,
|
||||
'grain_pcre': self._check_grain_pcre_minions,
|
||||
'exsel': self._all_minions,
|
||||
'pillar': self._all_minions,
|
||||
'compound': self._all_minions,
|
||||
}[expr_form](expr)
|
||||
except Exception:
|
||||
minions = expr
|
||||
return minions
|
||||
|
||||
def pub(self, tgt, fun, arg=(), expr_form='glob',
|
||||
ret='', jid='', timeout=5):
|
||||
def pub(self,
|
||||
tgt,
|
||||
fun,
|
||||
arg=(),
|
||||
expr_form='glob',
|
||||
ret='',
|
||||
jid='',
|
||||
timeout=5,
|
||||
**kwargs):
|
||||
'''
|
||||
Take the required arguments and publish the given command.
|
||||
Arguments:
|
||||
@ -1005,7 +856,10 @@ class LocalClient(object):
|
||||
conf_file = self.opts.get('conf_file', 'the master config file')
|
||||
err = 'Node group {0} unavailable in {1}'.format(tgt, conf_file)
|
||||
raise SaltInvocationError(err)
|
||||
tgt = self.opts['nodegroups'][tgt]
|
||||
tgt = salt.utils.minions.nodegroup_comp(
|
||||
tgt,
|
||||
self.opts['nodegroups']
|
||||
)
|
||||
expr_form = 'compound'
|
||||
|
||||
# Convert a range expression to a list of nodes and change expression
|
||||
@ -1014,28 +868,26 @@ class LocalClient(object):
|
||||
tgt = self._convert_range_to_list(tgt)
|
||||
expr_form = 'list'
|
||||
|
||||
# Run a check_minions, if no minions match return False
|
||||
# format the payload - make a function that does this in the payload
|
||||
# module
|
||||
# make the zmq client
|
||||
# connect to the req server
|
||||
# send!
|
||||
# return what we get back
|
||||
minions = self.check_minions(tgt, expr_form)
|
||||
|
||||
if not minions:
|
||||
return {'jid': None,
|
||||
'minions': minions}
|
||||
|
||||
# Generate the standard keyword args to feed to format_payload
|
||||
payload_kwargs = {'cmd': 'publish',
|
||||
'tgt': tgt,
|
||||
'fun': fun,
|
||||
'arg': arg,
|
||||
'key': self.key,
|
||||
'tgt_type': expr_form,
|
||||
'ret': ret,
|
||||
'jid': jid}
|
||||
'tgt': tgt,
|
||||
'fun': fun,
|
||||
'arg': arg,
|
||||
'key': self.key,
|
||||
'tgt_type': expr_form,
|
||||
'ret': ret,
|
||||
'jid': jid}
|
||||
|
||||
# if kwargs are passed, pack them.
|
||||
if kwargs:
|
||||
payload_kwargs['kwargs'] = kwargs
|
||||
|
||||
# If we have a salt user, add it to the payload
|
||||
if self.salt_user:
|
||||
@ -1052,7 +904,7 @@ class LocalClient(object):
|
||||
if not payload:
|
||||
return payload
|
||||
return {'jid': payload['load']['jid'],
|
||||
'minions': minions}
|
||||
'minions': payload['load']['minions']}
|
||||
|
||||
|
||||
class FunctionWrapper(dict):
|
||||
|
@ -104,7 +104,6 @@ def include_config(include, opts, orig_path, verbose):
|
||||
Parses extra configuration file(s) specified in an include list in the
|
||||
main config file.
|
||||
'''
|
||||
|
||||
# Protect against empty option
|
||||
if not include:
|
||||
return opts
|
||||
@ -239,14 +238,14 @@ def minion_config(path):
|
||||
opts['open_mode'] = opts['open_mode'] is True
|
||||
|
||||
# set up the extension_modules location from the cachedir
|
||||
opts['extension_modules'] = os.path.join(opts['cachedir'], 'extmods')
|
||||
opts['extension_modules'] = (
|
||||
opts.get('extension_modules') or
|
||||
os.path.join(opts['cachedir'], 'extmods')
|
||||
)
|
||||
|
||||
# Prepend root_dir to other paths
|
||||
prepend_root_dir(opts, ['pki_dir', 'cachedir', 'log_file', 'sock_dir',
|
||||
'key_logfile', 'extension_modules'])
|
||||
|
||||
opts['grains'] = salt.loader.grains(opts)
|
||||
|
||||
return opts
|
||||
|
||||
|
||||
@ -274,20 +273,25 @@ def master_config(path):
|
||||
'pillar_roots': {
|
||||
'base': ['/srv/pillar'],
|
||||
},
|
||||
'ext_pillar': {},
|
||||
'ext_pillar': [],
|
||||
# TODO - Set this to 2 by default in 0.10.5
|
||||
'pillar_version': 1,
|
||||
'syndic_master': '',
|
||||
'runner_dirs': [],
|
||||
'client_acl': {},
|
||||
'external_auth': {},
|
||||
'token_expire': 720,
|
||||
'file_buffer_size': 1048576,
|
||||
'max_open_files': 100000,
|
||||
'hash_type': 'md5',
|
||||
'conf_file': path,
|
||||
'pub_refresh': True,
|
||||
'pub_refresh': False,
|
||||
'open_mode': False,
|
||||
'auto_accept': False,
|
||||
'renderer': 'yaml_jinja',
|
||||
'failhard': False,
|
||||
'state_top': 'top.sls',
|
||||
'master_tops': {},
|
||||
'external_nodes': '',
|
||||
'order_masters': False,
|
||||
'job_cache': True,
|
||||
@ -327,10 +331,15 @@ def master_config(path):
|
||||
|
||||
opts['aes'] = salt.crypt.Crypticle.generate_key_string()
|
||||
|
||||
opts['extension_modules'] = os.path.join(opts['cachedir'], 'extmods')
|
||||
opts['extension_modules'] = (
|
||||
opts.get('extension_modules') or
|
||||
os.path.join(opts['cachedir'], 'extmods')
|
||||
)
|
||||
opts['token_dir'] = os.path.join(opts['cachedir'], 'tokens')
|
||||
# Prepend root_dir to other paths
|
||||
prepend_root_dir(opts, ['pki_dir', 'cachedir', 'log_file',
|
||||
'sock_dir', 'key_logfile', 'extension_modules', 'autosign_file'])
|
||||
'sock_dir', 'key_logfile', 'extension_modules',
|
||||
'autosign_file', 'token_dir'])
|
||||
|
||||
# Enabling open mode requires that the value be set to True, and
|
||||
# nothing else!
|
||||
@ -338,3 +347,21 @@ def master_config(path):
|
||||
opts['auto_accept'] = opts['auto_accept'] is True
|
||||
opts['file_roots'] = _validate_file_roots(opts['file_roots'])
|
||||
return opts
|
||||
|
||||
|
||||
def client_config(path):
|
||||
'''
|
||||
Load in the configuration data needed for the LocalClient. This function
|
||||
searches for client specific configurations and adds them to the data from
|
||||
the master configuration.
|
||||
'''
|
||||
opts = {'token_file': os.path.expanduser('~/.salt_token')}
|
||||
opts.update(master_config(path))
|
||||
cpath = os.path.expanduser('~/.salt')
|
||||
load_config(opts, cpath, 'SALT_CLIENT_CONFIG')
|
||||
if 'token_file' in opts:
|
||||
opts['token_file'] = os.path.expanduser(opts['token_file'])
|
||||
if os.path.isfile(opts['token_file']):
|
||||
with open(opts['token_file']) as fp_:
|
||||
opts['token'] = fp_.read().strip()
|
||||
return opts
|
||||
|
@ -129,6 +129,7 @@ class Auth(object):
|
||||
'''
|
||||
def __init__(self, opts):
|
||||
self.opts = opts
|
||||
self.token = Crypticle.generate_key_string()
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.pub_path = os.path.join(self.opts['pki_dir'], 'minion.pub')
|
||||
self.rsa_path = os.path.join(self.opts['pki_dir'], 'minion.pem')
|
||||
@ -177,6 +178,11 @@ class Auth(object):
|
||||
payload['load'] = {}
|
||||
payload['load']['cmd'] = '_auth'
|
||||
payload['load']['id'] = self.opts['id']
|
||||
try:
|
||||
pub = RSA.load_pub_key(os.path.join(self.opts['pki_dir'], self.mpub))
|
||||
payload['load']['token'] = pub.public_encrypt(self.token, 4)
|
||||
except Exception:
|
||||
pass
|
||||
with open(tmp_pub, 'r') as fp_:
|
||||
payload['load']['pub'] = fp_.read()
|
||||
os.remove(tmp_pub)
|
||||
@ -218,12 +224,16 @@ class Auth(object):
|
||||
'have been subverted, verify salt master\'s public '
|
||||
'key')
|
||||
return False
|
||||
try:
|
||||
if token and not self.decrypt_aes(token) == self.token:
|
||||
log.error('The master failed to decrypt the random minion token')
|
||||
return False
|
||||
except Exception:
|
||||
log.error('The master failed to decrypt the random minion token')
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
open(m_pub_fn, 'w+').write(master_pub)
|
||||
pub = RSA.load_pub_key(tmp_pub)
|
||||
plaintext = pub.public_decrypt(token, 5)
|
||||
os.remove(tmp_pub)
|
||||
if plaintext == 'salty bacon':
|
||||
return True
|
||||
log.error('The salt master has failed verification for an unknown '
|
||||
'reason, verify your salt keys')
|
||||
@ -275,6 +285,8 @@ class Auth(object):
|
||||
if not self.verify_master(payload['pub_key'], payload['token']):
|
||||
log.critical(
|
||||
'The Salt Master server\'s public key did not authenticate!\n'
|
||||
'The master may need to be updated if it is a version of Salt '
|
||||
'lower than 0.10.4, or\n'
|
||||
'If you are confident that you are connecting to a valid Salt '
|
||||
'Master, then remove the master public key and restart the '
|
||||
'Salt Minion.\nThe master public key can be found '
|
||||
|
@ -729,7 +729,8 @@ class RemoteClient(Client):
|
||||
master.
|
||||
'''
|
||||
load = {'cmd': '_ext_nodes',
|
||||
'id': self.opts['id']}
|
||||
'id': self.opts['id'],
|
||||
'opts': self.opts}
|
||||
try:
|
||||
return self.auth.crypticle.loads(
|
||||
self.sreq.send(
|
||||
|
@ -18,6 +18,8 @@ import socket
|
||||
import sys
|
||||
import re
|
||||
import platform
|
||||
import logging
|
||||
import locale
|
||||
|
||||
# Extend the default list of supported distros. This will be used for the
|
||||
# /etc/DISTRO-release checking that is part of platform.linux_distribution()
|
||||
@ -25,12 +27,29 @@ from platform import _supported_dists
|
||||
_supported_dists += ('arch', 'mageia', 'meego', 'vmware', 'bluewhite64',
|
||||
'slamd64', 'enterprise', 'ovs', 'system')
|
||||
|
||||
import salt.log
|
||||
import salt.utils
|
||||
|
||||
# Solve the Chicken and egg problem where grains need to run before any
|
||||
# of the modules are loaded and are generally available for any usage.
|
||||
import salt.modules.cmdmod
|
||||
__salt__ = {'cmd.run': salt.modules.cmdmod._run_quiet}
|
||||
__salt__ = {
|
||||
'cmd.run': salt.modules.cmdmod._run_quiet,
|
||||
'cmd.run_all': salt.modules.cmdmod._run_all_quiet
|
||||
}
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
has_wmi = False
|
||||
if sys.platform.startswith('win'):
|
||||
# attempt to import the python wmi module
|
||||
# the Windows minion uses WMI for some of its grains
|
||||
try:
|
||||
import wmi
|
||||
has_wmi = True
|
||||
except ImportError:
|
||||
log.exception("Unable to import Python wmi module, some core grains "
|
||||
"will be missing")
|
||||
|
||||
|
||||
def _windows_cpudata():
|
||||
@ -119,10 +138,13 @@ def _sunos_cpudata(osdata):
|
||||
grains = {'num_cpus': 0}
|
||||
|
||||
grains['cpuarch'] = __salt__['cmd.run']('uname -p').strip()
|
||||
for line in __salt__['cmd.run']('/usr/sbin/psrinfo 2>/dev/null').split('\n'):
|
||||
for line in __salt__['cmd.run'](
|
||||
'/usr/sbin/psrinfo 2>/dev/null'
|
||||
).split('\n'):
|
||||
grains['num_cpus'] += 1
|
||||
grains['cpu_model'] = __salt__['cmd.run']('kstat -p cpu_info:0:cpu_info0:implementation').split()[1].strip()
|
||||
|
||||
grains['cpu_model'] = __salt__['cmd.run'](
|
||||
'kstat -p cpu_info:*:*:implementation'
|
||||
).split()[1].strip()
|
||||
return grains
|
||||
|
||||
|
||||
@ -150,18 +172,17 @@ def _memdata(osdata):
|
||||
grains['mem_total'] = str(int(mem) / 1024 / 1024)
|
||||
elif osdata['kernel'] == 'SunOS':
|
||||
for line in __salt__['cmd.run']('/usr/sbin/prtconf 2>/dev/null').split('\n'):
|
||||
comps = line.split(' ')
|
||||
comps = line.split(' ')
|
||||
if comps[0].strip() == 'Memory' and comps[1].strip() == 'size:':
|
||||
grains['mem_total'] = int(comps[2].strip())
|
||||
elif osdata['kernel'] == 'Windows':
|
||||
for line in __salt__['cmd.run']('SYSTEMINFO /FO LIST').split('\n'):
|
||||
comps = line.split(':')
|
||||
if not len(comps) > 1:
|
||||
continue
|
||||
if comps[0].strip() == 'Total Physical Memory':
|
||||
# Windows XP use '.' as separator and Windows 2008 Server R2 use ','
|
||||
grains['mem_total'] = int(comps[1].split()[0].replace('.', '').replace(',', ''))
|
||||
break
|
||||
grains['mem_total'] = int(comps[2].strip())
|
||||
elif osdata['kernel'] == 'Windows' and has_wmi:
|
||||
wmi_c = wmi.WMI()
|
||||
# this is a list of each stick of ram in a system
|
||||
# WMI returns it as the string value of the number of bytes
|
||||
tot_bytes = sum(map(lambda x: int(x.Capacity),
|
||||
wmi_c.Win32_PhysicalMemory()), 0)
|
||||
# return memory info in gigabytes
|
||||
grains['mem_total'] = int(tot_bytes / (1024 ** 2))
|
||||
return grains
|
||||
|
||||
|
||||
@ -177,38 +198,69 @@ def _virtual(osdata):
|
||||
lspci = salt.utils.which('lspci')
|
||||
dmidecode = salt.utils.which('dmidecode')
|
||||
|
||||
if dmidecode:
|
||||
output = __salt__['cmd.run']('dmidecode')
|
||||
# Product Name: VirtualBox
|
||||
if 'Vendor: QEMU' in output:
|
||||
# FIXME: Make this detect between kvm or qemu
|
||||
grains['virtual'] = 'kvm'
|
||||
if 'Vendor: Bochs' in output:
|
||||
grains['virtual'] = 'kvm'
|
||||
elif 'VirtualBox' in output:
|
||||
grains['virtual'] = 'VirtualBox'
|
||||
# Product Name: VMware Virtual Platform
|
||||
elif 'VMware' in output:
|
||||
grains['virtual'] = 'VMware'
|
||||
# Manufacturer: Microsoft Corporation
|
||||
# Product Name: Virtual Machine
|
||||
elif 'Manufacturer: Microsoft' in output and 'Virtual Machine' in output:
|
||||
grains['virtual'] = 'VirtualPC'
|
||||
# Manufacturer: Parallels Software International Inc.
|
||||
elif 'Parallels Software' in output:
|
||||
grains['virtual'] = 'Parallels'
|
||||
# Fall back to lspci if dmidecode isn't available
|
||||
elif lspci:
|
||||
model = __salt__['cmd.run']('lspci').lower()
|
||||
if 'vmware' in model:
|
||||
grains['virtual'] = 'VMware'
|
||||
# 00:04.0 System peripheral: InnoTek Systemberatung GmbH VirtualBox Guest Service
|
||||
elif 'virtualbox' in model:
|
||||
grains['virtual'] = 'VirtualBox'
|
||||
elif 'qemu' in model:
|
||||
grains['virtual'] = 'kvm'
|
||||
elif 'virtio' in model:
|
||||
grains['virtual'] = 'kvm'
|
||||
for command in ('dmidecode', 'lspci'):
|
||||
which = salt.utils.which(command)
|
||||
|
||||
if which is None:
|
||||
continue
|
||||
|
||||
ret = __salt__['cmd.run_all'](which)
|
||||
|
||||
if ret['retcode'] > 0:
|
||||
if salt.log.is_logging_configured():
|
||||
log.warn(
|
||||
'Although \'{0}\' was found in path, the current user '
|
||||
'cannot execute it. Grains output might not be '
|
||||
'accurate.'.format(command)
|
||||
)
|
||||
continue
|
||||
|
||||
output = ret['stdout']
|
||||
|
||||
if command == 'dmidecode':
|
||||
# Product Name: VirtualBox
|
||||
if 'Vendor: QEMU' in output:
|
||||
# FIXME: Make this detect between kvm or qemu
|
||||
grains['virtual'] = 'kvm'
|
||||
if 'Vendor: Bochs' in output:
|
||||
grains['virtual'] = 'kvm'
|
||||
elif 'VirtualBox' in output:
|
||||
grains['virtual'] = 'VirtualBox'
|
||||
# Product Name: VMware Virtual Platform
|
||||
elif 'VMware' in output:
|
||||
grains['virtual'] = 'VMware'
|
||||
# Manufacturer: Microsoft Corporation
|
||||
# Product Name: Virtual Machine
|
||||
elif 'Manufacturer: Microsoft' in output and 'Virtual Machine' in output:
|
||||
grains['virtual'] = 'VirtualPC'
|
||||
# Manufacturer: Parallels Software International Inc.
|
||||
elif 'Parallels Software' in output:
|
||||
grains['virtual'] = 'Parallels'
|
||||
# Break out of the loop, lspci parsing is not necessary
|
||||
break
|
||||
elif command == 'lspci':
|
||||
# dmidecode not available or the user does not have the necessary
|
||||
# permissions
|
||||
model = output.lower()
|
||||
if 'vmware' in model:
|
||||
grains['virtual'] = 'VMware'
|
||||
# 00:04.0 System peripheral: InnoTek Systemberatung GmbH VirtualBox Guest Service
|
||||
elif 'virtualbox' in model:
|
||||
grains['virtual'] = 'VirtualBox'
|
||||
elif 'qemu' in model:
|
||||
grains['virtual'] = 'kvm'
|
||||
elif 'virtio' in model:
|
||||
grains['virtual'] = 'kvm'
|
||||
# Break out of the loop so the next log message is not issued
|
||||
break
|
||||
else:
|
||||
log.warn(
|
||||
'Both \'dmidecode\' and \'lspci\' failed to execute, either '
|
||||
'because they do not exist on the system of the user running '
|
||||
'this instance does not have the necessary permissions to '
|
||||
'execute them. Grains output might not be accurate.'
|
||||
)
|
||||
|
||||
choices = ('Linux', 'OpenBSD', 'HP-UX')
|
||||
isdir = os.path.isdir
|
||||
if osdata['kernel'] in choices:
|
||||
@ -268,7 +320,7 @@ def _virtual(osdata):
|
||||
# Check if it's a "regular" zone. (i.e. Solaris 10/11 zone)
|
||||
zonename = salt.utils.which('zonename')
|
||||
if zonename:
|
||||
zone = __salt__['cmd.run']('{0}'.format(zonename)).strip()
|
||||
zone = __salt__['cmd.run']('{0}'.format(zonename)).strip()
|
||||
if zone != "global":
|
||||
grains['virtual'] = 'zone'
|
||||
# Check if it's a branded zone (i.e. Solaris 8/9 zone)
|
||||
@ -285,7 +337,7 @@ def _ps(osdata):
|
||||
bsd_choices = ('FreeBSD', 'NetBSD', 'OpenBSD', 'MacOS')
|
||||
if osdata['os'] in bsd_choices:
|
||||
grains['ps'] = 'ps auxwww'
|
||||
if osdata['os'] == 'Solaris':
|
||||
elif osdata['os_family'] == 'Solaris':
|
||||
grains['ps'] = '/usr/ucb/ps auxwww'
|
||||
elif osdata['os'] == 'Windows':
|
||||
grains['ps'] = 'tasklist.exe'
|
||||
@ -305,30 +357,39 @@ def _windows_platform_data(osdata):
|
||||
# productname
|
||||
# biosversion
|
||||
# osfullname
|
||||
# inputlocale
|
||||
# timezone
|
||||
# windowsdomain
|
||||
|
||||
grains = {}
|
||||
get_these_grains = {
|
||||
'OS Manufacturer': 'osmanufacturer',
|
||||
'System Manufacturer': 'manufacturer',
|
||||
'System Model': 'productname',
|
||||
'BIOS Version': 'biosversion',
|
||||
'OS Name': 'osfullname',
|
||||
'Input Locale': 'inputlocale',
|
||||
'Time Zone': 'timezone',
|
||||
'Domain': 'windowsdomain',
|
||||
}
|
||||
systeminfo = __salt__['cmd.run']('SYSTEMINFO')
|
||||
for line in systeminfo.split('\n'):
|
||||
comps = line.split(':', 1)
|
||||
if not len(comps) > 1:
|
||||
continue
|
||||
item = comps[0].strip()
|
||||
value = comps[1].strip()
|
||||
if item in get_these_grains:
|
||||
grains[get_these_grains[item]] = value
|
||||
if not has_wmi:
|
||||
return {}
|
||||
|
||||
wmi_c = wmi.WMI()
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394102%28v=vs.85%29.aspx
|
||||
systeminfo = wmi_c.Win32_ComputerSystem()[0]
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394239%28v=vs.85%29.aspx
|
||||
osinfo = wmi_c.Win32_OperatingSystem()[0]
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394077(v=vs.85).aspx
|
||||
biosinfo = wmi_c.Win32_BIOS()[0]
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394498(v=vs.85).aspx
|
||||
timeinfo = wmi_c.Win32_TimeZone()[0]
|
||||
|
||||
# the name of the OS comes with a bunch of other data about the install
|
||||
# location. For example:
|
||||
# 'Microsoft Windows Server 2008 R2 Standard |C:\\Windows|\\Device\\Harddisk0\\Partition2'
|
||||
(osfullname, _) = osinfo.Name.split('|', 1)
|
||||
osfullname = osfullname.strip()
|
||||
grains = {
|
||||
'osmanufacturer': osinfo.Manufacturer,
|
||||
'manufacturer': systeminfo.Manufacturer,
|
||||
'productname': systeminfo.Model,
|
||||
# bios name had a bunch of whitespace appended to it in my testing
|
||||
# 'PhoenixBIOS 4.0 Release 6.0 '
|
||||
'biosversion': biosinfo.Name.strip(),
|
||||
'osfullname': osfullname,
|
||||
'timezone': timeinfo.Description,
|
||||
'windowsdomain': systeminfo.Domain,
|
||||
}
|
||||
|
||||
return grains
|
||||
|
||||
|
||||
@ -338,12 +399,14 @@ def id_():
|
||||
'''
|
||||
return {'id': __opts__['id']}
|
||||
|
||||
# This maps (at most) the first ten characters (no spaces, lowercased) of
|
||||
# This maps (at most) the first ten characters (no spaces, lowercased) of
|
||||
# 'osfullname' to the 'os' grain that Salt traditionally uses.
|
||||
_os_name_map = {
|
||||
'redhatente': 'RedHat',
|
||||
'debian': 'Debian',
|
||||
'arch': 'Arch',
|
||||
'amazonlinu': 'Amazon',
|
||||
'centoslinu': 'CentOS',
|
||||
}
|
||||
|
||||
# Map the 'os' grain to the 'os_family' grain
|
||||
@ -355,17 +418,19 @@ _os_family_map = {
|
||||
'Scientific': 'RedHat',
|
||||
'Amazon': 'RedHat',
|
||||
'CloudLinux': 'RedHat',
|
||||
'OVS': 'RedHat',
|
||||
'OEL': 'RedHat',
|
||||
'Mandrake': 'Mandriva',
|
||||
'ESXi': 'VMWare',
|
||||
'VMWareESX': 'VMWare',
|
||||
'Bluewhite64': 'Bluewhite',
|
||||
'Slamd64': 'Slackware',
|
||||
'OVS': 'Oracle',
|
||||
'OEL': 'Oracle',
|
||||
'SLES': 'Suse',
|
||||
'SLED': 'Suse',
|
||||
'openSUSE': 'Suse',
|
||||
'SUSE': 'Suse'
|
||||
'SUSE': 'Suse',
|
||||
'Solaris': 'Solaris',
|
||||
'SmartOS': 'Solaris',
|
||||
}
|
||||
|
||||
|
||||
@ -374,10 +439,18 @@ def os_data():
|
||||
Return grains pertaining to the operating system
|
||||
'''
|
||||
grains = {}
|
||||
try:
|
||||
(grains['defaultlanguage'],
|
||||
grains['defaultencoding']) = locale.getdefaultlocale()
|
||||
except Exception:
|
||||
# locale.getdefaultlocale can ValueError!! Catch anything else it
|
||||
# might do, per #2205
|
||||
grains['defaultlanguage'] = 'unknown'
|
||||
grains['defaultencoding'] = 'unknown'
|
||||
# Windows Server 2008 64-bit
|
||||
# ('Windows', 'MINIONNAME', '2008ServerR2', '6.1.7601', 'AMD64', 'Intel64 Fam ily 6 Model 23 Stepping 6, GenuineIntel')
|
||||
# Ubuntu 10.04
|
||||
# ('Linux', 'FIRE66VMA01', '2.6.32-38-server', '#83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012', 'x86_64', '')
|
||||
# ('Linux', 'MINIONNAME', '2.6.32-38-server', '#83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012', 'x86_64', '')
|
||||
(grains['kernel'], grains['host'],
|
||||
grains['kernelrelease'], version, grains['cpuarch'], _) = platform.uname()
|
||||
if grains['kernel'] == 'Windows':
|
||||
@ -432,6 +505,11 @@ def os_data():
|
||||
grains.update(_linux_cpudata())
|
||||
elif grains['kernel'] == 'SunOS':
|
||||
grains['os'] = 'Solaris'
|
||||
if os.path.isfile('/etc/release'):
|
||||
with open('/etc/release', 'r') as fp_:
|
||||
rel_data = fp_.read()
|
||||
if 'SmartOS' in rel_data:
|
||||
grains['os'] = 'SmartOS'
|
||||
grains.update(_sunos_cpudata(grains))
|
||||
elif grains['kernel'] == 'VMkernel':
|
||||
grains['os'] = 'ESXi'
|
||||
|
@ -22,7 +22,13 @@ salt_base_path = os.path.dirname(salt.__file__)
|
||||
loaded_base_name = 'salt.loaded'
|
||||
|
||||
|
||||
def _create_loader(opts, ext_type, tag, ext_dirs=True, ext_type_dirs=None):
|
||||
def _create_loader(
|
||||
opts,
|
||||
ext_type,
|
||||
tag,
|
||||
ext_dirs=True,
|
||||
ext_type_dirs=None,
|
||||
base_path=None):
|
||||
'''
|
||||
Creates Loader instance
|
||||
|
||||
@ -31,8 +37,11 @@ def _create_loader(opts, ext_type, tag, ext_dirs=True, ext_type_dirs=None):
|
||||
extension types,
|
||||
base types.
|
||||
'''
|
||||
if base_path:
|
||||
sys_types = os.path.join(base_path, ext_type)
|
||||
else:
|
||||
sys_types = os.path.join(salt_base_path, ext_type)
|
||||
ext_types = os.path.join(opts['extension_modules'], ext_type)
|
||||
sys_types = os.path.join(salt_base_path, ext_type)
|
||||
|
||||
ext_type_types = []
|
||||
if ext_dirs:
|
||||
@ -94,6 +103,22 @@ def pillars(opts, functions):
|
||||
return load.filter_func('ext_pillar', pack)
|
||||
|
||||
|
||||
def tops(opts):
|
||||
'''
|
||||
Returns the returner modules
|
||||
'''
|
||||
load = _create_loader(opts, 'tops', 'top')
|
||||
return load.filter_func('top')
|
||||
|
||||
|
||||
def auth(opts):
|
||||
'''
|
||||
Returns the returner modules
|
||||
'''
|
||||
load = _create_loader(opts, 'auth', 'auth')
|
||||
return load.gen_functions()
|
||||
|
||||
|
||||
def states(opts, functions):
|
||||
'''
|
||||
Returns the state modules
|
||||
@ -132,7 +157,7 @@ def grains(opts):
|
||||
salt.config.load_config(
|
||||
pre_opts, opts['conf_file'], 'SALT_MINION_CONFIG'
|
||||
)
|
||||
default_include = pre_opts.get('default_include', [])
|
||||
default_include = pre_opts.get('default_include', opts['default_include'])
|
||||
include = pre_opts.get('include', [])
|
||||
pre_opts = salt.config.include_config(
|
||||
default_include, pre_opts, opts['conf_file'], verbose=False
|
||||
@ -509,17 +534,17 @@ class Loader(object):
|
||||
Pass in a function object returned from get_functions to load in
|
||||
introspection functions.
|
||||
'''
|
||||
funcs['sys.list_functions'] = lambda: self.list_funcs(funcs)
|
||||
funcs['sys.list_functions'] = lambda module='': self.list_funcs(funcs, module)
|
||||
funcs['sys.list_modules'] = lambda: self.list_modules(funcs)
|
||||
funcs['sys.doc'] = lambda module = '': self.get_docs(funcs, module)
|
||||
funcs['sys.reload_modules'] = lambda: True
|
||||
return funcs
|
||||
|
||||
def list_funcs(self, funcs):
|
||||
def list_funcs(self, funcs, module=''):
|
||||
'''
|
||||
List the functions
|
||||
List the functions. Optionally, specify a module to list from.
|
||||
'''
|
||||
return sorted(funcs)
|
||||
return sorted(f for f in funcs if f.startswith(module + '.')) if module else sorted(funcs)
|
||||
|
||||
def list_modules(self, funcs):
|
||||
'''
|
||||
|
@ -52,6 +52,11 @@ def is_logfile_configured():
|
||||
return __LOGFILE_CONFIGURED
|
||||
|
||||
|
||||
def is_logging_configured():
|
||||
global __CONSOLE_CONFIGURED, __LOGFILE_CONFIGURED
|
||||
return __CONSOLE_CONFIGURED or __LOGFILE_CONFIGURED
|
||||
|
||||
|
||||
class Logging(LoggingLoggerClass):
|
||||
def __new__(cls, logger_name, *args, **kwargs):
|
||||
global MAX_LOGGER_MODNAME_LENGTH
|
||||
|
252
salt/master.py
252
salt/master.py
@ -39,8 +39,11 @@ import salt.payload
|
||||
import salt.pillar
|
||||
import salt.state
|
||||
import salt.runner
|
||||
import salt.auth
|
||||
import salt.utils.atomicfile
|
||||
import salt.utils.event
|
||||
import salt.utils.verify
|
||||
import salt.utils.minions
|
||||
from salt.utils.debug import enable_sigusr1_handler
|
||||
|
||||
|
||||
@ -398,7 +401,6 @@ class ReqServer(object):
|
||||
self.publisher = Publisher(self.opts)
|
||||
self.publisher.start()
|
||||
|
||||
|
||||
def start_event_publisher(self):
|
||||
'''
|
||||
Start the salt publisher interface
|
||||
@ -524,6 +526,9 @@ class AESFuncs(object):
|
||||
self.event = salt.utils.event.MasterEvent(self.opts['sock_dir'])
|
||||
self.serial = salt.payload.Serial(opts)
|
||||
self.crypticle = crypticle
|
||||
self.ckminions = salt.utils.minions.CkMinions(opts)
|
||||
# Create the tops dict for loading external top data
|
||||
self.tops = salt.loader.tops(self.opts)
|
||||
# Make a client
|
||||
self.local = salt.client.LocalClient(self.opts['conf_file'])
|
||||
|
||||
@ -533,6 +538,8 @@ class AESFuncs(object):
|
||||
'''
|
||||
fnd = {'path': '',
|
||||
'rel': ''}
|
||||
if os.path.isabs(path):
|
||||
return fnd
|
||||
if env not in self.opts['file_roots']:
|
||||
return fnd
|
||||
for root in self.opts['file_roots'][env]:
|
||||
@ -582,33 +589,53 @@ class AESFuncs(object):
|
||||
if not 'id' in load:
|
||||
log.error('Received call for external nodes without an id')
|
||||
return {}
|
||||
if not self.opts['external_nodes']:
|
||||
return {}
|
||||
if not salt.utils.which(self.opts['external_nodes']):
|
||||
log.error(('Specified external nodes controller {0} is not'
|
||||
' available, please verify that it is installed'
|
||||
'').format(self.opts['external_nodes']))
|
||||
return {}
|
||||
cmd = '{0} {1}'.format(self.opts['external_nodes'], load['id'])
|
||||
ndata = yaml.safe_load(
|
||||
subprocess.Popen(
|
||||
cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE
|
||||
).communicate()[0])
|
||||
ret = {}
|
||||
if 'environment' in ndata:
|
||||
env = ndata['environment']
|
||||
else:
|
||||
env = 'base'
|
||||
|
||||
if 'classes' in ndata:
|
||||
if isinstance(ndata['classes'], dict):
|
||||
ret[env] = list(ndata['classes'])
|
||||
elif isinstance(ndata['classes'], list):
|
||||
ret[env] = ndata['classes']
|
||||
# The old ext_nodes method is set to be deprecated in 0.10.4
|
||||
# and should be removed within 3-5 releases in favor of the
|
||||
# "master_tops" system
|
||||
if self.opts['external_nodes']:
|
||||
if not salt.utils.which(self.opts['external_nodes']):
|
||||
log.error(('Specified external nodes controller {0} is not'
|
||||
' available, please verify that it is installed'
|
||||
'').format(self.opts['external_nodes']))
|
||||
return {}
|
||||
cmd = '{0} {1}'.format(self.opts['external_nodes'], load['id'])
|
||||
ndata = yaml.safe_load(
|
||||
subprocess.Popen(
|
||||
cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE
|
||||
).communicate()[0])
|
||||
if 'environment' in ndata:
|
||||
env = ndata['environment']
|
||||
else:
|
||||
return ret
|
||||
env = 'base'
|
||||
|
||||
if 'classes' in ndata:
|
||||
if isinstance(ndata['classes'], dict):
|
||||
ret[env] = list(ndata['classes'])
|
||||
elif isinstance(ndata['classes'], list):
|
||||
ret[env] = ndata['classes']
|
||||
else:
|
||||
return ret
|
||||
# Evaluate all configured master_tops interfaces
|
||||
|
||||
opts = {}
|
||||
grains = {}
|
||||
if 'opts' in load:
|
||||
opts = load['opts']
|
||||
if 'grains' in load['opts']:
|
||||
grains = load['opts']['grains']
|
||||
for fun in self.tops:
|
||||
try:
|
||||
ret.update(self.tops[fun](opts=opts, grains=grains))
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
('Top function {0} failed with error {1} for minion '
|
||||
'{2}').format(fun, exc, load['id'])
|
||||
)
|
||||
# If anything happens in the top generation, log it and move on
|
||||
pass
|
||||
return ret
|
||||
|
||||
def _serve_file(self, load):
|
||||
@ -742,7 +769,7 @@ class AESFuncs(object):
|
||||
'''
|
||||
if 'id' not in load or 'tag' not in load or 'data' not in load:
|
||||
return False
|
||||
tag = '{0}_{1}'.format(load['tag'], load['id'])
|
||||
tag = load['tag']
|
||||
return self.event.fire_event(load, tag)
|
||||
|
||||
def _return(self, load):
|
||||
@ -784,11 +811,24 @@ class AESFuncs(object):
|
||||
' attack').format(load['id'])
|
||||
)
|
||||
return False
|
||||
self.serial.dump(load['return'],
|
||||
open(os.path.join(hn_dir, 'return.p'), 'w+'))
|
||||
|
||||
self.serial.dump(
|
||||
load['return'],
|
||||
# Use atomic open here to avoid the file being read before it's
|
||||
# completely written to. Refs #1935
|
||||
salt.utils.atomicfile.atomic_open(
|
||||
os.path.join(hn_dir, 'return.p'), 'w+'
|
||||
)
|
||||
)
|
||||
if 'out' in load:
|
||||
self.serial.dump(load['out'],
|
||||
open(os.path.join(hn_dir, 'out.p'), 'w+'))
|
||||
self.serial.dump(
|
||||
load['out'],
|
||||
# Use atomic open here to avoid the file being read before
|
||||
# it's completely written to. Refs #1935
|
||||
salt.utils.atomicfile.atomic_open(
|
||||
os.path.join(hn_dir, 'out.p'), 'w+'
|
||||
)
|
||||
)
|
||||
|
||||
def _syndic_return(self, load):
|
||||
'''
|
||||
@ -873,7 +913,6 @@ class AESFuncs(object):
|
||||
runner = salt.runner.Runner(opts)
|
||||
return runner.run()
|
||||
|
||||
|
||||
def minion_publish(self, clear_load):
|
||||
'''
|
||||
Publish a command initiated from a minion, this method executes minion
|
||||
@ -915,13 +954,12 @@ class AESFuncs(object):
|
||||
clear_load['id'])
|
||||
log.warn(msg)
|
||||
return {}
|
||||
perms = set()
|
||||
perms = []
|
||||
for match in self.opts['peer']:
|
||||
if re.match(match, clear_load['id']):
|
||||
# This is the list of funcs/modules!
|
||||
if isinstance(self.opts['peer'][match], list):
|
||||
perms.update(self.opts['peer'][match])
|
||||
good = False
|
||||
perms.extend(self.opts['peer'][match])
|
||||
if ',' in clear_load['fun']:
|
||||
# 'arg': [['cat', '/proc/cpuinfo'], [], ['foo']]
|
||||
clear_load['fun'] = clear_load['fun'].split(',')
|
||||
@ -929,15 +967,11 @@ class AESFuncs(object):
|
||||
for arg in clear_load['arg']:
|
||||
arg_.append(arg.split())
|
||||
clear_load['arg'] = arg_
|
||||
for perm in perms:
|
||||
if isinstance(clear_load['fun'], list):
|
||||
good = True
|
||||
for fun in clear_load['fun']:
|
||||
if not re.match(perm, fun):
|
||||
good = False
|
||||
else:
|
||||
if re.match(perm, clear_load['fun']):
|
||||
good = True
|
||||
good = self.ckminions.auth_check(
|
||||
perms,
|
||||
clear_load['fun'],
|
||||
clear_load['tgt'],
|
||||
clear_load.get('tgt_type', 'glob'))
|
||||
if not good:
|
||||
return {}
|
||||
# Set up the publication payload
|
||||
@ -1001,7 +1035,7 @@ class AESFuncs(object):
|
||||
if ret_form == 'clean':
|
||||
return self.local.get_returns(
|
||||
jid,
|
||||
self.local.check_minions(
|
||||
self.ckminions.check_minions(
|
||||
clear_load['tgt'],
|
||||
expr_form
|
||||
),
|
||||
@ -1010,7 +1044,7 @@ class AESFuncs(object):
|
||||
elif ret_form == 'full':
|
||||
ret = self.local.get_full_returns(
|
||||
jid,
|
||||
self.local.check_minions(
|
||||
self.ckminions.check_minions(
|
||||
clear_load['tgt'],
|
||||
expr_form
|
||||
),
|
||||
@ -1037,6 +1071,27 @@ class AESFuncs(object):
|
||||
# (we don't care about the return value, so why encrypt it?)
|
||||
if func == '_return':
|
||||
return ret
|
||||
if func == '_pillar' and 'id' in load:
|
||||
if not load.get('ver') == '2' and self.opts['pillar_version'] == 1:
|
||||
# Authorized to return old pillar proto
|
||||
return self.crypticle.dumps(ret)
|
||||
# encrypt with a specific aes key
|
||||
pubfn = os.path.join(self.opts['pki_dir'],
|
||||
'minions',
|
||||
load['id'])
|
||||
key = salt.crypt.Crypticle.generate_key_string()
|
||||
pcrypt = salt.crypt.Crypticle(
|
||||
self.opts,
|
||||
key)
|
||||
try:
|
||||
pub = RSA.load_pub_key(pubfn)
|
||||
except RSA.RSAError, e:
|
||||
return self.crypticle.dumps({})
|
||||
|
||||
pret = {}
|
||||
pret['key'] = pub.public_encrypt(key, 4)
|
||||
pret['pillar'] = pcrypt.dumps(ret)
|
||||
return pret
|
||||
# AES Encrypt the return
|
||||
return self.crypticle.dumps(ret)
|
||||
|
||||
@ -1060,6 +1115,10 @@ class ClearFuncs(object):
|
||||
self.event = salt.utils.event.MasterEvent(self.opts['sock_dir'])
|
||||
# Make a client
|
||||
self.local = salt.client.LocalClient(self.opts['conf_file'])
|
||||
# Make an minion checker object
|
||||
self.ckminions = salt.utils.minions.CkMinions(opts)
|
||||
# Make an Auth object
|
||||
self.loadauth = salt.auth.LoadAuth(opts)
|
||||
|
||||
def _send_cluster(self):
|
||||
'''
|
||||
@ -1214,6 +1273,16 @@ class ClearFuncs(object):
|
||||
# open mode is turned on, nuts to checks and overwrite whatever
|
||||
# is there
|
||||
pass
|
||||
elif os.path.isfile(pubfn_rejected):
|
||||
# The key has been rejected, don't place it in pending
|
||||
log.info('Public key rejected for {id}'.format(**load))
|
||||
ret = {'enc': 'clear',
|
||||
'load': {'ret': False}}
|
||||
eload = {'result': False,
|
||||
'id': load['id'],
|
||||
'pub': load['pub']}
|
||||
self.event.fire_event(eload, 'auth')
|
||||
return ret
|
||||
elif os.path.isfile(pubfn):
|
||||
# The key has been accepted check it
|
||||
if not open(pubfn, 'r').read() == load['pub']:
|
||||
@ -1229,16 +1298,6 @@ class ClearFuncs(object):
|
||||
'pub': load['pub']}
|
||||
self.event.fire_event(eload, 'auth')
|
||||
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}'.format(**load))
|
||||
ret = {'enc': 'clear',
|
||||
'load': {'ret': False}}
|
||||
eload = {'result': False,
|
||||
'id': load['id'],
|
||||
'pub': load['pub']}
|
||||
self.event.fire_event(eload, 'auth')
|
||||
return ret
|
||||
elif not os.path.isfile(pubfn_pend)\
|
||||
and not self._check_autosign(load['id']):
|
||||
# This is a new key, stick it in pre
|
||||
@ -1331,6 +1390,15 @@ class ClearFuncs(object):
|
||||
'token': self.master_key.token,
|
||||
'publish_port': self.opts['publish_port'],
|
||||
}
|
||||
if 'token' in load:
|
||||
try:
|
||||
mtoken = self.master_key.key.private_decrypt(load['token'], 4)
|
||||
ret['token'] = pub.public_encrypt(mtoken, 4)
|
||||
except Exception:
|
||||
# Token failed to decrypt, send back the salty bacon to
|
||||
# support older minions
|
||||
pass
|
||||
|
||||
ret['aes'] = pub.public_encrypt(self.opts['aes'], 4)
|
||||
eload = {'result': True,
|
||||
'act': 'accept',
|
||||
@ -1339,13 +1407,64 @@ class ClearFuncs(object):
|
||||
self.event.fire_event(eload, 'auth')
|
||||
return ret
|
||||
|
||||
def mk_token(self, clear_load):
|
||||
if not 'eauth' in clear_load:
|
||||
return ''
|
||||
if not clear_load['eauth'] in self.opts['external_auth']:
|
||||
# The eauth system is not enabled, fail
|
||||
return ''
|
||||
name = self.loadauth.load_name(clear_load)
|
||||
if not name in self.opts['external_auth'][clear_load['eauth']]:
|
||||
return ''
|
||||
if not self.loadauth.time_auth(clear_load):
|
||||
return ''
|
||||
return self.loadauth.mk_token(clear_load)
|
||||
|
||||
def publish(self, clear_load):
|
||||
'''
|
||||
This method sends out publications to the minions, it can only be used
|
||||
by the LocalClient.
|
||||
'''
|
||||
extra = clear_load.get('kwargs', {})
|
||||
# Check for external auth calls
|
||||
if extra.get('token', False):
|
||||
# A token was passwd, check it
|
||||
token = self.loadauth.get_tok(extra['token'])
|
||||
if not token:
|
||||
return ''
|
||||
if not token['eauth'] in self.opts['external_auth']:
|
||||
return ''
|
||||
if not token['name'] in self.opts['external_auth'][token['eauth']]:
|
||||
return ''
|
||||
good = self.ckminions.auth_check(
|
||||
self.opts['external_auth'][token['eauth']][token['name']],
|
||||
clear_load['fun'],
|
||||
clear_load['tgt'],
|
||||
clear_load.get('tgt_type', 'glob'))
|
||||
if not good:
|
||||
# Accept find_job so the cli will function cleanly
|
||||
if not clear_load['fun'] == 'saltutil.find_job':
|
||||
return ''
|
||||
elif 'eauth' in extra:
|
||||
if not extra['eauth'] in self.opts['external_auth']:
|
||||
# The eauth system is not enabled, fail
|
||||
return ''
|
||||
name = self.loadauth.load_name(extra)
|
||||
if not name in self.opts['external_auth'][extra['eauth']]:
|
||||
return ''
|
||||
if not self.loadauth.time_auth(extra):
|
||||
return ''
|
||||
good = self.ckminions.auth_check(
|
||||
self.opts['external_auth'][extra['eauth']][name],
|
||||
clear_load['fun'],
|
||||
clear_load['tgt'],
|
||||
clear_load.get('tgt_type', 'glob'))
|
||||
if not good:
|
||||
# Accept find_job so the cli will function cleanly
|
||||
if not clear_load['fun'] == 'saltutil.find_job':
|
||||
return ''
|
||||
# Verify that the caller has root on master
|
||||
if 'user' in clear_load:
|
||||
elif 'user' in clear_load:
|
||||
if clear_load['user'].startswith('sudo_'):
|
||||
if not clear_load.pop('key') == self.key.get(getpass.getuser(), ''):
|
||||
return ''
|
||||
@ -1361,14 +1480,15 @@ class ClearFuncs(object):
|
||||
if not clear_load.pop('key') == self.key[clear_load['user']]:
|
||||
return ''
|
||||
good = False
|
||||
for user in self.opts['client_acl']:
|
||||
if clear_load['user'] != user:
|
||||
continue
|
||||
for regex in self.opts['client_acl'][user]:
|
||||
if re.match(regex, clear_load['fun']):
|
||||
good = True
|
||||
good = self.ckminions.auth_check(
|
||||
self.opts['client_acl'],
|
||||
clear_load['fun'],
|
||||
clear_load['tgt'],
|
||||
clear_load.get('tgt_type', 'glob'))
|
||||
if not good:
|
||||
return ''
|
||||
# Accept find_job so the cli will function cleanly
|
||||
if not clear_load['fun'] == 'saltutil.find_job':
|
||||
return ''
|
||||
else:
|
||||
return ''
|
||||
else:
|
||||
@ -1433,5 +1553,7 @@ class ClearFuncs(object):
|
||||
)
|
||||
pub_sock.connect(pull_uri)
|
||||
pub_sock.send(self.serial.dumps(payload))
|
||||
minions = self.ckminions.check_minions(load['tgt'], load.get('tgt_type', 'glob'))
|
||||
return {'enc': 'clear',
|
||||
'load': {'jid': clear_load['jid']}}
|
||||
'load': {'jid': clear_load['jid'],
|
||||
'minions': minions}}
|
||||
|
@ -99,6 +99,10 @@ class SMinion(object):
|
||||
def __init__(self, opts):
|
||||
# Generate all of the minion side components
|
||||
self.opts = opts
|
||||
# Late setup the of the opts grains, so we can log from the grains
|
||||
# module
|
||||
opts['grains'] = salt.loader.grains(opts)
|
||||
self.opts = opts
|
||||
self.gen_modules()
|
||||
|
||||
def gen_modules(self):
|
||||
@ -128,6 +132,9 @@ class Minion(object):
|
||||
'''
|
||||
Pass in the options dict
|
||||
'''
|
||||
# Late setup the of the opts grains, so we can log from the grains
|
||||
# module
|
||||
opts['grains'] = salt.loader.grains(opts)
|
||||
self.opts = opts
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.mod_opts = self.__prep_mod_opts()
|
||||
@ -163,6 +170,20 @@ class Minion(object):
|
||||
returners = salt.loader.returners(self.opts, functions)
|
||||
return functions, returners
|
||||
|
||||
def _fire_master(self, data, tag):
|
||||
'''
|
||||
Fire an event on the master
|
||||
'''
|
||||
load = {'id': self.opts['id'],
|
||||
'tag': tag,
|
||||
'data': data,
|
||||
'cmd': '_minion_event'}
|
||||
sreq = salt.payload.SREQ(self.opts['master_uri'])
|
||||
try:
|
||||
sreq.send('aes', self.crypticle.dumps(load))
|
||||
except:
|
||||
pass
|
||||
|
||||
def _handle_payload(self, payload):
|
||||
'''
|
||||
Takes a payload from the master publisher and does whatever the
|
||||
@ -570,6 +591,14 @@ class Minion(object):
|
||||
socket.connect(self.master_pub)
|
||||
poller.register(socket, zmq.POLLIN)
|
||||
epoller.register(epull_sock, zmq.POLLIN)
|
||||
# Send an event to the master that the minion is live
|
||||
self._fire_master(
|
||||
'Minion {0} started at {1}'.format(
|
||||
self.opts['id'],
|
||||
time.asctime()
|
||||
),
|
||||
'minion_start'
|
||||
)
|
||||
|
||||
# Make sure to gracefully handle SIGUSR1
|
||||
enable_sigusr1_handler()
|
||||
@ -724,10 +753,9 @@ class Matcher(object):
|
||||
'''
|
||||
def __init__(self, opts, functions=None):
|
||||
self.opts = opts
|
||||
if not functions:
|
||||
if functions is None:
|
||||
functions = salt.loader.minion_mods(self.opts)
|
||||
else:
|
||||
self.functions = functions
|
||||
self.functions = functions
|
||||
|
||||
def confirm_top(self, match, data, nodegroups=None):
|
||||
'''
|
||||
@ -850,6 +878,25 @@ class Matcher(object):
|
||||
comps[1].lower(),
|
||||
))
|
||||
|
||||
def ipcidr_match(self, tgt):
|
||||
'''
|
||||
Matches based on ip address or CIDR notation
|
||||
'''
|
||||
num_parts = len(tgt.split('/'))
|
||||
if num_parts > 2:
|
||||
return False
|
||||
elif num_parts == 2:
|
||||
return self.functions['network.in_subnet'](tgt)
|
||||
else:
|
||||
import socket
|
||||
try:
|
||||
socket.inet_aton(tgt)
|
||||
except socket.error:
|
||||
# Not a valid IPv4 address
|
||||
return False
|
||||
else:
|
||||
return tgt in self.functions['network.ip_addrs']()
|
||||
|
||||
def compound_match(self, tgt):
|
||||
'''
|
||||
Runs the compound target check
|
||||
@ -862,12 +909,13 @@ class Matcher(object):
|
||||
'X': 'exsel',
|
||||
'I': 'pillar',
|
||||
'L': 'list',
|
||||
'S': 'ipcidr',
|
||||
'E': 'pcre'}
|
||||
results = []
|
||||
opers = ['and', 'or', 'not']
|
||||
for match in tgt.split():
|
||||
# Try to match tokens from the compound target, first by using
|
||||
# the 'G, X, I, L, E' matcher types, then by hostname glob.
|
||||
# the 'G, X, I, L, S, E' matcher types, then by hostname glob.
|
||||
if '@' in match and match[1] == '@':
|
||||
comps = match.split('@')
|
||||
matcher = ref.get(comps[0])
|
||||
@ -895,5 +943,7 @@ class Matcher(object):
|
||||
matcher is used in states
|
||||
'''
|
||||
if tgt in nodegroups:
|
||||
return self.compound_match(nodegroups[tgt])
|
||||
return self.compound_match(
|
||||
salt.utils.nodegroup_comp(tgt, nodegroups)
|
||||
)
|
||||
return False
|
||||
|
@ -13,7 +13,6 @@ def __virtual__():
|
||||
'''
|
||||
Confirm this module is on a Debian based system
|
||||
'''
|
||||
|
||||
return 'pkg' if __grains__['os'] in ('Debian', 'Ubuntu') else False
|
||||
|
||||
|
||||
@ -283,7 +282,7 @@ def list_pkgs(regex_string=""):
|
||||
|
||||
for line in out.split('\n'):
|
||||
cols = line.split()
|
||||
if len(cols) and 'install' in cols[0] and 'installed' in cols[2]:
|
||||
if len(cols) and ('install' in cols[0] or 'hold' in cols[0]) and 'installed' in cols[2]:
|
||||
ret[cols[3]] = cols[4]
|
||||
|
||||
# If ret is empty at this point, check to see if the package is virtual.
|
||||
|
@ -11,7 +11,7 @@ REQUIREMENT 2:
|
||||
Add the following values in /etc/salt/minion for the
|
||||
CA module to function properly::
|
||||
|
||||
ca.cert_base_path: '/etc/pki/koji'
|
||||
ca.cert_base_path: '/etc/pki'
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
@ -167,7 +167,7 @@ def create_ca(
|
||||
organization, default is "Salt Stack"
|
||||
OU
|
||||
organizational unit, default is None
|
||||
email
|
||||
emailAddress
|
||||
email address for the CA owner, default is 'xyz@pdq.net'
|
||||
|
||||
Writes out a CA certificate based upon defined config values. If the file
|
||||
@ -205,7 +205,7 @@ def create_ca(
|
||||
ca.get_subject().emailAddress = emailAddress
|
||||
|
||||
ca.gmtime_adj_notBefore(0)
|
||||
ca.gmtime_adj_notAfter(days * 24 * 60 * 60)
|
||||
ca.gmtime_adj_notAfter(int(days) * 24 * 60 * 60)
|
||||
ca.set_issuer(ca.get_subject())
|
||||
ca.set_pubkey(key)
|
||||
|
||||
@ -441,7 +441,7 @@ def create_ca_signed_cert(ca_name, CN, days=365):
|
||||
cert = OpenSSL.crypto.X509()
|
||||
cert.set_subject(req.get_subject())
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(days * 24 * 60 * 60)
|
||||
cert.gmtime_adj_notAfter(int(days) * 24 * 60 * 60)
|
||||
cert.set_serial_number(_new_serial(ca_name, CN))
|
||||
cert.set_issuer(ca_cert.get_subject())
|
||||
cert.set_pubkey(req.get_pubkey())
|
||||
|
@ -12,6 +12,7 @@ import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
@ -35,6 +36,7 @@ __outputter__ = {
|
||||
|
||||
DEFAULT_SHELL = shell_grain()['shell']
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Overwriting the cmd python module makes debugging modules
|
||||
@ -43,6 +45,48 @@ def __virtual__():
|
||||
return 'cmd'
|
||||
|
||||
|
||||
def _chugid(runas):
|
||||
uinfo = pwd.getpwnam(runas)
|
||||
|
||||
if os.getuid() == uinfo.pw_uid and os.getgid() == uinfo.pw_gid:
|
||||
# No need to change user or group
|
||||
return
|
||||
|
||||
# No logging can happen on this function
|
||||
#
|
||||
# 08:46:32,161 [salt.loaded.int.module.cmdmod:276 ][DEBUG ] stderr: Traceback (most recent call last):
|
||||
# File "/usr/lib/python2.7/logging/__init__.py", line 870, in emit
|
||||
# self.flush()
|
||||
# File "/usr/lib/python2.7/logging/__init__.py", line 832, in flush
|
||||
# self.stream.flush()
|
||||
# IOError: [Errno 9] Bad file descriptor
|
||||
# Logged from file cmdmod.py, line 59
|
||||
# 08:46:17,481 [salt.loaded.int.module.cmdmod:59 ][DEBUG ] Switching user 0 -> 1008 and group 0 -> 1012 if needed
|
||||
#
|
||||
# apparently because we closed fd's on Popen, though if not closed, output
|
||||
# would also go to it's stderr
|
||||
|
||||
if os.getgid() != uinfo.pw_gid:
|
||||
try:
|
||||
os.setgid(uinfo.pw_gid)
|
||||
except OSError, err:
|
||||
raise CommandExecutionError(
|
||||
'Failed to change from gid {0} to {1}. Error: {2}'.format(
|
||||
os.getgid(), uinfo.pw_gid, err
|
||||
)
|
||||
)
|
||||
|
||||
if os.getuid() != uinfo.pw_uid:
|
||||
try:
|
||||
os.setuid(uinfo.pw_uid)
|
||||
except OSError, err:
|
||||
raise CommandExecutionError(
|
||||
'Failed to change from uid {0} to {1}. Error: {2}'.format(
|
||||
os.getuid(), uinfo.pw_uid, err
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _run(cmd,
|
||||
cwd=None,
|
||||
stdout=subprocess.PIPE,
|
||||
@ -87,30 +131,19 @@ def _run(cmd,
|
||||
|
||||
if runas:
|
||||
# Save the original command before munging it
|
||||
orig_cmd = cmd
|
||||
try:
|
||||
pwd.getpwnam(runas)
|
||||
except KeyError:
|
||||
msg = 'User \'{0}\' is not available'.format(runas)
|
||||
raise CommandExecutionError(msg)
|
||||
|
||||
cmd_prefix = 'su -s {0}'.format(shell)
|
||||
|
||||
# Load the 'nix environment
|
||||
if with_env:
|
||||
cmd_prefix += ' -'
|
||||
cmd = 'cd {0} && {1}'.format(cwd, cmd)
|
||||
|
||||
cmd_prefix += ' {0} -c'.format(runas)
|
||||
cmd = '{0} {1}'.format(cmd_prefix, pipes.quote(cmd))
|
||||
|
||||
if not quiet:
|
||||
# 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))
|
||||
log.info(
|
||||
'Executing command {0!r} {1}in directory {2!r}'.format(
|
||||
cmd, 'as user {0!r} '.format(runas) if runas else '', cwd
|
||||
)
|
||||
)
|
||||
|
||||
run_env = os.environ
|
||||
run_env.update(env)
|
||||
@ -118,29 +151,25 @@ def _run(cmd,
|
||||
'shell': True,
|
||||
'env': run_env,
|
||||
'stdout': stdout,
|
||||
'stderr':stderr}
|
||||
'stderr': stderr}
|
||||
|
||||
if runas:
|
||||
kwargs['preexec_fn'] = partial(_chugid, runas)
|
||||
|
||||
if not sys.platform.startswith('win'):
|
||||
# close_fds is not supported on Windows platforms if you redirect
|
||||
# stdin/stdout/stderr
|
||||
kwargs['executable'] = shell
|
||||
kwargs['close_fds'] = True
|
||||
|
||||
# If all we want is the return code then don't block on gathering input.
|
||||
if retcode:
|
||||
kwargs['stdout'] = None
|
||||
kwargs['stderr'] = None
|
||||
|
||||
# This is where the magic happens
|
||||
proc = subprocess.Popen(cmd, **kwargs)
|
||||
|
||||
# If all we want is the return code then don't block on gathering input,
|
||||
# this is used to bypass ampersand issues with background processes in
|
||||
# scripts
|
||||
if retcode:
|
||||
while True:
|
||||
retcode = proc.poll()
|
||||
if retcode is None:
|
||||
continue
|
||||
else:
|
||||
out = ''
|
||||
err = ''
|
||||
break
|
||||
else:
|
||||
out, err = proc.communicate()
|
||||
out, err = proc.communicate()
|
||||
|
||||
if rstrip:
|
||||
if out is not None:
|
||||
@ -164,6 +193,14 @@ def _run_quiet(cmd, cwd=None, runas=None, shell=DEFAULT_SHELL, env=()):
|
||||
quiet=True, shell=shell, env=env)['stdout']
|
||||
|
||||
|
||||
def _run_all_quiet(cmd, cwd=None, runas=None, shell=DEFAULT_SHELL, env=()):
|
||||
'''
|
||||
Helper for running commands quietly for minion startup.
|
||||
Returns a dict of return data
|
||||
'''
|
||||
return _run(cmd, runas=runas, cwd=cwd, shell=shell, env=env, quiet=True)
|
||||
|
||||
|
||||
def run(cmd, cwd=None, runas=None, shell=DEFAULT_SHELL, env=()):
|
||||
'''
|
||||
Execute the passed command and return the output as a string
|
||||
@ -213,6 +250,7 @@ def run_all(cmd, cwd=None, runas=None, shell=DEFAULT_SHELL, env=()):
|
||||
salt '*' cmd.run_all "ls -l | awk '/foo/{print $2}'"
|
||||
'''
|
||||
ret = _run(cmd, runas=runas, cwd=cwd, shell=shell, env=env)
|
||||
|
||||
if ret['retcode'] != 0:
|
||||
rcode = ret['retcode']
|
||||
msg = 'Command \'{0}\' failed with return code: {1}'
|
||||
|
32
salt/modules/config.py
Normal file
32
salt/modules/config.py
Normal file
@ -0,0 +1,32 @@
|
||||
'''
|
||||
Return config information
|
||||
'''
|
||||
|
||||
def backup_mode(backup=''):
|
||||
'''
|
||||
Return the backup mode
|
||||
'''
|
||||
if backup:
|
||||
return backup
|
||||
if 'backup_mode' in __opts__:
|
||||
return __opts__['backup_mode']
|
||||
if 'master.backup_mode' in __pillar__:
|
||||
return __pillar__['master.backup_mode']
|
||||
id_conf = 'master.{0}.backup_mode'.format(__grains__['id'])
|
||||
if id_conf in __pillar__:
|
||||
return __pillar__[id_conf]
|
||||
|
||||
|
||||
def manage_mode(mode):
|
||||
'''
|
||||
Return a mode value, normalized to a string
|
||||
'''
|
||||
if mode:
|
||||
mode = str(mode).lstrip('0')
|
||||
if not mode:
|
||||
return '0'
|
||||
else:
|
||||
return mode
|
||||
return mode
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user