mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '0.9.7'
This commit is contained in:
commit
1d1ab88d03
133 changed files with 5536 additions and 2365 deletions
|
@ -31,11 +31,15 @@
|
|||
# Set the number of hours to keep old job information
|
||||
#keep_jobs: 24
|
||||
|
||||
# Set the default timeout for the salt command and api, the default is 5
|
||||
# seconds
|
||||
#timeout: 5
|
||||
|
||||
# Set the directory used to hold unix sockets
|
||||
#sock_dir: /tmp/salt-unix
|
||||
|
||||
# Set the acceptance level for serialization of messages. This should only be
|
||||
# set if the master is newer that 0.9.5 and the minion are older, this option
|
||||
# set if the master is newer than 0.9.5 and the minion are older. This option
|
||||
# allows a 0.9.5 and newer master to communicate with minions 0.9.4 and
|
||||
# earlier. It is not recommended to keep this setting on if the minions are
|
||||
# all 0.9.5 or higher, as leaving pickle as the serialization medium is slow
|
||||
|
@ -59,9 +63,15 @@
|
|||
##########################################
|
||||
# The state system uses a "top" file to tell the minions what environment to
|
||||
# use and what modules to use. The state_top file is defined relative to the
|
||||
# root of the base environment
|
||||
# root of the base environment.
|
||||
#state_top: top.sls
|
||||
#
|
||||
# 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
|
||||
# files and combine the results if both are enabled!
|
||||
#external_nodes: None
|
||||
#
|
||||
# The renderer to use on the minions to render the state data
|
||||
#renderer: yaml_jinja
|
||||
#
|
||||
|
@ -78,7 +88,7 @@
|
|||
# The file server works on environments passed to the master, each environment
|
||||
# can have multiple root directories, the subdirectories in the multiple file
|
||||
# roots cannot match, otherwise the downloaded files will not be able to be
|
||||
# reliably ensured. A base environment is required to house the top file
|
||||
# reliably ensured. A base environment is required to house the top file.
|
||||
# Example:
|
||||
# file_roots:
|
||||
# base:
|
||||
|
@ -110,15 +120,14 @@
|
|||
# syndic servers(s) below it set the "order_masters" setting to True, if this
|
||||
# is a master that will be running a syndic daemon for passthrough the
|
||||
# "syndic_master" setting needs to be set to the location of the master server
|
||||
# to recieve commands from
|
||||
# to recieve commands from.
|
||||
#
|
||||
# Set the order_masters setting to True if this master will command lower
|
||||
# masters' syndic interfaces
|
||||
# masters' syndic interfaces.
|
||||
#order_masters: False
|
||||
#
|
||||
# If this master will be running a salt syndic daemon, then the syndic needs
|
||||
# to know where the master it is recieving commands from is, set it with the
|
||||
# syndic_master value
|
||||
# If this master will be running a salt syndic daemon, syndic_master tells
|
||||
# this master where to recieve commands from.
|
||||
#syndic_master: masterofmaster
|
||||
|
||||
##### Peer Publish settings #####
|
||||
|
@ -129,9 +138,9 @@
|
|||
# compartmentalization of commands based on individual minions.
|
||||
#
|
||||
# The configuration uses regular expressions to match minions and then a list
|
||||
# of regular expressions to match functions, the following will allow the
|
||||
# of regular expressions to match functions. The following will allow the
|
||||
# minion authenticated as foo.example.com to execute functions from the test
|
||||
# and pkg modules
|
||||
# and pkg modules.
|
||||
# peer:
|
||||
# foo.example.com:
|
||||
# - test.*
|
||||
|
@ -149,7 +158,7 @@
|
|||
##########################################
|
||||
# Salt supports automatic clustering, salt creates a single ip address which
|
||||
# is shared among the individual salt components using ucarp. The private key
|
||||
# and all of the minion keys are maintained across the defined cluster masters
|
||||
# and all of the minion keys are maintained across the defined cluster masters.
|
||||
# The failover service is automatically managed via these settings
|
||||
|
||||
# List the identifiers for the other cluster masters in this manner:
|
||||
|
@ -168,14 +177,15 @@
|
|||
##########################################
|
||||
# The location of the master log file
|
||||
#log_file: /var/log/salt/master
|
||||
#
|
||||
# The level of messages to send to the log file.
|
||||
# One of 'info', 'quiet', 'critical', 'error', 'debug', 'warning'.
|
||||
# Default: 'warning'
|
||||
#log_level: warning
|
||||
#
|
||||
# Logger levels can be used to tweak specific loggers logging levels.
|
||||
# Imagine you want to have the salt library at the 'warning' level, but, you
|
||||
# still wish to have 'salt.modules' at the 'debug' level:
|
||||
# For example, if you want to have the salt library at the 'warning' level,
|
||||
# but you still wish to have 'salt.modules' at the 'debug' level:
|
||||
# log_granular_levels:
|
||||
# 'salt': 'warning',
|
||||
# 'salt.modules': 'debug'
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
##### Primary configuration settings #####
|
||||
##########################################
|
||||
# Set the location of the salt master server, if the master server cannot be
|
||||
# resolved, then the minion will fail to start
|
||||
# resolved, then the minion will fail to start.
|
||||
#master: salt
|
||||
|
||||
# Set the post used by the master reply and authentication server
|
||||
# Set the port used by the master reply and authentication server
|
||||
#master_port: 4506
|
||||
|
||||
# The user to run salt
|
||||
|
@ -24,18 +24,19 @@
|
|||
# clusters.
|
||||
#id:
|
||||
|
||||
# The minion connection to the master may be inturupted, the minion will
|
||||
# verify the connection every so many seconds, to disable connection
|
||||
# verification set this value to 0
|
||||
# If the the connection to the server is interrupted, the minion will
|
||||
# attempt to reconnect. sub_timeout allows you to control the rate
|
||||
# of reconnection attempts (in seconds). To disable reconnects, set
|
||||
# this value to 0.
|
||||
#sub_timeout: 60
|
||||
|
||||
# Where cache data goes
|
||||
#cachedir: /var/cache/salt
|
||||
|
||||
# The minion can locally cache the return data from jobs sent to it, this
|
||||
# can be a good way to keep track minion side of the jobs the minion has
|
||||
# executed. By default this feature is disabled, to enable set cache_jobs
|
||||
# to True
|
||||
# can be a good way to keep track of jobs the minion has executed
|
||||
# (on the minion side). By default this feature is disabled, to enable
|
||||
# set cache_jobs to True
|
||||
#cache_jobs: False
|
||||
|
||||
# When waiting for a master to accept the minion's public key, salt will
|
||||
|
@ -47,18 +48,20 @@
|
|||
|
||||
##### Minion module management #####
|
||||
##########################################
|
||||
# Disable specific modules, this will allow the admin to limit the level os
|
||||
# Disable specific modules. This allows the admin to limit the level of
|
||||
# access the master has to the minion
|
||||
#disable_modules: [cmd,test]
|
||||
#disable_returners: []
|
||||
# Modules can be loaded from arbitrary paths, this enables the easy deployment
|
||||
# of third party modules, modules for returners and minions can be loaded.
|
||||
#
|
||||
# Modules can be loaded from arbitrary paths. This enables the easy deployment
|
||||
# of third party modules. Modules for returners and minions can be loaded.
|
||||
# Specify a list of extra directories to search for minion modules and
|
||||
# returners. These paths must be fully qualified!
|
||||
#module_dirs: []
|
||||
#returner_dirs: []
|
||||
#states_dirs: []
|
||||
#render_dirs: []
|
||||
#
|
||||
# Enable Cython modules searching and loading. (Default: False)
|
||||
#cython_enable: False
|
||||
|
||||
|
@ -92,6 +95,12 @@
|
|||
# not on the master it will be deleted from the minion. By default this is
|
||||
# enabled and can be disabled by changing this value to False
|
||||
#clean_dynamic_modules: True
|
||||
#
|
||||
# Normally the minion is not isolated to any single environment on the master
|
||||
# when running states, but the environment can be isolated on the minion side
|
||||
# by statically setting it. Remember that the recommended way to manage
|
||||
# environments is to issolate via the top file.
|
||||
#environment: None
|
||||
|
||||
###### Security settings #####
|
||||
###########################################
|
||||
|
@ -112,14 +121,15 @@
|
|||
###########################################
|
||||
# The location of the minion log file
|
||||
#log_file: /var/log/salt/minion
|
||||
#
|
||||
# The level of messages to send to the log file.
|
||||
# One of 'info', 'quiet', 'critical', 'error', 'debug', 'warning'.
|
||||
# Default: 'warning'
|
||||
#log_level: warning
|
||||
#
|
||||
# Logger levels can be used to tweak specific loggers logging levels.
|
||||
# Imagine you want to have the salt library at the 'warning' level, but, you
|
||||
# still wish to have 'salt.modules' at the 'debug' level:
|
||||
# For example, if you want to have the salt library at the 'warning' level,
|
||||
# but you still wish to have 'salt.modules' at the 'debug' level:
|
||||
# log_granular_levels: {
|
||||
# 'salt': 'warning',
|
||||
# 'salt.modules': 'debug'
|
||||
|
@ -133,7 +143,7 @@
|
|||
# passed here in valid yaml format will be passed on to the salt minion modules
|
||||
# for use. It is STRONGLY recommended that a naming convention be used in which
|
||||
# the module name is followed by a . and then the value. Also, all top level
|
||||
# data must be allied via the yaml dict construct, some examples:
|
||||
# data must be applied via the yaml dict construct, some examples:
|
||||
#
|
||||
# A simple value for the test module:
|
||||
#test.foo: foo
|
||||
|
|
18
debian/changelog
vendored
18
debian/changelog
vendored
|
@ -1,3 +1,21 @@
|
|||
salt (0.9.7~pre2-0ppa1) lucid; urgency=low
|
||||
|
||||
* Fix arch and deps issue
|
||||
|
||||
-- Corey Quinn <corey@sequestered.net> Wed, 08 Feb 2012 17:22:47 -0800
|
||||
|
||||
salt (0.9.7~pre1-0ppa1) lucid; urgency=low
|
||||
|
||||
* Version bump, fixed a few issues
|
||||
|
||||
-- Corey Quinn <corey@sequestered.net> Wed, 08 Feb 2012 16:35:59 -0800
|
||||
|
||||
salt (0.9.6-1) lucid; urgency=low
|
||||
|
||||
* Bump version; time to upgrade
|
||||
|
||||
-- Corey Quinn <corey@sequestered.net> Tue, 07 Feb 2012 18:15:20 -0800
|
||||
|
||||
salt (0.9.5-1) unstable; urgency=low
|
||||
|
||||
* First package release. (Closes: #643789)
|
||||
|
|
12
debian/control
vendored
12
debian/control
vendored
|
@ -3,7 +3,6 @@ Section: admin
|
|||
Priority: optional
|
||||
Maintainer: Corey Quinn <corey@sequestered.net>
|
||||
Build-Depends: debhelper (>= 7.0.50~),
|
||||
python-support,
|
||||
cython,
|
||||
python-yaml,
|
||||
python-setuptools,
|
||||
|
@ -11,7 +10,6 @@ Build-Depends: debhelper (>= 7.0.50~),
|
|||
python-m2crypto,
|
||||
python-zmq (>= 2.1.9),
|
||||
libzmq-dev (>= 2.1.9),
|
||||
python-all-dev,
|
||||
python-jinja2
|
||||
Standards-Version: 3.9.2
|
||||
Homepage: http://saltstack.org
|
||||
|
@ -20,21 +18,17 @@ Homepage: http://saltstack.org
|
|||
|
||||
|
||||
Package: salt-common
|
||||
Architecture: any
|
||||
Architecture: all
|
||||
Depends: ${python:Depends},
|
||||
${misc:Depends},
|
||||
${shlibs:Depends},
|
||||
python-support,
|
||||
cython,
|
||||
python-setuptools,
|
||||
python-yaml,
|
||||
python-crypto,
|
||||
python-m2crypto,
|
||||
python-zmq (>= 2.1.9),
|
||||
libzmq-dev (>= 2.1.9),
|
||||
python,
|
||||
python-dev,
|
||||
python-jinja2
|
||||
python-jinja2,
|
||||
msgpack-python
|
||||
Description: Shared libraries that salt requires for all packages
|
||||
This package is a powerful remote execution manager that can be used
|
||||
to administer servers in a fast and efficient way.
|
||||
|
|
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.5.tar.gz
|
||||
Source: https://github.com/downloads/saltstack/salt/salt-0.9.7.tar.gz
|
||||
|
||||
Files: *
|
||||
Copyright: 2012 Thomas S Hatch <thatch45@gmail.com>
|
||||
|
|
15
debian/rules
vendored
15
debian/rules
vendored
|
@ -1,14 +1,11 @@
|
|||
#!/usr/bin/make -f
|
||||
#export DH_VERBOSE=1
|
||||
%:
|
||||
dh $@ --buildsystem=python_distutils
|
||||
#override_dh_installinit:
|
||||
# dh_installinit --no-start --name="salt-master"
|
||||
# dh_installinit --no-start --name="salt-minion"
|
||||
# dh_installinit --no-start --name="salt-syndic"
|
||||
|
||||
dh $@ #--with python2
|
||||
dh_override_auto_build:
|
||||
python setup.py build #--install-layout=deb build
|
||||
get-orig-source:
|
||||
git clone https://github.com/saltstack/salt.git
|
||||
mv salt salt-0.9.5
|
||||
tar -zcvf salt_0.9.5.orig.tar.gz --exclude "debian*" --exclude-vcs salt-0.9.5
|
||||
rm -rf salt-0.9.5
|
||||
mv salt salt-0.9.7
|
||||
tar -zcvf salt_0.9.7.orig.tar.gz --exclude "debian*" --exclude-vcs salt-0.9.7
|
||||
rm -rf salt-0.9.7
|
||||
|
|
25
debian/salt-common.install
vendored
25
debian/salt-common.install
vendored
|
@ -1,25 +1,2 @@
|
|||
salt/exceptions.py /usr/share/salt
|
||||
salt/loader.py /usr/share/salt
|
||||
salt/master.py /usr/share/salt
|
||||
salt/client.py /usr/share/salt
|
||||
salt/runner.py /usr/share/salt
|
||||
salt/output.py /usr/share/salt
|
||||
salt/minion.py /usr/share/salt
|
||||
salt/version.py /usr/share/salt
|
||||
salt/config.py /usr/share/salt
|
||||
salt/state.py /usr/share/salt
|
||||
salt/log.py /usr/share/salt
|
||||
salt/__init__.py /usr/share/salt
|
||||
salt/payload.py /usr/share/salt
|
||||
salt/crypt.py /usr/share/salt
|
||||
salt/runners /usr/share/salt/
|
||||
salt/renderers /usr/share/salt/
|
||||
salt/returners /usr/share/salt/
|
||||
salt/ext /usr/share/salt/
|
||||
salt/msgpack /usr/share/salt/
|
||||
salt/grains /usr/share/salt/
|
||||
salt/cli /usr/share/salt/
|
||||
salt/states /usr/share/salt/
|
||||
salt/utils /usr/share/salt/
|
||||
usr/lib/python2*/dist-packages/salt/msgpack
|
||||
usr/lib/python2*/dist-packages/salt/
|
||||
debian/lintian-overrides /usr/share/lintian/overrides/salt-common
|
||||
|
|
12
debian/salt-master.install
vendored
12
debian/salt-master.install
vendored
|
@ -1,6 +1,6 @@
|
|||
conf/master.template /etc/salt/master
|
||||
scripts/salt-key /usr/share/salt
|
||||
scripts/salt /usr/share/salt
|
||||
scripts/salt-run /usr/share/salt
|
||||
scripts/salt-cp /usr/share/salt
|
||||
scripts/salt-master /usr/share/salt
|
||||
conf/master.template /etc/salt
|
||||
scripts/salt-master /usr/bin
|
||||
scripts/salt-cp /usr/bin
|
||||
scripts/salt-run /usr/bin
|
||||
scripts/salt-key /usr/bin
|
||||
scripts/salt /usr/bin
|
||||
|
|
5
debian/salt-master.links
vendored
5
debian/salt-master.links
vendored
|
@ -1,5 +0,0 @@
|
|||
usr/share/salt/salt /usr/bin/salt
|
||||
usr/share/salt/salt-master /usr/bin/salt-master
|
||||
usr/share/salt/salt-cp /usr/bin/salt-cp
|
||||
usr/share/salt/salt-key /usr/bin/salt-key
|
||||
usr/share/salt/salt-run /usr/bin/salt-run
|
7
debian/salt-minion.install
vendored
7
debian/salt-minion.install
vendored
|
@ -1,4 +1,3 @@
|
|||
scripts/salt-minion /usr/share/salt
|
||||
scripts/salt-call /usr/share/salt
|
||||
salt/modules /usr/share/salt/modules
|
||||
conf/minion.template /etc/salt/minion
|
||||
conf/minion.template /etc/salt
|
||||
scripts/salt-minion /usr/bin
|
||||
scripts/salt-call /usr/bin
|
||||
|
|
2
debian/salt-minion.links
vendored
2
debian/salt-minion.links
vendored
|
@ -1,2 +0,0 @@
|
|||
usr/share/salt/salt-minion /usr/bin/salt-minion
|
||||
usr/share/salt/salt-call /usr/bin/salt-call
|
2
debian/salt-syndic.install
vendored
2
debian/salt-syndic.install
vendored
|
@ -1 +1 @@
|
|||
scripts/salt-syndic /usr/share/salt
|
||||
scripts/salt-syndic /usr/bin
|
||||
|
|
2
debian/salt-syndic.links
vendored
2
debian/salt-syndic.links
vendored
|
@ -1 +1 @@
|
|||
usr/share/salt/salt-syndic /usr/bin/salt-syndic
|
||||
usr/lib/python2*/dist-packages/salt/salt-syndic /usr/bin/salt-syndic
|
||||
|
|
BIN
doc/_static/favicon.ico
vendored
BIN
doc/_static/favicon.ico
vendored
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
doc/_static/salt-vert.png
vendored
BIN
doc/_static/salt-vert.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 6.7 KiB |
83
doc/_templates/index.html
vendored
83
doc/_templates/index.html
vendored
|
@ -1,83 +0,0 @@
|
|||
{% extends "layout.html" %}
|
||||
{% set title = 'Overview' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="introduction">
|
||||
<h1>What is Salt?</h1>
|
||||
|
||||
<p>Salt is a powerful remote execution manager that can be used to
|
||||
administer servers in a fast and efficient way.</p>
|
||||
|
||||
<p>Salt allows commands to be executed across large groups of servers.
|
||||
This means systems can be easily managed, but data can also be easily
|
||||
gathered. Quick introspection into running systems becomes a
|
||||
reality.</p>
|
||||
|
||||
<p>Remote execution is usually used to set up a certain state on a
|
||||
remote system. Salt addresses this problem as well, the salt state
|
||||
system uses salt state files to define the state a server needs to be
|
||||
in.</p>
|
||||
|
||||
<p>Between the remote execution system, and state management Salt
|
||||
addresses the backbone of cloud and data center management.</p>
|
||||
|
||||
<h2>The Salt stack</h2>
|
||||
<dl class="feat-link">
|
||||
<dt>Remote execution</dt>
|
||||
<dd>Remote execution is the core of modern cloud and infrastructure
|
||||
solutions, and Salt offers the fastest and easiest remote execution
|
||||
system available today</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="feat-link">
|
||||
<dt>Configuration management</dt>
|
||||
<dd>Salt offers the world simplest, yet most powerful, flexible and
|
||||
scalable configuration management system</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="feat-link">
|
||||
<dt>Highly scalable</dt>
|
||||
<dd>Easily manage tens or hundreds or even tens of thousands of
|
||||
nodes with a multi-tiered management infrastructure.</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="feat-link">
|
||||
<dt>Highly configurable</dt>
|
||||
<dd>Receive responses where, how, and when you want.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="documentation">
|
||||
<h2>Documentation</h2>
|
||||
|
||||
<dl class="doc-link">
|
||||
<dt class="doc-tutorials"><a href="{{ pathto("home") }}#docs-tutorials">Tutorials</a></dt>
|
||||
<dd>Get started with each Salt component in five minutes or less</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="doc-link">
|
||||
<dt class="doc-fulldocs"><a href="{{ pathto("home") }}#docs-reference">Documentation</a></dt>
|
||||
<dd>Comprehensive and in-depth explanations and reference;
|
||||
<br>Offline?
|
||||
<a href="{{ github_downloads }}/salt-{{ version }}.pdf" class="doc-pdf">
|
||||
<img src="{{ pathto('_static/page_white_acrobat.png', 1) }}">
|
||||
PDF</a>
|
||||
| <a href="{{ github_downloads }}/salt-{{ version }}.epub" class="doc-epub">
|
||||
<img src="{{ pathto('_static/book_open.png', 1) }}">
|
||||
ePUB</a>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="doc-link">
|
||||
<dt class="doc-offline"><a href="{{ pathto("contents") }}">Contents</a></dt>
|
||||
<dd>An exhaustive (and exhausting) overview of all the Salt documentation</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="doc-link">
|
||||
<dt class="doc-fulldocs"><a href="{{ pathto("py-modindex") }}">Module index</a></dt>
|
||||
<dd>Quickly jump to the reference for individual modules, states, and more</dd>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
32
doc/_templates/indexsidebar.html
vendored
32
doc/_templates/indexsidebar.html
vendored
|
@ -1,32 +0,0 @@
|
|||
<h3>Download</h3>
|
||||
|
||||
{% if version.endswith('pre') %}
|
||||
<p>This documentation is for version <b>{{ version }}</b>, which is not released yet.</p>
|
||||
<p>You can download it from the <a href="{{ github_base }}">GitHub repository</a>.</p>
|
||||
{% else %}
|
||||
<p>Current version: <b>{{ version }}</b></p>
|
||||
<p>Get the Salt tarball from the <a href="{{ github_downloads }}">GitHub downloads page</a>,
|
||||
or <a href="{{ pathto("topics/installation") }}">packages are available</a> for some OSes.</p>
|
||||
<p><a href="{{ pathto("topics/releases/index") }}">Read the release announcement.</a></p>
|
||||
{#
|
||||
<p>Latest <a href="http://saltstack.org/latest">development version docs</a> are also available.</p>
|
||||
#}
|
||||
{% endif %}
|
||||
|
||||
<h3>Recent updates</h3>
|
||||
<p>Our IRC channel is now on the popular Freenode network. See you there!</p>
|
||||
<p>The Salt git repository can now be found at the new saltstack GitHub organization.
|
||||
<a href="http://red45.wordpress.com/2011/11/15/little-move-big-progress/">Read why.</a></p>
|
||||
|
||||
<h3>Get help. Get involved.</h3>
|
||||
|
||||
<form action="http://groups.google.com/group/salt-users/boxsubscribe">
|
||||
<p>Join the <a href="https://groups.google.com/forum/#!forum/salt-users">mailing list</a>:
|
||||
<br>
|
||||
<input type="email" name="email" placeholder="Email address">
|
||||
<button type="submit" name="sub">Subscribe</button></p>
|
||||
</form>
|
||||
|
||||
<p>Join us via IRC in the <tt>#salt</tt> channel via
|
||||
<a href="http://webchat.freenode.net/?channels=salt&uio=Mj10cnVlJjk9dHJ1ZSYxMD10cnVl83">Freenode's webchat</a>.</p>
|
||||
<p><a href="{{ github_issues }}">Search bugs</a> or file one.</p>
|
8
doc/_templates/layout.html
vendored
8
doc/_templates/layout.html
vendored
|
@ -1,6 +1,6 @@
|
|||
{% extends "!layout.html" %}
|
||||
|
||||
{% block rootrellink %}
|
||||
<li><a href="{{ pathto('index') }}">Salt home</a> | </li>
|
||||
<li><a href="{{ pathto('home') }}">Documentation</a> »</li>
|
||||
{% endblock %}
|
||||
{%- block rootrellink %}
|
||||
<li><a href="http://saltstack.org">« SaltStack.org</a> | </li>
|
||||
<li><a href="{{ pathto('index') }}">Documentation home</a></li>
|
||||
{%- endblock %}
|
||||
|
|
186
doc/_themes/saltstack/layout.html
vendored
186
doc/_themes/saltstack/layout.html
vendored
|
@ -1,186 +0,0 @@
|
|||
{%- block doctype -%}
|
||||
<!DOCTYPE html>
|
||||
{%- endblock %}
|
||||
|
||||
{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %}
|
||||
{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
|
||||
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and (sidebars != []) %}
|
||||
{%- set url_root = pathto('', 1) %}
|
||||
{# XXX necessary? #}
|
||||
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
||||
{%- if not embedded and docstitle %}
|
||||
{%- set titlesuffix = " — "|safe + docstitle|e %}
|
||||
{%- else %}
|
||||
{%- set titlesuffix = "" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- macro relbar() %}
|
||||
<div class="related">
|
||||
<h3>{{ _('Navigation') }}</h3>
|
||||
<ul class="rel-main">
|
||||
{%- block rootrellink %}
|
||||
<li><a href="{{ pathto('index') }}">Salt home</a> | </li>
|
||||
<li><a href="{{ pathto('home') }}">Documentation</a> »</li>
|
||||
{%- endblock %}
|
||||
</ul>
|
||||
<ul class="rel-extra">
|
||||
{%- for rellink in rellinks|reverse %}
|
||||
<li>
|
||||
<a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}">{{ rellink[3] }}</a>
|
||||
{%- if not loop.last %}{{ reldelim2 }}{% endif %}</li>
|
||||
{%- endfor %}
|
||||
|
||||
{%- block relbaritems %} {% endblock %}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro sidebar() %}
|
||||
{%- if render_sidebar %}
|
||||
<div class="sidebar">
|
||||
{%- block sidebarlogo %}{%- endblock %}
|
||||
|
||||
{%- for sidebartemplate in sidebars|default(html_default_sidebars, true) %}
|
||||
{%- include sidebartemplate %}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro script() %}
|
||||
<script>
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '{{ url_root }}',
|
||||
VERSION: '{{ release|e }}',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro css() %}
|
||||
<link rel="stylesheet" href="http://yui.yahooapis.com/combo?3.3.0/build/cssreset/reset-min.css&3.3.0/build/cssfonts/fonts-min.css&3.3.0/build/cssbase/base-min.css">
|
||||
{% if style %}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}">
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}">
|
||||
{%- endmacro %}
|
||||
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
<meta charset="{{ encoding }}">
|
||||
{{ metatags }}
|
||||
|
||||
{%- block htmltitle %}
|
||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||
{%- endblock %}
|
||||
|
||||
{{ css() }}
|
||||
|
||||
{%- if not embedded %}
|
||||
{{ script() }}
|
||||
{%- if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml"
|
||||
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
|
||||
href="{{ pathto('_static/opensearch.xml', 1) }}">
|
||||
{%- endif %}
|
||||
|
||||
{%- if favicon %}
|
||||
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}">
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- block linktags %}
|
||||
{%- if hasdoc('about') %}
|
||||
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}">
|
||||
{%- endif %}
|
||||
{%- if hasdoc('genindex') %}
|
||||
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}">
|
||||
{%- endif %}
|
||||
{%- if hasdoc('search') %}
|
||||
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}">
|
||||
{%- endif %}
|
||||
{%- if hasdoc('copyright') %}
|
||||
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}">
|
||||
{%- endif %}
|
||||
<link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}">
|
||||
{%- if parents %}
|
||||
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}">
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}">
|
||||
{%- endif %}
|
||||
{%- if prev %}
|
||||
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}">
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
|
||||
{%- block extrahead %} {% endblock %}
|
||||
|
||||
{%- block analytics %}
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-26984928-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body id="saltstack.org"><div class="container">
|
||||
{% block header %}
|
||||
<div class="header">
|
||||
<h1 class="logo"><a href="{{ pathto('index') }}">
|
||||
<img src="{{ pathto('_static/salt-horiz.png', 1) }}" alt="Salt Stack">
|
||||
</a></h1>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{%- block relbar1 %}{{ relbar() }}{% endblock %}
|
||||
|
||||
{%- block content %}
|
||||
|
||||
{%- block sidebar1 %}{% endblock %}
|
||||
|
||||
<div class="content">
|
||||
<div class="document">
|
||||
{%- block document %}
|
||||
{% block body %} {% endblock %}
|
||||
{%- endblock %}
|
||||
</div>
|
||||
|
||||
{%- block sidebar2 %}{{ sidebar() }}{% endblock %}
|
||||
</div>
|
||||
{%- endblock %}
|
||||
|
||||
{%- block relbar2 %}{{ relbar() }}{% endblock %}
|
||||
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
{%- if show_copyright %}
|
||||
{%- if hasdoc('copyright') %}
|
||||
{% trans path=pathto('copyright'), copyright=copyright|e %}© <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
|
||||
{%- else %}
|
||||
{% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if last_updated %}
|
||||
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if show_sphinx %}
|
||||
{% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endblock %}
|
||||
</body>
|
||||
</html>
|
273
doc/_themes/saltstack/static/base-salt.css
vendored
273
doc/_themes/saltstack/static/base-salt.css
vendored
|
@ -1,273 +0,0 @@
|
|||
body {
|
||||
background-color: #eeeeec;
|
||||
font-size: 100%; line-height: 1.25; text-align: center; }
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
width: 60.5em; min-height: 57.25em;
|
||||
text-align: left; margin: 0 auto; }
|
||||
|
||||
.content {
|
||||
margin-bottom: 5em;
|
||||
text-align: justify; display: inline-block; }
|
||||
|
||||
.document {
|
||||
float: left;
|
||||
width: 36.8em; margin-right: 2.5em; }
|
||||
|
||||
.document a, .footer a {
|
||||
text-decoration: underline; }
|
||||
|
||||
.sidebar {
|
||||
float: left; width: 18em;
|
||||
text-align: left; }
|
||||
|
||||
.related { overflow: hidden; }
|
||||
|
||||
.related h3 {
|
||||
font-size: 1px; text-indent: -9999999em; }
|
||||
|
||||
.related ul { margin: 0; }
|
||||
|
||||
.related li {
|
||||
display: inline; list-style-type: none; }
|
||||
|
||||
.rel-main { float: left; }
|
||||
.rel-extra { float: right; }
|
||||
|
||||
.header,
|
||||
.related,
|
||||
.content,
|
||||
.footer {
|
||||
margin-left: 1.5em; margin-right: 1.5em; }
|
||||
|
||||
.header {
|
||||
border-bottom: 3px solid #2e3436; }
|
||||
|
||||
.header h1 { margin: 0; }
|
||||
|
||||
.footer {
|
||||
padding-top: 2em; text-align: right;
|
||||
border-top: 4px solid #babdb6; }
|
||||
|
||||
/*
|
||||
* http://lamb.cc/typograph/
|
||||
*/
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
color: #3465a4; font-family: georgia; font-weight: normal; }
|
||||
|
||||
h1 { font-size: 1.75em; margin: 1.51786em 0; color: #204a87; }
|
||||
h2 {
|
||||
border-bottom: 1px solid #3465a4;
|
||||
font-size: 1.5em; margin: 1.45833em 0; }
|
||||
h3 { font-size: 1.33333em; margin: 1.25em 0; }
|
||||
h4 { font-size: 1.16667em; margin: 1.51786em 0; }
|
||||
|
||||
a {
|
||||
color: #ce5c00; }
|
||||
|
||||
.headerlink {
|
||||
visibility: hidden; color: #dddddd; padding-left: .3em; }
|
||||
|
||||
h1:hover > .headerlink,
|
||||
h2:hover > .headerlink,
|
||||
h3:hover > .headerlink,
|
||||
h4:hover > .headerlink,
|
||||
h5:hover > .headerlink,
|
||||
h6:hover > .headerlink,
|
||||
dt:hover > .headerlink { visibility: visible; }
|
||||
|
||||
img {
|
||||
border: 0; }
|
||||
|
||||
dt:target, .highlighted {
|
||||
background-color: #fbe54e; }
|
||||
|
||||
/*
|
||||
* http://www.blueprintcss.org/
|
||||
*/
|
||||
|
||||
.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; }
|
||||
.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; }
|
||||
.hide { display: none; }
|
||||
|
||||
.quiet { color: #666; }
|
||||
.loud { color: #000; }
|
||||
.highlight { background: #ff0; }
|
||||
.added { background: #060; color: #fff; }
|
||||
.removed { background: #900; color: #fff; }
|
||||
|
||||
.first { margin-left: 0; padding-left: 0; }
|
||||
.last { margin-right: 0; padding-right: 0; }
|
||||
.top { margin-top: 0; padding-top: 0; }
|
||||
.bottom { margin-bottom: 0; padding-bottom: 0; }
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
.doc-link,
|
||||
.feat-link {
|
||||
margin-left: 0; text-align: left; }
|
||||
|
||||
.doc-link dd,
|
||||
.feat-link dd {
|
||||
margin-left: 0; font-style: italic; }
|
||||
|
||||
.feat-link dt {
|
||||
color: #3465a4; font-weight: bold; }
|
||||
|
||||
.doc-link {
|
||||
width: 44%; height: 6em; float: left;
|
||||
margin-right: 2em; }
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
.sidebar .toctree-l1.current a {
|
||||
border-right: 5px solid #fcaf3e; }
|
||||
|
||||
.line-block {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.line-block .line-block {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
div.header div.rel a {
|
||||
color: #fcaf3e;
|
||||
letter-spacing: .1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
padding: 3px;
|
||||
background-color: #eeeeec;
|
||||
border-top: 2px solid #dddddd;
|
||||
border-bottom: 2px solid #dddddd;
|
||||
margin-top: .8em;
|
||||
margin-bottom: .8em;
|
||||
}
|
||||
|
||||
.descname {
|
||||
font-weight: bold; }
|
||||
|
||||
.literal {
|
||||
background-color: #eeeeec; }
|
||||
|
||||
blockquote {
|
||||
margin: 1em; }
|
||||
|
||||
.footer, .footer a {
|
||||
color: #888a85; }
|
||||
|
||||
div.admonition {
|
||||
font-size: 0.9em;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.5em 1em 0.5em 1em;
|
||||
border: 1px solid #ddd; }
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-weight: bold; color: #3465a4; }
|
||||
|
||||
div.warning {
|
||||
border-color: #940000; }
|
||||
|
||||
div.warning p.admonition-title {
|
||||
color: #940000; }
|
||||
|
||||
div.viewcode-block:target {
|
||||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
||||
|
||||
/* Styles copied from basic theme */
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left {
|
||||
clear: left; float: left; margin-right: 1em; }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right {
|
||||
clear: right; float: right; margin-left: 1em; }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block; margin-left: auto; margin-right: auto; }
|
||||
|
||||
.align-left { text-align: left; }
|
||||
.align-center { text-align: center; }
|
||||
.align-right { text-align: right; }
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold; }
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* -- viewcode extension ---------------------------------------------------- */
|
||||
|
||||
.viewcode-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
margin: -1px -3px;
|
||||
padding: 0 3px;
|
||||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
BIN
doc/_themes/saltstack/static/salt-horiz.png
vendored
BIN
doc/_themes/saltstack/static/salt-horiz.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 5.6 KiB |
3
doc/_themes/saltstack/theme.conf
vendored
3
doc/_themes/saltstack/theme.conf
vendored
|
@ -1,3 +0,0 @@
|
|||
[theme]
|
||||
inherit = default
|
||||
stylesheet = base-salt.css
|
39
doc/conf.py
39
doc/conf.py
|
@ -74,22 +74,17 @@ on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
|||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
project = u'Salt'
|
||||
copyright = u'2011, Thomas S. Hatch'
|
||||
copyright = u'2012, Thomas S. Hatch'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = __version__
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
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.extlinks',
|
||||
'sphinx.ext.autosummary']
|
||||
|
||||
modindex_common_prefix = ['salt.']
|
||||
|
||||
|
@ -97,6 +92,7 @@ autosummary_generate = True
|
|||
|
||||
# Define a substitution for linking to the latest release tarball
|
||||
rst_prolog = """\
|
||||
.. _`installation`: http://saltstack.org/install/
|
||||
.. |saltrepo| replace:: https://github.com/saltstack/salt
|
||||
.. |latest| replace:: https://github.com/downloads/saltstack/salt/salt-%s.tar.gz
|
||||
""" % __version__
|
||||
|
@ -110,12 +106,8 @@ extlinks = {
|
|||
|
||||
|
||||
### HTML options
|
||||
if on_rtd:
|
||||
html_theme = 'default'
|
||||
else:
|
||||
html_theme = 'saltstack'
|
||||
html_theme = 'default'
|
||||
|
||||
html_theme_path = ['_themes']
|
||||
html_title = None
|
||||
html_short_title = 'Salt'
|
||||
|
||||
|
@ -125,23 +117,20 @@ html_favicon = 'favicon.ico'
|
|||
html_use_smartypants = False
|
||||
|
||||
html_additional_pages = {
|
||||
'index': 'index.html',
|
||||
'404': '404.html',
|
||||
}
|
||||
|
||||
html_default_sidebars = [
|
||||
'localtoc.html',
|
||||
'relations.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html']
|
||||
|
||||
html_sidebars = {
|
||||
'ref/**/all/salt.*': ['autosummarysidebar.html'] + html_default_sidebars,
|
||||
'index': ['indexsidebar.html', 'searchbox.html'],
|
||||
'ref/**/all/salt.*': [
|
||||
'autosummarysidebar.html',
|
||||
'localtoc.html',
|
||||
'relations.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html',
|
||||
],
|
||||
}
|
||||
|
||||
html_context = {
|
||||
'html_default_sidebars': html_default_sidebars,
|
||||
'github_base': 'https://github.com/saltstack/salt',
|
||||
'github_issues': 'https://github.com/saltstack/salt/issues',
|
||||
'github_downloads': 'https://github.com/saltstack/salt/downloads',
|
||||
|
@ -161,7 +150,7 @@ latex_documents = [
|
|||
u'Thomas Hatch', 'manual'),
|
||||
]
|
||||
|
||||
latex_logo = '_static/salt.png'
|
||||
latex_logo = '_static/salt-vert.png'
|
||||
|
||||
|
||||
### Manpage options
|
||||
|
@ -188,7 +177,7 @@ man_pages = [
|
|||
epub_title = u'Salt Documentation'
|
||||
epub_author = u'Thomas S. Hatch'
|
||||
epub_publisher = epub_author
|
||||
epub_copyright = u'2011, Thomas S. Hatch'
|
||||
epub_copyright = u'2012, Thomas S. Hatch'
|
||||
|
||||
epub_scheme = 'URL'
|
||||
epub_identifier = 'http://saltstack.org/'
|
||||
|
|
|
@ -6,10 +6,7 @@ Full Table of Contents
|
|||
:maxdepth: 3
|
||||
:glob:
|
||||
|
||||
home
|
||||
|
||||
topics/index
|
||||
topics/installation
|
||||
topics/configuration
|
||||
topics/tutorials/modules
|
||||
topics/tutorials/states*
|
||||
|
|
80
doc/home.rst
80
doc/home.rst
|
@ -1,80 +0,0 @@
|
|||
.. _contents:
|
||||
|
||||
.. |vid| image:: /_static/film_link.png
|
||||
|
||||
.. _docs-tutorials:
|
||||
|
||||
Salt at a glance
|
||||
================
|
||||
|
||||
Learn about the various parts of Salt in five minute increments (or less).
|
||||
|
||||
* **What is Salt?:**
|
||||
:doc:`Overview <topics/index>`
|
||||
| :doc:`Community </topics/community>`
|
||||
* **The basics:**
|
||||
:doc:`Installation <topics/installation>`
|
||||
| :doc:`Configuration <topics/configuration>`
|
||||
| |vid| `Watch the screencast <http://blip.tv/saltstack/salt-installation-configuration-and-remote-execution-5713423>`_
|
||||
* **Remote execution:**
|
||||
:doc:`Modules <topics/tutorials/modules>`
|
||||
* **State management:**
|
||||
:doc:`Part 1 <topics/tutorials/states_pt1>`
|
||||
| :doc:`Part 2 <topics/tutorials/states_pt2>`
|
||||
| :doc:`Part 3 <topics/tutorials/states_pt3>`
|
||||
|
||||
|
||||
|
||||
.. _docs-reference:
|
||||
|
||||
Salt in depth
|
||||
=============
|
||||
|
||||
While using and setting up Salt is a simple task, the capabilities of Salt run
|
||||
much deeper. Gaining a better understanding of how Salt works will allow you to
|
||||
get much more out of Salt.
|
||||
|
||||
* **Remote execution:**
|
||||
:doc:`Writing modules <ref/modules/index>`
|
||||
| :doc:`full list of modules <ref/modules/all/index>`
|
||||
* **System info & detection:**
|
||||
:doc:`Grains <ref/grains>`
|
||||
* **Displaying or storing responses:**
|
||||
:doc:`Writing returners <ref/returners/index>`
|
||||
| :doc:`full list of returners <ref/returners/all/index>`
|
||||
* **State enforcement:**
|
||||
:doc:`States <ref/states/index>`
|
||||
| :doc:`Highstate data structure <ref/states/highstate>`
|
||||
| :doc:`full list of states <ref/states/all/index>`
|
||||
* **Renderers:**
|
||||
:doc:`Renderers <ref/renderers/index>`
|
||||
| :doc:`full list of renderers <ref/renderers/all/index>`
|
||||
* **Transferring & syncing files:**
|
||||
:doc:`File Server <ref/file_server/index>`
|
||||
* **Network topology:**
|
||||
:doc:`ref/syndic`
|
||||
| :doc:`ref/peer`
|
||||
* **Configuration:**
|
||||
:doc:`Full list of minion settings <ref/configuration/minion>`
|
||||
| :doc:`Full list of master settings <ref/configuration/master>`
|
||||
* **Using Salt:**
|
||||
:doc:`From the command-line <ref/cli/index>`
|
||||
| :doc:`Through the Python API <ref/python-api>`
|
||||
|
||||
.. admonition:: Screencasts and presentations
|
||||
|
||||
* Thomas S. Hatch was `interviewed on episode 191 of FLOSS Weekly
|
||||
<http://twit.tv/show/floss-weekly/191>`_.
|
||||
* Presentation at the Salt Lake Linux User Group (SLLUG) in May 2011
|
||||
`video <http://blip.tv/thomas-s-hatch/salt-0-8-7-presentation-5180182>`_
|
||||
| :download:`slides <Salt.pdf>` (PDF)
|
||||
|
||||
Salt quick reference
|
||||
====================
|
||||
|
||||
* :doc:`Release notes </topics/releases/index>`
|
||||
| :doc:`Roadmap </topics/roadmap/index>`
|
||||
* :ref:`modindex`
|
||||
| :ref:`genindex`
|
||||
| :doc:`Full table of contents </contents>`
|
||||
* :ref:`search`
|
187
doc/index.rst
Normal file
187
doc/index.rst
Normal file
|
@ -0,0 +1,187 @@
|
|||
.. _contents:
|
||||
|
||||
.. |vid| image:: /_static/film_link.png
|
||||
|
||||
Get started with Salt
|
||||
=====================
|
||||
|
||||
.. sidebar:: Presentations
|
||||
|
||||
A list of `presentations and interviews on Salt`_ (including the FLOSS
|
||||
Weekly interview).
|
||||
|
||||
.. _`presentations and interviews on Salt`: http://saltstack.org/presentations/
|
||||
|
||||
Salt is a **remote execution** and **configuration management** tool.
|
||||
|
||||
Salt is designed to be secure using **AES encryption** and **public-key
|
||||
authentication**; incredibly scaleable using an advanced **ZeroMQ** topology;
|
||||
fast and efficient using **msgpack**; and extensible using small and simple
|
||||
**Python** modules.
|
||||
|
||||
Read the :doc:`Salt overview <topics/index>` for a more thorough description.
|
||||
|
||||
Step 1: Remote execution
|
||||
------------------------
|
||||
|
||||
.. sidebar:: |vid| Screencasts
|
||||
|
||||
Watch the `remote execution screencast`__.
|
||||
|
||||
.. __: http://blip.tv/saltstack/salt-installation-configuration-and-remote-execution-5713423
|
||||
|
||||
The quickest way to see Salt in action is to run a command on a :term:`minion`
|
||||
host from the :term:`master` host. This is widely known as :term:`remote
|
||||
execution` — executing commands on remote hosts.
|
||||
|
||||
1. `Installation`_
|
||||
2. :doc:`Configure the minion <topics/configuration>`
|
||||
3. :doc:`Run remote commands <topics/tutorials/modules>`
|
||||
|
||||
Step 2: Configuration management
|
||||
--------------------------------
|
||||
|
||||
Now that you have the basics out of the way, learn to use Salt to configure
|
||||
your servers. This is widely known as :term:`configuration management` —
|
||||
installing packages, configuring users and services, and much more.
|
||||
|
||||
1. :doc:`Basic config management <topics/tutorials/states_pt1>`
|
||||
2. :doc:`Less basic config management <topics/tutorials/states_pt2>`
|
||||
3. :doc:`Advanced techniques <topics/tutorials/states_pt3>`
|
||||
|
||||
Salt in depth
|
||||
=============
|
||||
|
||||
Setting up and using Salt is a simple task but it's capabilities run much, much
|
||||
deeper. Gaining a better understanding of how Salt works will allow you to
|
||||
truly make it work for you.
|
||||
|
||||
.. contents:: Overview
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
**Remote execution**
|
||||
Remote execution is the core functionality of Salt. Running pre-defined or
|
||||
arbitrary commands on remote hosts.
|
||||
|
||||
**Modules**
|
||||
Salt modules are the core of remote execution. They provide
|
||||
functionality such as installing a package, restarting a service,
|
||||
running a remote command, transferring a file — and the list goes on.
|
||||
|
||||
:doc:`Full list of modules </ref/modules/all/index>`
|
||||
The giant list of core modules that ship with Salt
|
||||
(And there are even more in the `salt-contrib`_ repository!)
|
||||
|
||||
:doc:`Writing modules <ref/modules/index>`
|
||||
A guide on how to write Salt modules
|
||||
|
||||
**Targeting**
|
||||
Specify which hosts should run commands or manage configuration.
|
||||
|
||||
:doc:`Targeting <ref/targeting/index>`
|
||||
Hostnames, lists, regular expressions, or define groups.
|
||||
|
||||
:doc:`Grains <ref/grains>`
|
||||
Bits of static information about a minion such as OS, version,
|
||||
virtualization, CPU, memory, and much more.
|
||||
|
||||
**Returners**
|
||||
Salt returners allow saving minion responses in various datastores or
|
||||
to various locations in addition to display at the CLI.
|
||||
|
||||
:doc:`Full list of returners </ref/returners/all/index>`
|
||||
Store minion responses in Redis, Mongo, Cassandra or more.
|
||||
|
||||
:doc:`Writing returners <ref/returners/index>`
|
||||
If we're missing your favorite storage backend, webservice, or you
|
||||
need a custom endpoint returners are *tiny* and simple to write.
|
||||
|
||||
**Configuration management**
|
||||
Building on the remote execution core is a robust and flexible config
|
||||
management framework. Execution happens on the minions allowing
|
||||
effortless, simultaneous configuration of thousands of hosts.
|
||||
|
||||
**States**
|
||||
Express the state of a host using small, easy to read, easy to
|
||||
understand configuration files. No programming required (unless you
|
||||
want to).
|
||||
|
||||
:doc:`Full list of states <ref/states/all/index>`
|
||||
Install packages, create users, transfer files, start services, and
|
||||
more and more.
|
||||
|
||||
:doc:`Using states <ref/states/index>`
|
||||
You've seen the big list of available states, now learn how to call
|
||||
them.
|
||||
|
||||
:doc:`Highstate data structure <ref/states/highstate>`
|
||||
A dry, vocabulary and technical representation of the configuration
|
||||
format that states represent.
|
||||
|
||||
**Renderers**
|
||||
Write state configuration files in the language, templating engine, or
|
||||
file type of your choice. The world doesn't need yet another DSL.
|
||||
|
||||
:doc:`Full list of renderers <ref/renderers/all/index>`
|
||||
YAML? JSON? Jinja? Mako? Python? We got you covered. (And if we
|
||||
don't, new renderers are *tiny* and easy to write.)
|
||||
|
||||
:doc:`Renderers <ref/renderers/index>`
|
||||
Salt states are only concerned with the ultimate highstate data
|
||||
structure. How you create that data structure isn't our business.
|
||||
Tweak a config option and use whatever you're most comfortable
|
||||
with.
|
||||
|
||||
**Miscellaneous topics**
|
||||
Salt is a many splendid thing.
|
||||
|
||||
:doc:`File Server <ref/file_server/index>`
|
||||
Salt can easily and quickly transfer files (in fact, that's how Salt
|
||||
States work). Even under load, files are chunked and served.
|
||||
|
||||
:doc:`Syndic <ref/syndic>`
|
||||
A seamless master of masters. Scale Salt to thousands of hosts or
|
||||
across many different networks.
|
||||
|
||||
:doc:`Peer communication <ref/peer>`
|
||||
Allow minions to communicate amongst themselves. For example, configure
|
||||
one minion by querying live data from all the others. With great power
|
||||
comes great responsibility.
|
||||
|
||||
:doc:`Network topology <ref/topology>`
|
||||
At it's core, Salt is a highly scalable communication layer built on
|
||||
top of ZeroMQ that enables remote execution and configuration
|
||||
management. The possibilities are endless and Salt's future looks
|
||||
bright.
|
||||
|
||||
:doc:`Python API interface <ref/python-api>`
|
||||
Use Salt programmatically from your own scripts and programs easily and
|
||||
simply via ``import salt``.
|
||||
|
||||
**Reference**
|
||||
:doc:`Command-line interface <ref/cli/index>`
|
||||
Read the Salt manpages.
|
||||
|
||||
:doc:`Full list of master settings <ref/configuration/master>`
|
||||
Read through the heavily-commented master configuration file.
|
||||
|
||||
:doc:`Full list of minion settings <ref/configuration/minion>`
|
||||
Read through the heavily-commented minion configuration file.
|
||||
|
||||
:doc:`Full table of contents </contents>`
|
||||
Dense but complete.
|
||||
|
||||
**More information about the project**
|
||||
|
||||
:doc:`Roadmap </topics/roadmap/index>`
|
||||
Where we're headed.
|
||||
|
||||
:doc:`Release notes </topics/releases/index>`
|
||||
Where we've been.
|
||||
|
||||
:doc:`Community </topics/community>`
|
||||
How you can get involved.
|
||||
|
||||
.. _`salt-contrib`: https://github.com/saltstack/salt-contrib
|
||||
.. _`salt-states`: https://github.com/saltstack/salt-states
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-CALL" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-CALL" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-call \- salt-call Documentation
|
||||
.
|
||||
|
@ -63,7 +63,7 @@ none are specified
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-CP" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-CP" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-cp \- salt-cp Documentation
|
||||
.
|
||||
|
@ -61,13 +61,13 @@ The timeout in seconds to wait for replies from the salt minions.
|
|||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-E, \-\-pcre
|
||||
The target expression will be interpereted as a pcre regular expression
|
||||
The target expression will be interpreted as a pcre regular expression
|
||||
rather than a shell glob.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-L, \-\-list
|
||||
The target expression will be interpereted as a comma delimited list,
|
||||
The target expression will be interpreted as a comma delimited list,
|
||||
example: server1.foo.bar,server2.foo.bar,example7.quo.qux
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
|
@ -93,7 +93,7 @@ default=/etc/salt/master
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-KEY" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-KEY" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-key \- salt-key Documentation
|
||||
.
|
||||
|
@ -51,8 +51,8 @@ List the unaccepted minion public keys.
|
|||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-L, \-\-list\-all
|
||||
List all public keys on this salt master, both accepted and pending
|
||||
acceptance.
|
||||
List all public keys on this salt master: accepted, pending,
|
||||
and rejected.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
@ -66,6 +66,16 @@ Accepts all pending public keys.
|
|||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-r REJECT, \-\-reject=REJECT
|
||||
Reject the named minion public key.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-R, \-\-reject\-all
|
||||
Rejects all pending public keys.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c CONFIG, \-\-config=CONFIG
|
||||
The master configuration file needs to be read to determine where the salt
|
||||
keys are stored via the pki_dir configuration value;
|
||||
|
@ -74,7 +84,7 @@ default=/etc/salt/master
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-MASTER" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-MASTER" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-master \- salt-master Documentation
|
||||
.
|
||||
|
@ -69,7 +69,7 @@ settings see the config file. Default: \fBwarning\fP.
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-MINION" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-MINION" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-minion \- salt-minion Documentation
|
||||
.
|
||||
|
@ -70,7 +70,7 @@ settings see the config file. Default: \fBwarning\fP.
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-RUN" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-RUN" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-run \- salt-run Documentation
|
||||
.
|
||||
|
@ -41,7 +41,7 @@ salt\-run RUNNER
|
|||
.fi
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
Salt run is the frontend command for executing \fBSalt Runners\fP.
|
||||
salt\-run is the frontend command for executing \fBSalt Runners\fP.
|
||||
Salt runners are simple modules used to execute convenience functions on the
|
||||
master
|
||||
.SH OPTIONS
|
||||
|
@ -60,7 +60,7 @@ default=/etc/salt/master
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT-SYNDIC" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT-SYNDIC" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt-syndic \- salt-syndic Documentation
|
||||
.
|
||||
|
@ -64,7 +64,7 @@ The minion configuration file to use, the default is /etc/salt/minion
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH "SALT" "1" "January 20, 2012" "0.9.6" "Salt"
|
||||
.TH "SALT" "1" "February 15, 2012" "0.9.7" "Salt"
|
||||
.SH NAME
|
||||
salt \- salt
|
||||
.
|
||||
|
@ -155,7 +155,7 @@ Print the output from the salt command in json.
|
|||
.SH AUTHOR
|
||||
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
|
||||
.SH COPYRIGHT
|
||||
2011, Thomas S. Hatch
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
|
1703
doc/man/salt.7
1703
doc/man/salt.7
File diff suppressed because it is too large
Load diff
|
@ -82,14 +82,14 @@ Targeting with Executions
|
|||
`````````````````````````
|
||||
|
||||
As of 0.8.8 targeting with executions is still under heavy development and this
|
||||
documentation is written to refernce the behavior of execution matching in the
|
||||
documentation is written to reference the behavior of execution matching in the
|
||||
future.
|
||||
|
||||
Execution matching allows for a primary function to be executed, and then based
|
||||
on the return of the primary function the main function is executed.
|
||||
|
||||
Execution matching allows for matching minions based on any arbitrairy running
|
||||
data on tne minions.
|
||||
Execution matching allows for matching minions based on any arbitrary running
|
||||
data on the minions.
|
||||
|
||||
Compound Targeting
|
||||
``````````````````
|
||||
|
@ -104,13 +104,14 @@ is well defined with an example:
|
|||
|
||||
salt -C 'G@os:Debian and webser* or E@db.*' test.ping
|
||||
|
||||
in this example any minion who's id starts with webser and is running Debian,
|
||||
or any minion who's id starts with db will be matched.
|
||||
In this example any minion who's id starts with ``webser`` and is running
|
||||
Debian, or any minion who's id starts with db will be matched.
|
||||
|
||||
The type of matcher defaults to glob, but can be specified with the
|
||||
corresponding letter followed by the @ symbol. In the above example a grain is
|
||||
used with G@ as well as a regular expression with E@. The webser* target does
|
||||
not need to be prefaced with a target type specifier because it is a glob.
|
||||
corresponding letter followed by the ``@`` symbol. In the above example a grain
|
||||
is used with ``G@`` as well as a regular expression with ``E@``. The
|
||||
``webser*`` target does not need to be prefaced with a target type specifier
|
||||
because it is a glob.
|
||||
|
||||
Node Group Targeting
|
||||
````````````````````
|
||||
|
|
|
@ -36,12 +36,12 @@ Options
|
|||
|
||||
.. option:: -E, --pcre
|
||||
|
||||
The target expression will be interpereted as a pcre regular expression
|
||||
The target expression will be interpreted as a pcre regular expression
|
||||
rather than a shell glob.
|
||||
|
||||
.. option:: -L, --list
|
||||
|
||||
The target expression will be interpereted as a comma delimited list,
|
||||
The target expression will be interpreted as a comma delimited list,
|
||||
example: server1.foo.bar,server2.foo.bar,example7.quo.qux
|
||||
|
||||
.. option:: -G, --grain
|
||||
|
|
|
@ -28,8 +28,8 @@ Options
|
|||
|
||||
.. option:: -L, --list-all
|
||||
|
||||
List all public keys on this salt master, both accepted and pending
|
||||
acceptance.
|
||||
List all public keys on this salt master: accepted, pending,
|
||||
and rejected.
|
||||
|
||||
.. option:: -a ACCEPT, --accept=ACCEPT
|
||||
|
||||
|
@ -39,6 +39,14 @@ Options
|
|||
|
||||
Accepts all pending public keys.
|
||||
|
||||
.. option:: -r REJECT, --reject=REJECT
|
||||
|
||||
Reject the named minion public key.
|
||||
|
||||
.. option:: -R, --reject-all
|
||||
|
||||
Rejects all pending public keys.
|
||||
|
||||
.. option:: -c CONFIG, --config=CONFIG
|
||||
|
||||
The master configuration file needs to be read to determine where the salt
|
||||
|
|
|
@ -14,7 +14,7 @@ Synopsis
|
|||
Description
|
||||
===========
|
||||
|
||||
Salt run is the frontend command for executing ``Salt Runners``.
|
||||
salt-run is the frontend command for executing ``Salt Runners``.
|
||||
Salt runners are simple modules used to execute convenience functions on the
|
||||
master
|
||||
|
||||
|
|
|
@ -198,6 +198,23 @@ root of the base environment
|
|||
|
||||
state_top: top.sls
|
||||
|
||||
.. conf_master:: external_nodes
|
||||
|
||||
``external_nodes``
|
||||
------------------
|
||||
|
||||
Default: None
|
||||
|
||||
The external_nodes option allows Salt to gather data that would normally be
|
||||
placed in a top file from and external node controller. The external_nodes
|
||||
option is the executable that will return the ENC data. Remember that Salt
|
||||
will look for external nodes AND top files and combine the results if both
|
||||
are enabled and available!
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
external_nodes: cobbler-ext-nodes
|
||||
|
||||
.. conf_master:: renderer
|
||||
|
||||
``renderer``
|
||||
|
|
|
@ -310,6 +310,21 @@ enabled and can be disabled by changing this value to False
|
|||
|
||||
clean_dynamic_modules: True
|
||||
|
||||
.. conf_minion:: environment
|
||||
|
||||
``environment``
|
||||
---------------
|
||||
|
||||
Default: ``None``
|
||||
|
||||
Normally the minion is not isolated to any single environment on the master
|
||||
when running states, but the environment can be isolated on the minion side
|
||||
by statically setting it. Remember that the recommended way to manage
|
||||
environments is to isolate via the top file.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
environment: None
|
||||
|
||||
Security Settings
|
||||
------------------
|
||||
|
|
|
@ -16,18 +16,38 @@ information in grains is unchanging, therefore the nature of the data is
|
|||
static. So grains information are things like the running kernel, or the
|
||||
operating system.
|
||||
|
||||
Grains in the Minion Config
|
||||
===========================
|
||||
|
||||
Grains can also be statically assigned within the minion configuration file.
|
||||
Just add the option ``grains`` and pass options to it:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
grains:
|
||||
roles:
|
||||
- webserver
|
||||
- memcache
|
||||
deployment: datacenter4
|
||||
cabinet: 13
|
||||
cab_u: 14-15
|
||||
|
||||
Then statis data specific to your servers can be retrived via Salt, or used
|
||||
inside of the state system for matching. It also makes targeting, in the case
|
||||
of the example above, simply based on specific data about your deployment.
|
||||
|
||||
Writing Grains
|
||||
==============
|
||||
|
||||
Grains are easy to write, the grains interface is derived my executing all of
|
||||
the "public" functions found in the modules located in the grains package.
|
||||
The functions in the modules of the grains must return a python dict, the keys
|
||||
in the dict are the names of the grains, the values are the values.
|
||||
Grains are easy to write. The grains interface is derived by executing all of
|
||||
the "public" functions found in the modules located in the grains package or
|
||||
the custom grains directory. The functions in the modules of the grains must
|
||||
return a python dict, where the keys in the dict are the names of the grains and
|
||||
the values are the values.
|
||||
|
||||
This means that the actual grains interface is simply a python dict.
|
||||
|
||||
Before adding a grain to salt, consider what the grain is and remember that
|
||||
grains need to be static data.
|
||||
Custom grains should be placed in a ``_grains`` directory located under your
|
||||
:conf_master:`file_roots`. Before adding a grain to salt, consider what the grain
|
||||
is and remember that grains need to be static data.
|
||||
|
||||
Examples of Grains
|
||||
------------------
|
||||
|
@ -36,3 +56,10 @@ The core module in the grains package is where the main grains are loaded by
|
|||
the salt minion and the principal example of how to write grains:
|
||||
|
||||
:blob:`salt/grains/core.py`
|
||||
|
||||
Syncing Grains
|
||||
--------------
|
||||
|
||||
Syncing grains can be done a number of ways, they are automatically synced when
|
||||
state.highstate is called, or the grains can be synced and reloaded by calling
|
||||
the saltutil.sync_grains or saltutil.sync_all functions.
|
||||
|
|
|
@ -37,6 +37,7 @@ Full list of builtin modules
|
|||
network
|
||||
pacman
|
||||
pip
|
||||
pkg
|
||||
ps
|
||||
publish
|
||||
puppet
|
||||
|
@ -51,6 +52,7 @@ Full list of builtin modules
|
|||
ssh
|
||||
state
|
||||
status
|
||||
sys
|
||||
systemd
|
||||
test
|
||||
tomcat
|
||||
|
|
15
doc/ref/modules/all/salt.modules.pkg.rst
Normal file
15
doc/ref/modules/all/salt.modules.pkg.rst
Normal file
|
@ -0,0 +1,15 @@
|
|||
================
|
||||
salt.modules.pkg
|
||||
================
|
||||
|
||||
.. py:module:: salt.modules.pkg
|
||||
:synopsis: A virtual module for installing software packages
|
||||
|
||||
``pkg`` is a virtual module that is fulfilled by one of the following modules:
|
||||
|
||||
* :mod:`salt.modules.apt`
|
||||
* :mod:`salt.modules.ebuild`
|
||||
* :mod:`salt.modules.freebsdpkg`
|
||||
* :mod:`salt.modules.pacman`
|
||||
* :mod:`salt.modules.yumpkg`
|
||||
* :mod:`salt.modules.yumpkg5`
|
24
doc/ref/modules/all/salt.modules.sys.rst
Normal file
24
doc/ref/modules/all/salt.modules.sys.rst
Normal file
|
@ -0,0 +1,24 @@
|
|||
================
|
||||
salt.modules.sys
|
||||
================
|
||||
|
||||
A pseudo-module for working with modules on a minion.
|
||||
|
||||
.. py:module:: salt.modules.sys
|
||||
|
||||
.. py:function:: doc([module[, module.function]])
|
||||
|
||||
Display the inline documentation for all available modules, or for the
|
||||
specified module or function.
|
||||
|
||||
.. py:function:: reload_modules
|
||||
|
||||
Instruct the minion to reload all available modules in memory.
|
||||
|
||||
.. py:function:: list_modules
|
||||
|
||||
List all available (loaded) modules.
|
||||
|
||||
.. py:function:: list_functions
|
||||
|
||||
List all known functions that are in available (loaded) modules.
|
|
@ -17,6 +17,35 @@ anything. This means that the Salt states could be managed by xml files, html
|
|||
files, puppet files, or any format that can be translated into the data
|
||||
structure used by the state system.
|
||||
|
||||
Multiple Renderers
|
||||
------------------
|
||||
|
||||
When deploying a state tree a default renderer is selected in the master
|
||||
configuration file with the renderer option. But multiple renderers can be
|
||||
used inside the same state tree.
|
||||
|
||||
When rendering sls files Salt checks for the presence of a salt specific
|
||||
shebang line. The shebang line syntax was chosen because it is familiar to
|
||||
the target audience, the systems admin and systems engineer.
|
||||
|
||||
The shebang line directly calls the name of the renderer as it is specified
|
||||
within Salt. One of the most common reasons to use multiple renderers in to
|
||||
use the python or ``py`` renderer:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!py
|
||||
|
||||
def run():
|
||||
'''
|
||||
Install the python-mako package
|
||||
'''
|
||||
return {'include': ['python'],
|
||||
'python-mako': {'pkg': ['installed']}}
|
||||
|
||||
The first line is a shebang that references the ``py`` renderer.
|
||||
|
||||
|
||||
Writing Renderers
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ Returners
|
|||
|
||||
By default the return values of the commands sent to the salt minions are
|
||||
returned to the salt-master. But since the commands executed on the salt
|
||||
minions are detatched from the call on the salt master, there is no need for
|
||||
minions are detached from the call on the salt master, there is no need for
|
||||
the minion to return the data to the salt master.
|
||||
|
||||
This is where the returner interface comes in. Returners are modules called
|
||||
|
@ -16,6 +16,30 @@ a MongoDB server, a MySQL server, or any system!
|
|||
|
||||
.. seealso:: :ref:`Full list of builtin returners <all-salt.returners>`
|
||||
|
||||
Using Returners
|
||||
===============
|
||||
|
||||
All commands will return the command data back to the master. Adding more
|
||||
returners will ensure that the data is also sent to the specified returner
|
||||
interfaces.
|
||||
|
||||
Specifying what returners to use is done when the command is invoked:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' test.ping --return redis_return
|
||||
|
||||
This command will ensure that the redis_return returner is used.
|
||||
|
||||
It is also possible to specify multiple returners:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' test.ping --return mongo_return,redis_return,cassandra_return
|
||||
|
||||
In this scenario all three returners will be called and the data from the
|
||||
test.ping command will be sent out to the three named returers.
|
||||
|
||||
Writing a Returner
|
||||
==================
|
||||
|
||||
|
@ -24,7 +48,7 @@ function must accept a single argument. this argument is the return data from
|
|||
the called minion function. So if the minion function ``test.ping`` is called
|
||||
the value of the argument will be ``True``.
|
||||
|
||||
A simple returner is implimented here:
|
||||
A simple returner is implemented here:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -51,5 +75,5 @@ serializes the data as json and sets it in redis.
|
|||
Examples
|
||||
--------
|
||||
|
||||
The collection of builtin salt returners can be found here:
|
||||
The collection of built-in salt returners can be found here:
|
||||
:blob:`salt/returners`
|
||||
|
|
|
@ -13,8 +13,14 @@ The Salt State Tree
|
|||
|
||||
Configurable via :conf_master:`state_top`.
|
||||
|
||||
.. seealso:: :doc:`A detailed description of the top file </ref/states/top>`
|
||||
|
||||
.. glossary::
|
||||
|
||||
State tree
|
||||
A collection of ``sls`` files.
|
||||
A collection of ``sls`` files that live under the directory specified
|
||||
in :conf_master:`file_roots`. A state tree can be organized into
|
||||
:term:`sls modules`.
|
||||
|
||||
Include declaration
|
||||
-------------------
|
||||
|
@ -27,13 +33,13 @@ Include declaration
|
|||
|
||||
Occurs only in the top level of the highstate structure.
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
.. code-block:: yaml
|
||||
|
||||
include:
|
||||
- edit.vim
|
||||
- http.server
|
||||
include:
|
||||
- edit.vim
|
||||
- http.server
|
||||
|
||||
Module reference
|
||||
----------------
|
||||
|
@ -59,19 +65,50 @@ ID declaration
|
|||
|
||||
Occurs on the top level or under the :term:`extend declaration`.
|
||||
|
||||
.. note:: Naming gotchas
|
||||
|
||||
Must **not** contain a dot, otherwise highstate summary output will be
|
||||
unpredictable. (This has been fixed in versions 0.9.7 and above)
|
||||
|
||||
Must be unique across entire state tree. If the same ID declaration is
|
||||
used twice, only the first one matched will be used. All subsequent
|
||||
ID declarations with the same name will be ignored.
|
||||
|
||||
Extend declaration
|
||||
------------------
|
||||
|
||||
.. glossary::
|
||||
|
||||
Extend declaration
|
||||
Used to extend a :term:`name declaration` from an included ``sls
|
||||
module``. The keys of the extend declaration always define existing
|
||||
:term:`ID declarations <ID declaration>` which have been defined in
|
||||
included ``sls modules``.
|
||||
Extends a :term:`name declaration` from an included ``sls module``. The
|
||||
keys of the extend declaration always define existing :term:`ID
|
||||
declarations <ID declaration>` which have been defined in included
|
||||
``sls modules``.
|
||||
|
||||
Occurs only in the top level and defines a dictionary.
|
||||
|
||||
Extend declarations are useful for adding-to or overriding parts of a
|
||||
:term:`state declaration` that is defined in another ``sls`` files. In the
|
||||
following contrived example, the shown ``mywebsite.sls`` file is ``include``
|
||||
-ing and ``extend`` -ing the ``apache.sls`` module in order to add a ``watch``
|
||||
declaration that will restart Apache whenever the Apache configuration file,
|
||||
``mywebsite`` changes.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
include:
|
||||
- apache
|
||||
|
||||
extend:
|
||||
apache:
|
||||
service:
|
||||
- watch:
|
||||
- file: mywebsite
|
||||
|
||||
mywebsite:
|
||||
file:
|
||||
- managed
|
||||
|
||||
State declaration
|
||||
-----------------
|
||||
|
||||
|
@ -122,8 +159,17 @@ Function declaration
|
|||
.. glossary::
|
||||
|
||||
Function declaration
|
||||
The name of the function to call within the state. Any given state
|
||||
declaration can only have a single function.
|
||||
The name of the function to call within the state. A state declaration
|
||||
can contain only a single function declaration.
|
||||
|
||||
For example, the following state declaration calls the :mod:`installed
|
||||
<salt.states.pkg.installed>` function in the ``pkg`` state module:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
httpd:
|
||||
pkg:
|
||||
- installed
|
||||
|
||||
Occurs as the only index in the :term:`state declaration` list.
|
||||
|
||||
|
@ -139,16 +185,75 @@ Function arg declaration
|
|||
|
||||
Occurs under a :term:`function declaration`.
|
||||
|
||||
For example in the following state declaration ``user``, ``group``, and
|
||||
``mode`` are passed as arguments to the :mod:`managed
|
||||
<salt.states.file.managed>` function in the ``file`` state module:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
/etc/http/conf/http.conf:
|
||||
file:
|
||||
- managed
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 644
|
||||
|
||||
Name declaration
|
||||
----------------
|
||||
|
||||
.. glossary::
|
||||
|
||||
Name declaration
|
||||
Used to override the name argument relative the :term:`state
|
||||
declaration`. If the name is not specified then the :term:`ID
|
||||
declaration` satisfies the name argument. The name is always a single
|
||||
key dictionary referencing a string.
|
||||
Overrides the ``name`` argument of a :term:`state declaration`. If
|
||||
``name`` is not specified the :term:`ID declaration` satisfies the
|
||||
``name`` argument.
|
||||
|
||||
The name is always a single key dictionary referencing a string.
|
||||
|
||||
Overriding ``name`` is useful for a variety of scenarios.
|
||||
|
||||
For example, avoiding clashing ID declarations. The following two state
|
||||
declarations cannot both have ``/etc/motd`` as the ID declaration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
motd_perms:
|
||||
file:
|
||||
- managed
|
||||
- name: /etc/motd
|
||||
- mode: 644
|
||||
|
||||
motd_quote:
|
||||
file:
|
||||
- append
|
||||
- name: /etc/motd
|
||||
- text: "Of all smells, bread; of all tastes, salt."
|
||||
|
||||
Another common reason to override ``name`` is if the ID declaration is long and
|
||||
needs to be referenced in multiple places. In the example below it is much
|
||||
easier to specify ``mywebsite`` than to specify
|
||||
``/etc/apache2/sites-available/mywebsite.com`` multiple times:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
mywebsite:
|
||||
file:
|
||||
- managed
|
||||
- name: /etc/apache2/sites-available/mywebsite.com
|
||||
- source: salt://mywebsite.com
|
||||
|
||||
a2ensite mywebsite.com:
|
||||
cmd:
|
||||
- wait
|
||||
- unless: test -L /etc/apache2/sites-enabled/mywebsite.com
|
||||
- watch:
|
||||
- file: mywebsite
|
||||
|
||||
apache2:
|
||||
service:
|
||||
- running
|
||||
- watch:
|
||||
- file: mywebsite
|
||||
|
||||
Names declaration
|
||||
-----------------
|
||||
|
@ -156,20 +261,37 @@ Names declaration
|
|||
.. glossary::
|
||||
|
||||
Names declaration
|
||||
Used to apply the contents of the :term:`state declaration` to multiple
|
||||
states, each with its own name.
|
||||
Expands the contents of the containing :term:`state declaration` into
|
||||
multiple state declarations, each with its own name.
|
||||
|
||||
Example:
|
||||
For example, given the following state declaration:
|
||||
|
||||
.. code-block:: yaml
|
||||
.. code-block:: yaml
|
||||
|
||||
python-pkgs:
|
||||
pkg:
|
||||
- installed
|
||||
- names:
|
||||
- python-django
|
||||
- python-crypto
|
||||
- python-yaml
|
||||
python-pkgs:
|
||||
pkg:
|
||||
- installed
|
||||
- names:
|
||||
- python-django
|
||||
- python-crypto
|
||||
- python-yaml
|
||||
|
||||
Once converted into the :term:`lowstate` data structure the above state
|
||||
declaration will be expaneded into the following three state declarations:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
python-django:
|
||||
pkg:
|
||||
- installed
|
||||
|
||||
python-crypto:
|
||||
pkg:
|
||||
- installed
|
||||
|
||||
python-yaml:
|
||||
pkg:
|
||||
- installed
|
||||
|
||||
Large example
|
||||
=============
|
||||
|
@ -182,40 +304,22 @@ components.
|
|||
<Include Declaration>:
|
||||
- <Module Reference>
|
||||
- <Module Reference>
|
||||
|
||||
<Extend Declaration>:
|
||||
<ID Declaration>:
|
||||
<State Declaration>:
|
||||
- <Function>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Name>: <name>
|
||||
- <Requisite Declaration>:
|
||||
- <Requisite Reference>
|
||||
- <Requisite Reference>
|
||||
<ID Declaration>:
|
||||
<State Declaration>:
|
||||
- <Function>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Names>:
|
||||
- <name>
|
||||
- <name>
|
||||
- <name>
|
||||
- <Requisite Declaration>:
|
||||
- <Requisite Reference>
|
||||
- <Requisite Reference>
|
||||
[<overrides>]
|
||||
|
||||
<ID Declaration>:
|
||||
<State Declaration>:
|
||||
- <Function>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Function Arg>
|
||||
- <Name>
|
||||
- <Name>: <name>
|
||||
- <Requisite Declaration>:
|
||||
- <Requisite Reference>
|
||||
- <Requisite Reference>
|
||||
|
||||
<ID Declaration>:
|
||||
<State Declaration>:
|
||||
- <Function>
|
||||
|
|
|
@ -104,32 +104,32 @@ Here is an example of a Salt State:
|
|||
.. code-block:: yaml
|
||||
|
||||
vim:
|
||||
pkg:
|
||||
- installed
|
||||
pkg:
|
||||
- installed
|
||||
|
||||
salt:
|
||||
pkg:
|
||||
- latest
|
||||
service:
|
||||
- running
|
||||
- require:
|
||||
- file: /etc/salt/minion
|
||||
- pkg: salt
|
||||
- names:
|
||||
- salt-master
|
||||
- salt-minion
|
||||
- watch:
|
||||
- file: /etc/salt/minion
|
||||
pkg:
|
||||
- latest
|
||||
service:
|
||||
- running
|
||||
- require:
|
||||
- file: /etc/salt/minion
|
||||
- pkg: salt
|
||||
- names:
|
||||
- salt-master
|
||||
- salt-minion
|
||||
- watch:
|
||||
- file: /etc/salt/minion
|
||||
|
||||
/etc/salt/minion:
|
||||
file:
|
||||
- managed
|
||||
- source: salt://salt/minion
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 644
|
||||
- require:
|
||||
- pkg: salt
|
||||
file:
|
||||
- managed
|
||||
- source: salt://salt/minion
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 644
|
||||
- require:
|
||||
- pkg: salt
|
||||
|
||||
This short stanza will ensure that vim is installed, salt is installed and up
|
||||
to date, the salt-master and salt-minion daemons are running and the Salt
|
||||
|
@ -150,13 +150,13 @@ lists of Salt states sent to the matching minions:
|
|||
.. code-block:: yaml
|
||||
|
||||
base:
|
||||
'*':
|
||||
- salt
|
||||
- users
|
||||
- users.admin
|
||||
'saltmaster.*':
|
||||
- match: pcre
|
||||
- salt.master
|
||||
'*':
|
||||
- salt
|
||||
- users
|
||||
- users.admin
|
||||
'saltmaster.*':
|
||||
- match: pcre
|
||||
- salt.master
|
||||
|
||||
This simple example uses the base environment, which is built into the default
|
||||
salt setup, and then all minions will have the modules salt, users and
|
||||
|
|
|
@ -4,17 +4,22 @@ Ordering States
|
|||
|
||||
When creating salt sls files, it is often important to ensure that they run in
|
||||
a specific order. While states will always execute in the same order, that
|
||||
order is not nessisarily defined the way you want it.
|
||||
order is not necessarily defined the way you want it.
|
||||
|
||||
A few tools exist in Salt to set up the corect state ordering, these tools
|
||||
A few tools exist in Salt to set up the correct state ordering. These tools
|
||||
consist of requisite declarations and order options.
|
||||
|
||||
.. note::
|
||||
|
||||
Salt does **not** execute :term:`state declarations <state declaration>` in
|
||||
the order they appear in the source.
|
||||
|
||||
The Order Option
|
||||
================
|
||||
|
||||
Before using the order option, remember that the majority of state ordering
|
||||
should be done with requisite statements, and that a requisite statement
|
||||
will override an order option.
|
||||
should be done with a :term:`requisite declaration`, and that a requisite
|
||||
declaration will override an order option.
|
||||
|
||||
The order option is used by adding an order number to a state declaration
|
||||
with the option `order`:
|
||||
|
@ -32,9 +37,9 @@ installed in tandem with any other state declaration set to the order `1`.
|
|||
Any state declared without an order option will be executed after all states
|
||||
with order options are executed.
|
||||
|
||||
But this construct can only handle ordering states from the beggining.
|
||||
Sometimes you may want to send a state to the end of the line, to do this
|
||||
set the order to last:
|
||||
But this construct can only handle ordering states from the beginning.
|
||||
Sometimes you may want to send a state to the end of the line. To do this,
|
||||
set the order to ``last``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
|
143
doc/ref/states/top.rst
Normal file
143
doc/ref/states/top.rst
Normal file
|
@ -0,0 +1,143 @@
|
|||
============
|
||||
The Top File
|
||||
============
|
||||
|
||||
The top file is used to map what sls modules get loaded onto what minions via
|
||||
the state system. The top file creates a few general abstractions. First it
|
||||
maps what nodes should pull from which environments, next it defines which
|
||||
matches systems should draw from.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
file_roots:
|
||||
base:
|
||||
- /srv/salt
|
||||
|
||||
This means that the top file will only have one environment to pull from,
|
||||
here is a simple, single environment top file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
base:
|
||||
'*':
|
||||
- core
|
||||
- edit
|
||||
|
||||
This also means that /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:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
file_roots:
|
||||
base:
|
||||
- /srv/salt/base
|
||||
dev:
|
||||
- /srv/salt/dev
|
||||
qa:
|
||||
- /srv/salt/qa
|
||||
prod:
|
||||
- /srv/salt/prod
|
||||
|
||||
Then our top file could reference the environments:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
dev:
|
||||
'webserver*dev*':
|
||||
- webserver
|
||||
'db*dev*':
|
||||
- db
|
||||
qa:
|
||||
'webserver*qa*':
|
||||
- webserver
|
||||
'db*qa*':
|
||||
- db
|
||||
prod:
|
||||
'webserver*prod*':
|
||||
- webserver
|
||||
'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
|
||||
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:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
base:
|
||||
'*':
|
||||
- global
|
||||
dev:
|
||||
'webserver*dev*':
|
||||
- webserver
|
||||
'db*dev*':
|
||||
- db
|
||||
qa:
|
||||
'webserver*qa*':
|
||||
- webserver
|
||||
'db*qa*':
|
||||
- db
|
||||
prod:
|
||||
'webserver*prod*':
|
||||
- webserver
|
||||
'db*prod*':
|
||||
- db
|
||||
|
||||
In this setup all systems will pull the global sls from the base environment,
|
||||
as well as pull from their respective environments.
|
||||
|
||||
Remember, that since everything is a file in salt, the environments are
|
||||
primarily file server environments, this means that environments that have
|
||||
nothing to do with states can be defined and used to distribute other files.
|
||||
|
||||
A clean and recommended setup for multiple environments would look like this:
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Master file_roots configuration:
|
||||
file_roots:
|
||||
base:
|
||||
- /srv/salt/base
|
||||
dev:
|
||||
- /srv/salt/dev
|
||||
qa:
|
||||
- /srv/salt/qa
|
||||
prod:
|
||||
- /srv/salt/prod
|
||||
|
||||
Then only place state trees in the dev, qa and prod environments, leaving
|
||||
the base environment open for generic file transfers. Then the top.sls file
|
||||
would look something like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
dev:
|
||||
'webserver*dev*':
|
||||
- webserver
|
||||
'db*dev*':
|
||||
- db
|
||||
qa:
|
||||
'webserver*qa*':
|
||||
- webserver
|
||||
'db*qa*':
|
||||
- db
|
||||
prod:
|
||||
'webserver*prod*':
|
||||
- webserver
|
||||
'db*prod*':
|
||||
- db
|
||||
|
||||
|
|
@ -14,7 +14,6 @@ passed to the SLS data structures will map directly to the states modules.
|
|||
Mapping the information from the SLS data is simple, this example should
|
||||
illustrate:
|
||||
|
||||
SLS file
|
||||
.. code-block:: yaml
|
||||
|
||||
/etc/salt/master: # maps to "name"
|
||||
|
@ -32,14 +31,20 @@ This does issue the burden, that function names, state names and function
|
|||
arguments should be very human readable inside state modules, since they
|
||||
directly define the user interface.
|
||||
|
||||
Using Custom State Modules
|
||||
==========================
|
||||
|
||||
Place your custom state modules inside a ``_states`` directory within the
|
||||
``file_roots`` specified by the master config file.
|
||||
|
||||
Cross Calling Modules
|
||||
=====================
|
||||
|
||||
As with Execution Modules State Modules can also make use of the ``__salt__``
|
||||
As with Execution Modules, State Modules can also make use of the ``__salt__``
|
||||
and ``__grains__`` data.
|
||||
|
||||
It is important to note, that the real work of state management should not be
|
||||
done in the state module unless it is needed, a good example is the pkg state
|
||||
It is important to note that the real work of state management should not be
|
||||
done in the state module unless it is needed. A good example is the pkg state
|
||||
module. This module does not do any package management work, it just calls the
|
||||
pkg execution module. This makes the pkg state module completely generic, which
|
||||
is why there is only one pkg state module and many backend pkg execution
|
||||
|
@ -49,3 +54,18 @@ On the other hand some modules will require that the logic be placed in the
|
|||
state module, a good example of this is the file module. But in the vast
|
||||
majority of cases this is not the best approach, and writing specific
|
||||
execution modules to do the backend work will be the optimal solution.
|
||||
|
||||
Return Data
|
||||
===========
|
||||
|
||||
A State Module must return a dict containing the following keys/values:
|
||||
|
||||
- **name:** The same value passed to the state as "name".
|
||||
- **changes:** A dict describing the changes made. Each thing changed should
|
||||
be a key, with its value being another dict with keys called "old" and "new"
|
||||
containing the old/new values. For example, the pkg state's **changes** dict
|
||||
has one key for each package changed, with the "old" and "new" keys in its
|
||||
sub-dict containing the old and new versions of the package.
|
||||
- **result:** A boolean value. *True* if the action was successful, otherwise
|
||||
*False*.
|
||||
- **comment:** A string containing a summary of the result.
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
===============
|
||||
Installing Salt
|
||||
===============
|
||||
|
||||
The Salt system setup is amazingly simple, as this is one of the central design
|
||||
goals of Salt. Setting up Salt only requires that the Salt :term:`master` be
|
||||
running and the Salt :term:`minions <minion>` point to the master.
|
||||
|
||||
.. admonition:: Salt dependencies
|
||||
|
||||
Salt should run on any Unix-like platform so long as the dependencies are
|
||||
met.
|
||||
|
||||
* `Python 2.6`_
|
||||
* `ZeroMQ`_ >= 2.1.9
|
||||
* `pyzmq`_ >= 2.1.9 — ZeroMQ Python bindings
|
||||
* `M2Crypto`_ — Python OpenSSL wrapper
|
||||
* `PyCrypto`_ — The Python cryptography toolkit
|
||||
* `YAML`_ — Python YAML bindings
|
||||
|
||||
Optional Dependencies:
|
||||
|
||||
* `Jinja2`_ — parsing Salt States (other renderers can be used via the
|
||||
:conf_master:`renderer` setting).
|
||||
* gcc — dynamic `Cython`_ module compiling
|
||||
|
||||
.. _`Python 2.6`: http://python.org/download/
|
||||
.. _`ZeroMQ`: http://www.zeromq.org/
|
||||
.. _`pyzmq`: https://github.com/zeromq/pyzmq
|
||||
.. _`M2Crypto`: http://chandlerproject.org/Projects/MeTooCrypto
|
||||
.. _`YAML`: http://pyyaml.org/
|
||||
.. _`PyCrypto`: http://www.dlitz.net/software/pycrypto/
|
||||
.. _`Cython`: http://cython.org/
|
||||
.. _`Jinja2`: http://jinja.pocoo.org/
|
||||
|
||||
.. contents:: Instructions by operating system
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
Red Hat
|
||||
=======
|
||||
|
||||
We are working to get Salt packages into EPEL. In the meantime you can
|
||||
:command:`yum install salt-master salt-minion` via our Fedora People
|
||||
repository.
|
||||
|
||||
Red Hat Enterprise Linux 5 & 6 or CentOS 5 & 6
|
||||
----------------------------------------------
|
||||
|
||||
1. Install the `EPEL`__ repository.
|
||||
|
||||
2. Install our repository on FedoraPeople::
|
||||
|
||||
wget -O /etc/yum.repos.d/epel-salt.repo \
|
||||
http://repos.fedorapeople.org/repos/herlo/salt/epel-salt.repo
|
||||
|
||||
.. __: http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F
|
||||
|
||||
Fedora 15 & 16
|
||||
--------------
|
||||
|
||||
1. Install our repository on FedoraPeople::
|
||||
|
||||
wget -O /etc/yum.repos.d/fedora-salt.repo \
|
||||
http://repos.fedorapeople.org/repos/herlo/salt/fedora-salt.repo
|
||||
|
||||
Arch Linux
|
||||
==========
|
||||
|
||||
Salt can be easily installed from the Arch Linux AUR in one of two flavors:
|
||||
|
||||
* `Install a Salt release <https://aur.archlinux.org/packages.php?ID=47512>`_
|
||||
* `Install the latest Salt from Git <https://aur.archlinux.org/packages.php?ID=47513>`_
|
||||
|
||||
Debian / Ubuntu
|
||||
===============
|
||||
|
||||
Ubuntu
|
||||
------
|
||||
|
||||
We are working to get Salt into apt. In the meantime we have a PPA available
|
||||
for Lucid::
|
||||
|
||||
aptitude -y install python-software-properties
|
||||
add-apt-repository ppa:chris-lea/libpgm
|
||||
add-apt-repository ppa:chris-lea/zeromq
|
||||
add-apt-repository ppa:saltstack/salt
|
||||
aptitude update
|
||||
aptitude install salt
|
||||
|
||||
Debian
|
||||
------
|
||||
|
||||
`A deb package is currently in testing`__ for inclusion in apt. Until that is
|
||||
accepted you can install Salt by downloading the latest ``.deb`` in the
|
||||
`downloads section on GitHub`__ and installing that manually:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
dpkg -i salt-|version|.deb
|
||||
|
||||
.. __: http://mentors.debian.net/package/salt
|
||||
.. __: https://github.com/saltstack/salt/downloads
|
||||
|
||||
.. admonition:: Installing ZeroMQ on Squeeze (Debian 6)
|
||||
|
||||
There is a `python-zmq`__ package available in Debian "wheezy (testing)".
|
||||
If you don't have that repo enabled the best way to install Salt and pyzmq
|
||||
is by using :command:`pip` (or :command:`easy_install`)::
|
||||
|
||||
pip install pyzmq salt
|
||||
|
||||
.. __: http://packages.debian.org/search?keywords=python-zmq
|
||||
|
||||
Gentoo
|
||||
======
|
||||
|
||||
Salt can be easily installed on Gentoo::
|
||||
|
||||
emerge pyyaml m2crypto pycrypto jinja pyzmq
|
||||
|
||||
Then download and install from source:
|
||||
|
||||
1. Download the latest source tarball from the GitHub downloads directory for
|
||||
the Salt project: |latest|
|
||||
|
||||
2. Untar the tarball and run the :file:`setup.py` as root:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
tar xvf salt-|version|.tar.gz
|
||||
cd salt-|version|
|
||||
python2 setup.py install
|
||||
|
||||
FreeBSD
|
||||
=======
|
||||
|
||||
Salt is available in the FreeBSD ports tree::
|
||||
|
||||
cd /usr/ports/sysutils/salt && make install clean
|
||||
|
||||
.. seealso:: :doc:`freebsd installation guide </topics/tutorials/freebsd>`
|
||||
|
||||
Installing from source
|
||||
======================
|
||||
|
||||
1. Download the latest source tarball from the GitHub downloads directory for
|
||||
the Salt project: |latest|
|
||||
|
||||
2. Untar the tarball and run the :file:`setup.py` as root:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
tar xvf salt-|version|.tar.gz
|
||||
cd salt-|version|
|
||||
python2 setup.py install
|
72
doc/topics/jobs/index.rst
Normal file
72
doc/topics/jobs/index.rst
Normal file
|
@ -0,0 +1,72 @@
|
|||
==============
|
||||
Job Management
|
||||
==============
|
||||
|
||||
.. versionadded:: 0.9.7
|
||||
|
||||
Since Salt executes jobs running on many systems, Salt needs to be able to
|
||||
manage jobs running on many systems. As of Salt 0.9.7 the capability was
|
||||
added for more advanced job management.
|
||||
|
||||
The Minion proc System
|
||||
======================
|
||||
|
||||
The Salt Minions now maintain a proc directory in the salt cachedir, the proc
|
||||
directory maintains files named after the executed job id. These files contain
|
||||
the information about the current running jobs on the minion and allow for
|
||||
jobs to be looked up. This is located in the proc directory under the
|
||||
cachedir, with a default configuration it is under /var/cache/salt/proc.
|
||||
|
||||
Functions in the saltutil Module
|
||||
================================
|
||||
|
||||
Salt 0.9.7 introduced a few new functions to the saltutil module for managing
|
||||
jobs. These functions are:
|
||||
1. running
|
||||
Returns the data of all running jobs that are found in the proc directory.
|
||||
2. find_job
|
||||
Returns specific data about a certain job based on job id.
|
||||
3. signal_job
|
||||
Allows for a given jid to be sent a signal.
|
||||
4. term_job
|
||||
Sends a termination signal (SIGTERM, 15) to the process controlling the
|
||||
specified job.
|
||||
5. kill_job
|
||||
Sends a kill signal (SIGKILL, 9) to the process controlling the
|
||||
specified job.
|
||||
|
||||
These functions make up the core of the back end used to manage jobs at the
|
||||
minion level.
|
||||
|
||||
The jobs Runner
|
||||
===============
|
||||
|
||||
A convenience runner front end and reporting system has been added as well.
|
||||
The jobs runner contains functions to make viewing data easier and cleaner.
|
||||
|
||||
The jobs runner contains a number of functions...
|
||||
|
||||
active
|
||||
------
|
||||
|
||||
The active function runs saltutil.running on all minions and formats the
|
||||
return data about all running jobs in a much more usable and compact format.
|
||||
The active function will also compare jobs that have returned and jobs that
|
||||
are still running, making it easier to see what systems have completed a job
|
||||
and what systems are still being waited on.
|
||||
|
||||
lookup_jid
|
||||
----------
|
||||
|
||||
When jobs are executed the return data is sent back to the master and cached.
|
||||
By default is is cached for 24 hours, but this can be configured via the
|
||||
``keep_jobs`` option in the master configuration.
|
||||
Using the lookup_jid runner will display the same return data that the initial
|
||||
job invocation with the salt command would display.
|
||||
|
||||
list_jobs
|
||||
---------
|
||||
|
||||
Before finding a historic job, it may be required to find the job id. list_jobs
|
||||
will parse the cached execution data and display all of the job data for jobs
|
||||
that have already, or partially returned.
|
|
@ -29,8 +29,8 @@ Here s the md5sum:
|
|||
|
||||
7d5aca4633bc22f59045f59e82f43b56
|
||||
|
||||
For instructions on how to set up Salt please see the :doc:`installation
|
||||
instructions </topics/installation>`.
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
|
|
@ -24,8 +24,8 @@ Here is the md5sum:
|
|||
|
||||
9a925da04981e65a0f237f2e77ddab37
|
||||
|
||||
For instructions on how to set up Salt please see the :doc:`installation
|
||||
instructions </topics/installation>`.
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
|
|
@ -23,8 +23,8 @@ 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 :doc:`installation
|
||||
instructions </topics/installation>`.
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
|
|
@ -22,8 +22,8 @@ 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 :doc:`installation
|
||||
instructions </topics/installation>`.
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
|
|
@ -24,8 +24,8 @@ 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 :doc:`installation
|
||||
instructions </topics/installation>`.
|
||||
For instructions on how to set up Salt please see the `installation`_
|
||||
instructions.
|
||||
|
||||
New Features
|
||||
============
|
||||
|
|
47
doc/topics/releases/0.9.6.rst
Normal file
47
doc/topics/releases/0.9.6.rst
Normal file
|
@ -0,0 +1,47 @@
|
|||
========================
|
||||
Salt 0.9.6 Release Notes
|
||||
========================
|
||||
|
||||
Salt 0.9.6 is a release targeting a few bugs and changes. This is primarily
|
||||
targeting an issue found in the names declaration in the state system. But a
|
||||
few other bugs were also repaired, like missing support for grains in extmods.
|
||||
|
||||
Due to a conflict in distribution packaging msgpack will no longer be bundled
|
||||
with Salt, and is required as a dependency.
|
||||
|
||||
New Features
|
||||
============
|
||||
|
||||
http and ftp support in files.managed
|
||||
-------------------------------------
|
||||
|
||||
Now under the source option in the file.managed state a http or ftp address
|
||||
can be used instead of a file located on the salt master.
|
||||
|
||||
Allow Multiple Returners
|
||||
------------------------
|
||||
|
||||
Now the returner interface can define multiple returners, and will also return
|
||||
data back to the master, making the process less ambiguous.
|
||||
|
||||
Minion Memory Improvements
|
||||
--------------------------
|
||||
|
||||
A number of modules have been taken out of the minion if the underlying
|
||||
systems required by said modules are not present on the minion system.
|
||||
A number of other modules need to be stripped out in this same way which
|
||||
should continue to make the minion more efficient.
|
||||
|
||||
Minions Can Locally Cache Return Data
|
||||
-------------------------------------
|
||||
|
||||
A new option, cache_jobs, has been added to the minion to allow for all of the
|
||||
historically run jobs to cache on the minion, allowing for looking up historic
|
||||
returns. By default cache_jobs is set to False.
|
||||
|
||||
Pure Python Template Support For file.managed
|
||||
---------------------------------------------
|
||||
|
||||
Templates in the file.managed state can now be defined in a python script.
|
||||
This script needs to have a run function that returns the string that needs to
|
||||
be in the named file.
|
179
doc/topics/releases/0.9.7.rst
Normal file
179
doc/topics/releases/0.9.7.rst
Normal file
|
@ -0,0 +1,179 @@
|
|||
========================
|
||||
Salt 0.9.7 Release Notes
|
||||
========================
|
||||
|
||||
Salt 0.9.7 is here! The latest iteration of Salt brings more features and many
|
||||
fixes. This release is a great refinement over 0.9.6, adding many conveniences
|
||||
under the hood, as well as some features that make working with Salt much
|
||||
better.
|
||||
|
||||
A few highlights include the new Job system, refinements to the requisite
|
||||
system in states, the mod_init interface for states, external node
|
||||
classification, search path to managed files in the file state, and refinements
|
||||
and additions to dynamic module loading.
|
||||
|
||||
0.9.7 also introduces the long developed (and oft changed) unit test framework
|
||||
and the initial unit tests.
|
||||
|
||||
|
||||
Major Features
|
||||
==============
|
||||
|
||||
Salt Jobs Interface
|
||||
-------------------
|
||||
|
||||
The new jobs interface makes the management of running executions much cleaner
|
||||
and more transparent. Building on the existing execution framework the jobs
|
||||
system allows clear introspection into the active running state of the,
|
||||
running Salt interface.
|
||||
|
||||
The Jobs interface is centered in the new minion side proc system. The
|
||||
minions now store a msgpack serialized file under ``cachedir``/proc. These
|
||||
files keep track of the active state of processes on the minion.
|
||||
|
||||
Functions in the saltutil Module
|
||||
````````````````````````````````
|
||||
|
||||
A number of functions have been added to the saltutil module to manage and
|
||||
view the jobs:
|
||||
|
||||
1. running
|
||||
Returns the data of all running jobs that are found in the proc directory.
|
||||
2. find_job
|
||||
Returns specific data about a certain job based on job id.
|
||||
3. signal_job
|
||||
Allows for a given jid to be sent a signal.
|
||||
4. term_job
|
||||
Sends a termination signal (SIGTERM, 15) to the process controlling the
|
||||
specified job.
|
||||
5. kill_job
|
||||
Sends a kill signal (SIGKILL, 9) to the process controlling the
|
||||
specified job.
|
||||
|
||||
The jobs Runner
|
||||
---------------
|
||||
|
||||
A convenience runner front end and reporting system has been added as well.
|
||||
The jobs runner contains functions to make viewing data easier and cleaner.
|
||||
|
||||
The jobs runner contains a number of functions...
|
||||
|
||||
active
|
||||
``````
|
||||
|
||||
The active function runs saltutil.running on all minions and formats the
|
||||
return data about all running jobs in a much more usable and compact format.
|
||||
The active function will also compare jobs that have returned and jobs that
|
||||
are still running, making it easier to see what systems have completed a job
|
||||
and what systems are still being waited on.
|
||||
|
||||
lookup_jid
|
||||
``````````
|
||||
|
||||
When jobs are executed the return data is sent back to the master and cached.
|
||||
By default is is cached for 24 hours, but this can be configured via the
|
||||
``keep_jobs`` option in the master configuration.
|
||||
Using the lookup_jid runner will display the same return data that the initial
|
||||
job invocation with the salt command would display.
|
||||
|
||||
list_jobs
|
||||
`````````
|
||||
|
||||
Before finding a historic job, it may be required to find the job id. list_jobs
|
||||
will parse the cached execution data and display all of the job data for jobs
|
||||
that have already, or partially returned.
|
||||
|
||||
|
||||
External Node Classification
|
||||
----------------------------
|
||||
|
||||
Salt can now use external node classifiers like Cobbler's
|
||||
``cobbler-ext-nodes``.
|
||||
|
||||
Salt uses specific data from the external node classifier. In particular the
|
||||
classes value denotes which sls modules to run, and the environment value sets
|
||||
to another environment.
|
||||
|
||||
An external node classification can be set in the master configuration file via
|
||||
the ``external_nodes`` option:
|
||||
http://salt.readthedocs.org/en/latest/ref/configuration/master.html#external-nodes
|
||||
|
||||
External nodes are loaded in addition to the top files. If it is intended to
|
||||
only use external nodes, do not deploy any top files.
|
||||
|
||||
State Mod Init System
|
||||
---------------------
|
||||
|
||||
An issue arose with the pkg state. Every time a package was run Salt would
|
||||
need to refresh the package database. This made systems with slower package
|
||||
metadata refresh speeds much slower to work with. To alleviate this issue the
|
||||
mod_init interface has been added to salt states.
|
||||
|
||||
The mod_init interface is a function that can be added to a state file.
|
||||
This function is called with the first state called. In the case of the pkg
|
||||
state, the mod_init function sets up a tag which makes the package database
|
||||
only refresh on the first attempt to install a package.
|
||||
|
||||
In a nutshell, the mod_init interface allows a state to run any command that
|
||||
only needs to be run once, or can be used to set up an environment for working
|
||||
with the state.
|
||||
|
||||
Source File Search Path
|
||||
-----------------------
|
||||
|
||||
The file state continues to be refined, adding speed and capabilities. This
|
||||
release adds the ability to pass a list to the source option. This list is then
|
||||
iterated over until the source file is found, and the first found file is used.
|
||||
|
||||
The new syntax looks like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
/etc/httpd/conf/httpd.conf:
|
||||
file:
|
||||
- managed
|
||||
- source:
|
||||
- salt://httpd/httpd.conf
|
||||
- http://myserver/httpd.conf: md5=8c1fe119e6f1fd96bc06614473509bf1
|
||||
|
||||
The source option can take sources in the list from the salt file server
|
||||
as well as an arbitrary web source. If using an arbitrary web source the
|
||||
checksum needs to be passed as well for file verification.
|
||||
|
||||
Refinements to the Requisite System
|
||||
-----------------------------------
|
||||
|
||||
A few discrepancies were still lingering in the requisite system, in
|
||||
particular, it was not possible to have a ``require`` and a ``watch`` requisite
|
||||
declared in the same state declaration.
|
||||
|
||||
This issue has been alleviated, as well as making the requisite system run
|
||||
more quickly.
|
||||
|
||||
Initial Unit Testing Framework
|
||||
------------------------------
|
||||
|
||||
Because of the module system, and the need to test real scenarios the,
|
||||
development of a viable unit testing system has been difficult, but unit
|
||||
testing has finally arrived. Only a small amount of unit testing coverage
|
||||
has been developed, much more coverage will be in place soon.
|
||||
|
||||
A huge thanks goes out to those who have helped with unit testing, and the
|
||||
contributions that have been made to get us where we are. Without these
|
||||
contributions unit tests would still be in the dark.
|
||||
|
||||
Compound Targets Expanded
|
||||
-------------------------
|
||||
|
||||
Originally only support for ``and`` and ``or`` were available in the compound
|
||||
target. 0.9.7 adds the capability to negate compound targets with ``not``.
|
||||
|
||||
Nodegroups in the Top File
|
||||
--------------------------
|
||||
|
||||
Previously the nodegroups defined in the master configuration file could not
|
||||
be used to match nodes for states. The nodegroups support has been expanded
|
||||
and the nodegroups defined in the master configuration can now be used to
|
||||
match minions in the top file.
|
||||
|
||||
|
50
doc/topics/tutorials/bootstrap_ec2.rst
Normal file
50
doc/topics/tutorials/bootstrap_ec2.rst
Normal file
|
@ -0,0 +1,50 @@
|
|||
==============================================
|
||||
Boostrapping Salt on Linux EC2 with Cloud-Init
|
||||
==============================================
|
||||
|
||||
`Salt <http://saltstack.org>`_ is a great tool for remote execution and configuration management, however you will still need to bootstrap the daemon when spinning up a new node. One option is to create and save a custom AMI, but this creates another resource to maintain and document.
|
||||
|
||||
A better method for Linux machines uses Canonical's `CloudInit <https://help.ubuntu.com/community/CloudInit>`_ to run a bootstrap script during an EC2 Instance initialization. Cloud-init takes the ``user_data`` string passed into a new AWS instance and runs it in a manner similar to rc.local. The bootstrap script needs to:
|
||||
|
||||
#. Install `Salt`_ with dependencies
|
||||
#. Point the minion to the master
|
||||
|
||||
Here is a sample script::
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
# Install saltstack
|
||||
add-apt-repository ppa:saltstack/salt -y
|
||||
apt-get update -y
|
||||
apt-get install salt -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
|
||||
salt-minion -d
|
||||
|
||||
First the script adds the saltstack ppa and installs the package. Then we copy over the minion config template and tell it where to find the master. You will have to replace ``[salt_master_fqdn]`` with something that resolves to your salt master.
|
||||
|
||||
Used With Boto
|
||||
--------------
|
||||
|
||||
`Boto <https://github.com/boto/boto>`_ will accept a string for user data which can be used to pass our bootstrap script. If the script is saved to a file, you can read it into a string::
|
||||
|
||||
import boto
|
||||
|
||||
user_data = open('salt_bootstrap.sh')
|
||||
|
||||
conn = boto.connect_ec2(<AWS_ACCESS_ID>, <AWS_SECRET_KEY>)
|
||||
|
||||
reservation = conn.run_instances(image_id=<ami_id>,
|
||||
key_name=<key_name>,
|
||||
user_data=user_data.read())
|
||||
|
||||
|
||||
Additional Notes
|
||||
-------------------
|
||||
|
||||
Sometime in the future the ppa will include and install an upstart file. In the meantime, you can use the bootstrap to `build one <https://gist.github.com/1617054>`_.
|
||||
|
||||
It may also be useful to set the node's role during this phase. One option would be saving the node's role to a file and then using a custom grain to select it.
|
|
@ -1,6 +1,6 @@
|
|||
**Before continuing** make sure you have a working Salt installation by
|
||||
following the :doc:`installation </topics/installation>` and the
|
||||
:doc:`configuration </topics/configuration>` instructions.
|
||||
following the `installation`_ and the :doc:`configuration
|
||||
</topics/configuration>` instructions.
|
||||
|
||||
.. admonition:: Stuck?
|
||||
|
||||
|
|
|
@ -149,7 +149,8 @@ directly.
|
|||
But with more than a single SLS file, more components can be added to the
|
||||
toolkit, consider this ssh example:
|
||||
|
||||
/ssh/init.sls
|
||||
``/ssh/init.sls``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
openssh-client:
|
||||
|
@ -166,7 +167,8 @@ toolkit, consider this ssh example:
|
|||
- require:
|
||||
- pkg: openssh-client
|
||||
|
||||
/ssh/server.sls
|
||||
``/ssh/server.sls``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
include:
|
||||
|
@ -231,7 +233,8 @@ needs to be placed.
|
|||
|
||||
These examples will add more watchers to apache and change the ssh banner.
|
||||
|
||||
/ssh/custom-server.sls
|
||||
``/ssh/custom-server.sls``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
include:
|
||||
|
@ -242,7 +245,8 @@ These examples will add more watchers to apache and change the ssh banner.
|
|||
file:
|
||||
- source: salt://ssh/custom-banner
|
||||
|
||||
/python/mod_python.sls
|
||||
``/python/mod_python.sls``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
include:
|
||||
|
@ -301,7 +305,8 @@ available, ``salt`` and ``grains``. The salt object allows for any salt
|
|||
function to be called from within the template, and grains allows for the
|
||||
grains to be accessed from within the template. A few examples are in order:
|
||||
|
||||
/apache/init.sls
|
||||
``/apache/init.sls``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
apache:
|
||||
|
@ -347,7 +352,8 @@ Red Hat, then the name of the apache package and service needs to be httpd.
|
|||
A more aggressive way to use Jinja can be found here, in a module to set up
|
||||
a MooseFS distributed filesystem chunkserver:
|
||||
|
||||
/moosefs/chunk.sls
|
||||
``/moosefs/chunk.sls``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
include:
|
||||
|
@ -421,7 +427,7 @@ but a SLS file set to use another renderer can be easily added to the tree.
|
|||
|
||||
This example shows a very basic python SLS file:
|
||||
|
||||
/python/django.sls
|
||||
``/python/django.sls``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
|
@ -81,6 +81,10 @@ In this case it defines the name of the package to be installed. **NOTE:** the
|
|||
package name for the Apache httpd web server may differ on your OS or distro —
|
||||
for example, on Fedora it is ``httpd`` but on Debian/Ubuntu it is ``apache2``.
|
||||
|
||||
Additionally, an ID declaration should not contain a dot, as this will produce
|
||||
unpredictable output in the summary returned from a call to
|
||||
:func:`state.highstate <salt.modules.state.highstate>`.
|
||||
|
||||
The second line, called the :term:`state declaration`, defines which of the
|
||||
Salt States we are using. In this example, we are using the :mod:`pkg state
|
||||
<salt.states.pkg>` to ensure that a given package is installed.
|
||||
|
|
|
@ -149,7 +149,7 @@ vhosts file is changed:
|
|||
- apache
|
||||
|
||||
extend:
|
||||
apache
|
||||
apache:
|
||||
service:
|
||||
- watch:
|
||||
- file: /etc/httpd/extra/httpd-vhosts.conf
|
||||
|
|
11
pkg/rpm/README.fedora
Normal file
11
pkg/rpm/README.fedora
Normal file
|
@ -0,0 +1,11 @@
|
|||
These packages are *optional* dependencies for salt. By default, they are not included in the salt RPMs.
|
||||
Install any of these packages to enable the functionality within salt.
|
||||
|
||||
MySQL-python
|
||||
libvirt-python
|
||||
python-mako
|
||||
pymongo
|
||||
python-redis / redis
|
||||
|
||||
A semi-canonical list of the optional salt modules can be found at
|
||||
https://github.com/saltstack/salt/blob/develop/doc/conf.py#L30
|
|
@ -9,8 +9,8 @@
|
|||
%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
|
||||
|
||||
Name: salt
|
||||
Version: 0.9.4
|
||||
Release: 6%{?dist}
|
||||
Version: 0.9.6
|
||||
Release: 2%{?dist}
|
||||
Summary: A parallel remote execution system
|
||||
|
||||
Group: System Environment/Daemons
|
||||
|
@ -23,6 +23,7 @@ Source3: %{name}-minion
|
|||
Source4: %{name}-master.service
|
||||
Source5: %{name}-syndic.service
|
||||
Source6: %{name}-minion.service
|
||||
Source7: README.fedora
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
BuildArch: noarch
|
||||
|
@ -33,6 +34,7 @@ BuildRequires: python26-crypto
|
|||
BuildRequires: python26-devel
|
||||
BuildRequires: python26-PyYAML
|
||||
BuildRequires: python26-m2crypto
|
||||
BuildRequires: python26-msgpack
|
||||
|
||||
Requires: python26-crypto
|
||||
Requires: python26-zmq
|
||||
|
@ -40,6 +42,7 @@ Requires: python26-jinja2
|
|||
Requires: python26-PyYAML
|
||||
Requires: python26-m2crypto
|
||||
Requires: python26-PyXML
|
||||
Requires: python26-msgpack
|
||||
|
||||
%else
|
||||
|
||||
|
@ -48,6 +51,7 @@ BuildRequires: python-crypto
|
|||
BuildRequires: python-devel
|
||||
BuildRequires: PyYAML
|
||||
BuildRequires: m2crypto
|
||||
BuildRequires: python-msgpack
|
||||
|
||||
Requires: python-crypto
|
||||
Requires: python-zmq
|
||||
|
@ -55,6 +59,7 @@ Requires: python-jinja2
|
|||
Requires: PyYAML
|
||||
Requires: m2crypto
|
||||
Requires: PyXML
|
||||
Requires: python-msgpack
|
||||
|
||||
%endif
|
||||
|
||||
|
@ -71,7 +76,7 @@ BuildRequires: systemd-units
|
|||
|
||||
%endif
|
||||
|
||||
Requires: MySQL-python libvirt-python yum
|
||||
#Requires: MySQL-python libvirt-python yum
|
||||
|
||||
%description
|
||||
Salt is a distributed remote execution system used to execute commands and
|
||||
|
@ -119,6 +124,11 @@ install -p -m 0644 %{SOURCE4} $RPM_BUILD_ROOT%{_unitdir}/
|
|||
install -p -m 0644 %{SOURCE5} $RPM_BUILD_ROOT%{_unitdir}/
|
||||
install -p -m 0644 %{SOURCE6} $RPM_BUILD_ROOT%{_unitdir}/
|
||||
%endif
|
||||
|
||||
install -p %{SOURCE7} .
|
||||
|
||||
install -p -m 0640 $RPM_BUILD_ROOT%{_sysconfdir}/salt/minion.template $RPM_BUILD_ROOT%{_sysconfdir}/salt/minion
|
||||
install -p -m 0640 $RPM_BUILD_ROOT%{_sysconfdir}/salt/master.template $RPM_BUILD_ROOT%{_sysconfdir}/salt/master
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
@ -129,6 +139,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||
%{python_sitelib}/%{name}/*
|
||||
%{python_sitelib}/%{name}-%{version}-py?.?.egg-info
|
||||
%doc %{_mandir}/man7/salt.7.*
|
||||
%doc README.fedora
|
||||
|
||||
%files -n salt-minion
|
||||
%defattr(-,root,root)
|
||||
|
@ -143,7 +154,8 @@ rm -rf $RPM_BUILD_ROOT
|
|||
%{_unitdir}/salt-minion.service
|
||||
%endif
|
||||
|
||||
%config(noreplace) /etc/salt/minion
|
||||
%config(noreplace) %{_sysconfdir}/salt/minion
|
||||
%config %{_sysconfdir}/salt/minion.template
|
||||
|
||||
%files -n salt-master
|
||||
%defattr(-,root,root)
|
||||
|
@ -166,7 +178,8 @@ rm -rf $RPM_BUILD_ROOT
|
|||
%{_unitdir}/salt-master.service
|
||||
%{_unitdir}/salt-syndic.service
|
||||
%endif
|
||||
%config(noreplace) /etc/salt/master
|
||||
%config(noreplace) %{_sysconfdir}/salt/master
|
||||
%config %{_sysconfdir}/salt/master.template
|
||||
|
||||
%if ! (0%{?rhel} >= 7 || 0%{?fedora} >= 15)
|
||||
|
||||
|
@ -242,6 +255,12 @@ fi
|
|||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Jan 24 2012 Clint Savage <herlo1@gmail.com> - 0.9.6-2
|
||||
- Added README.fedora and removed deps for optional modules
|
||||
|
||||
* Sat Jan 21 2012 Clint Savage <herlo1@gmail.com> - 0.9.6-1
|
||||
- New upstream release
|
||||
|
||||
* Sun Jan 8 2012 Clint Savage <herlo1@gmail.com> - 0.9.4-6
|
||||
- Missed some critical elements for SysV and rpmlint cleanup
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# pip requirements file for Salt
|
||||
Jinja2
|
||||
pyzmq
|
||||
msgpack-python
|
||||
M2Crypto
|
||||
pycrypto
|
||||
msgpack-python
|
||||
PyCrypto
|
||||
PyYAML
|
||||
pyzmq >= 2.1.9
|
||||
|
|
|
@ -19,6 +19,22 @@ except ImportError as e:
|
|||
if e.message != 'No module named _msgpack':
|
||||
raise
|
||||
|
||||
|
||||
def set_pidfile(pidfile):
|
||||
'''
|
||||
Save the pidfile
|
||||
'''
|
||||
pdir = os.path.dirname(pidfile)
|
||||
if not os.path.isdir(pdir):
|
||||
os.makedirs(pdir)
|
||||
try:
|
||||
open(pidfile, 'w+').write(str(os.getpid()))
|
||||
except IOError:
|
||||
err = ('Failed to commit the pid file to location {0}, please verify'
|
||||
' that the location is available').format(pidfile)
|
||||
log.error(err)
|
||||
|
||||
|
||||
def verify_env(dirs):
|
||||
'''
|
||||
Verify that the named directories are in place and that the environment
|
||||
|
@ -81,6 +97,8 @@ class Master(object):
|
|||
# command line overrides config
|
||||
if self.cli['user']:
|
||||
self.opts['user'] = self.cli['user']
|
||||
# Send the pidfile location to the opts
|
||||
self.opts['pidfile'] = self.cli['pidfile']
|
||||
|
||||
def __parse_cli(self):
|
||||
'''
|
||||
|
@ -93,7 +111,7 @@ class Master(object):
|
|||
dest='daemon',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run the master in a daemon')
|
||||
help='Run the master as a daemon')
|
||||
parser.add_option('-c',
|
||||
'--config',
|
||||
dest='config',
|
||||
|
@ -102,7 +120,12 @@ class Master(object):
|
|||
parser.add_option('-u',
|
||||
'--user',
|
||||
dest='user',
|
||||
help='Specify user to run minion')
|
||||
help='Specify user to run master')
|
||||
parser.add_option('--pid-file',
|
||||
dest='pidfile',
|
||||
default='/var/run/salt-master.pid',
|
||||
help=('Specify the location of the pidfile. Default'
|
||||
' %default'))
|
||||
parser.add_option('-l',
|
||||
'--log-level',
|
||||
dest='log_level',
|
||||
|
@ -118,7 +141,8 @@ class Master(object):
|
|||
|
||||
cli = {'daemon': options.daemon,
|
||||
'config': options.config,
|
||||
'user': options.user}
|
||||
'user': options.user,
|
||||
'pidfile': options.pidfile}
|
||||
|
||||
return cli
|
||||
|
||||
|
@ -128,6 +152,7 @@ class Master(object):
|
|||
'''
|
||||
verify_env([os.path.join(self.opts['pki_dir'], 'minions'),
|
||||
os.path.join(self.opts['pki_dir'], 'minions_pre'),
|
||||
os.path.join(self.opts['pki_dir'], 'minions_rejected'),
|
||||
os.path.join(self.opts['cachedir'], 'jobs'),
|
||||
os.path.dirname(self.opts['log_file']),
|
||||
self.opts['sock_dir'],
|
||||
|
@ -148,6 +173,7 @@ class Master(object):
|
|||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
salt.utils.daemonize()
|
||||
set_pidfile(self.cli['pidfile'])
|
||||
master.start()
|
||||
|
||||
|
||||
|
@ -183,6 +209,11 @@ class Minion(object):
|
|||
'--user',
|
||||
dest='user',
|
||||
help='Specify user to run minion')
|
||||
parser.add_option('--pid-file',
|
||||
dest='pidfile',
|
||||
default='/var/run/salt-minion.pid',
|
||||
help=('Specify the location of the pidfile. Default'
|
||||
' %default'))
|
||||
parser.add_option('-l',
|
||||
'--log-level',
|
||||
dest='log_level',
|
||||
|
@ -197,7 +228,8 @@ class Minion(object):
|
|||
salt.log.setup_console_logger(options.log_level, log_format=log_format)
|
||||
cli = {'daemon': options.daemon,
|
||||
'config': options.config,
|
||||
'user': options.user}
|
||||
'user': options.user,
|
||||
'pidfile': options.pidfile}
|
||||
|
||||
return cli
|
||||
|
||||
|
@ -226,6 +258,7 @@ class Minion(object):
|
|||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
salt.utils.daemonize()
|
||||
set_pidfile(self.cli['pidfile'])
|
||||
minion = salt.minion.Minion(self.opts)
|
||||
minion.tune_in()
|
||||
except KeyboardInterrupt:
|
||||
|
@ -269,7 +302,7 @@ class Syndic(object):
|
|||
|
||||
def __parse_cli(self):
|
||||
'''
|
||||
Parse the cli for options passed to a master daemon
|
||||
Parse the cli for options passed to a syndic daemon
|
||||
'''
|
||||
import salt.log
|
||||
parser = optparse.OptionParser(version="%%prog %s" % __version__)
|
||||
|
@ -278,7 +311,7 @@ class Syndic(object):
|
|||
dest='daemon',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run the master in a daemon')
|
||||
help='Run the syndic as a daemon')
|
||||
parser.add_option('--master-config',
|
||||
dest='master_config',
|
||||
default='/etc/salt/master',
|
||||
|
@ -290,7 +323,12 @@ class Syndic(object):
|
|||
parser.add_option('-u',
|
||||
'--user',
|
||||
dest='user',
|
||||
help='Specify user to run minion')
|
||||
help='Specify user to run syndic')
|
||||
parser.add_option('--pid-file',
|
||||
dest='pidfile',
|
||||
default='/var/run/salt-syndic.pid',
|
||||
help=('Specify the location of the pidfile. Default'
|
||||
' %default'))
|
||||
parser.add_option('-l',
|
||||
'--log-level',
|
||||
dest='log_level',
|
||||
|
@ -337,6 +375,7 @@ class Syndic(object):
|
|||
# Late import so logging works correctly
|
||||
import salt.utils
|
||||
salt.utils.daemonize()
|
||||
set_pidfile(self.cli['pidfile'])
|
||||
syndic.tune_in()
|
||||
except KeyboardInterrupt:
|
||||
log.warn('Stopping the Salt Syndic Minion')
|
||||
|
|
|
@ -6,8 +6,6 @@ The management of salt command line utilities are stored in here
|
|||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import json
|
||||
|
||||
# Import salt components
|
||||
import salt.cli.caller
|
||||
|
@ -41,8 +39,7 @@ class SaltCMD(object):
|
|||
|
||||
parser.add_option('-t',
|
||||
'--timeout',
|
||||
default=5,
|
||||
type=int,
|
||||
default=None,
|
||||
dest='timeout',
|
||||
help=('Set the return timeout for batch jobs; '
|
||||
'default=5 seconds'))
|
||||
|
@ -146,7 +143,8 @@ class SaltCMD(object):
|
|||
|
||||
opts = {}
|
||||
|
||||
opts['timeout'] = options.timeout
|
||||
if not options.timeout is None:
|
||||
opts['timeout'] = int(options.timeout)
|
||||
opts['pcre'] = options.pcre
|
||||
opts['list'] = options.list_
|
||||
opts['grain'] = options.grain
|
||||
|
@ -160,10 +158,6 @@ class SaltCMD(object):
|
|||
opts['yaml_out'] = options.yaml_out
|
||||
opts['json_out'] = options.json_out
|
||||
|
||||
if opts['return']:
|
||||
if opts['timeout'] == 5:
|
||||
opts['timeout'] = 0
|
||||
|
||||
if options.query:
|
||||
opts['query'] = options.query
|
||||
if len(args) < 1:
|
||||
|
@ -231,6 +225,8 @@ class SaltCMD(object):
|
|||
print ''
|
||||
|
||||
else:
|
||||
if not 'timeout' in self.opts:
|
||||
self.opts['timeout'] = local.opts['timeout']
|
||||
args = [self.opts['tgt'],
|
||||
self.opts['fun'],
|
||||
self.opts['arg'],
|
||||
|
@ -390,6 +386,10 @@ class SaltCP(object):
|
|||
opts['nodegroup'] = options.nodegroup
|
||||
opts['conf_file'] = options.conf_file
|
||||
|
||||
if len(args) <= 1:
|
||||
parser.print_help()
|
||||
parser.exit()
|
||||
|
||||
if opts['list']:
|
||||
opts['tgt'] = args[0].split(',')
|
||||
else:
|
||||
|
@ -447,6 +447,19 @@ class SaltKey(object):
|
|||
action='store_true',
|
||||
help='Accept all pending keys')
|
||||
|
||||
parser.add_option('-r',
|
||||
'--reject',
|
||||
dest='reject',
|
||||
default='',
|
||||
help='Reject the specified public key')
|
||||
|
||||
parser.add_option('-R',
|
||||
'--reject-all',
|
||||
dest='reject_all',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Reject all pending keys')
|
||||
|
||||
parser.add_option('-p',
|
||||
'--print',
|
||||
dest='print_',
|
||||
|
@ -466,6 +479,19 @@ class SaltKey(object):
|
|||
default='',
|
||||
help='Delete the named key')
|
||||
|
||||
parser.add_option('-q',
|
||||
'--quiet',
|
||||
dest='quiet',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Supress output')
|
||||
|
||||
parser.add_option('--logfile',
|
||||
dest='logfile',
|
||||
default='/var/log/salt/key.log',
|
||||
help=('Send all output to a file. '
|
||||
'Default is /var/log/salt/key.log'))
|
||||
|
||||
parser.add_option('--gen-keys',
|
||||
dest='gen_keys',
|
||||
default='',
|
||||
|
@ -496,10 +522,17 @@ class SaltKey(object):
|
|||
|
||||
opts = {}
|
||||
|
||||
opts['quiet'] = options.quiet
|
||||
opts['logfile'] = options.logfile
|
||||
# I decided to always set this to info, since it really all is info or
|
||||
# error.
|
||||
opts['loglevel'] = 'info'
|
||||
opts['list'] = options.list_
|
||||
opts['list_all'] = options.list_all
|
||||
opts['accept'] = options.accept
|
||||
opts['accept_all'] = options.accept_all
|
||||
opts['reject'] = options.reject
|
||||
opts['reject_all'] = options.reject_all
|
||||
opts['print'] = options.print_
|
||||
opts['print_all'] = options.print_all
|
||||
opts['delete'] = options.delete
|
||||
|
@ -518,6 +551,9 @@ class SaltKey(object):
|
|||
'''
|
||||
Execute saltkey
|
||||
'''
|
||||
import salt.log
|
||||
salt.log.setup_logfile_logger(self.opts['logfile'],
|
||||
self.opts['loglevel'])
|
||||
key = salt.cli.key.Key(self.opts)
|
||||
key.run()
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@ import sys
|
|||
|
||||
# Import salt libs
|
||||
import salt
|
||||
import salt.utils
|
||||
import salt.loader
|
||||
import salt.minion
|
||||
|
||||
# Custom exceptions
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.exceptions import CommandExecutionError, CommandNotFoundError
|
||||
|
||||
class Caller(object):
|
||||
'''
|
||||
|
@ -31,18 +32,25 @@ class Caller(object):
|
|||
Call the module
|
||||
'''
|
||||
ret = {}
|
||||
if self.opts['fun'] not in self.minion.functions:
|
||||
sys.stderr.write('Function {0} is not available\n'.format(self.opts['fun']))
|
||||
fun = self.opts['fun']
|
||||
|
||||
if fun not in self.minion.functions:
|
||||
sys.stderr.write('Function {0} is not available\n'.format(fun))
|
||||
sys.exit(1)
|
||||
try:
|
||||
ret['return'] = self.minion.functions[self.opts['fun']](
|
||||
ret['return'] = self.minion.functions[fun](
|
||||
*self.opts['arg']
|
||||
)
|
||||
except (TypeError, CommandExecutionError) as exc:
|
||||
sys.stderr.write('Error running \'{0}\': {1}\n'.format(self.opts['fun'], str(exc)))
|
||||
msg = 'Error running \'{0}\': {1}\n'
|
||||
sys.stderr.write(msg.format(fun, str(exc)))
|
||||
sys.exit(1)
|
||||
if hasattr(self.minion.functions[self.opts['fun']], '__outputter__'):
|
||||
oput = self.minion.functions[self.opts['fun']].__outputter__
|
||||
except CommandNotFoundError as exc:
|
||||
msg = 'Command not found in \'{0}\': {1}\n'
|
||||
sys.stderr.write(msg.format(fun, str(exc)))
|
||||
sys.exit(1)
|
||||
if hasattr(self.minion.functions[fun], '__outputter__'):
|
||||
oput = self.minion.functions[fun].__outputter__
|
||||
if isinstance(oput, str):
|
||||
ret['out'] = oput
|
||||
return ret
|
||||
|
|
173
salt/cli/key.py
173
salt/cli/key.py
|
@ -6,11 +6,12 @@ The actual saltkey functional code
|
|||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import logging
|
||||
# Import salt modules
|
||||
import salt.crypt
|
||||
import salt.utils as utils
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class Key(object):
|
||||
'''
|
||||
|
@ -28,13 +29,15 @@ class Key(object):
|
|||
subdir = ''
|
||||
if key_type == 'pre':
|
||||
subdir = 'minions_pre'
|
||||
elif key_type == 'rej':
|
||||
subdir = 'minions_rejected'
|
||||
elif key_type == 'acc':
|
||||
subdir = 'minions'
|
||||
dir_ = os.path.join(self.opts['pki_dir'], subdir)
|
||||
if not os.path.isdir(dir_):
|
||||
err = ('The ' + subdir + ' directory is not present, ensure that '
|
||||
'the master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
self._log(err, level='error')
|
||||
sys.exit(42)
|
||||
keys = os.listdir(dir_)
|
||||
if full_path:
|
||||
|
@ -43,22 +46,38 @@ class Key(object):
|
|||
else:
|
||||
ret = set(keys)
|
||||
return ret
|
||||
|
||||
def _log(self, message, level=''):
|
||||
if hasattr(log, level):
|
||||
log_msg = getattr(log, level)
|
||||
log_msg(message)
|
||||
if not self.opts['quiet']:
|
||||
print message
|
||||
|
||||
def _list_pre(self):
|
||||
'''
|
||||
List the unaccepted keys
|
||||
'''
|
||||
print utils.LIGHT_RED + 'Unaccepted Keys:' + utils.ENDC
|
||||
self._log(utils.LIGHT_RED + 'Unaccepted Keys:' + utils.ENDC)
|
||||
for key in sorted(self._keys('pre')):
|
||||
print utils.RED + key + utils.ENDC
|
||||
output = utils.RED + key + utils.ENDC
|
||||
self._log(output)
|
||||
|
||||
def _list_accepted(self):
|
||||
'''
|
||||
List the accepted public keys
|
||||
'''
|
||||
print utils.LIGHT_GREEN + 'Accepted Keys:' + utils.ENDC
|
||||
self._log(utils.LIGHT_GREEN + 'Accepted Keys:' + utils.ENDC)
|
||||
for key in sorted(self._keys('acc')):
|
||||
print utils.GREEN + key + utils.ENDC
|
||||
self._log(utils.GREEN + key + utils.ENDC)
|
||||
|
||||
def _list_rejected(self):
|
||||
'''
|
||||
List the unaccepted keys
|
||||
'''
|
||||
self._log(utils.LIGHT_BLUE + 'Rejected:' + utils.ENDC)
|
||||
for key in sorted(self._keys('rej')):
|
||||
self._log(utils.BLUE + key + utils.ENDC)
|
||||
|
||||
def _list_all(self):
|
||||
'''
|
||||
|
@ -66,6 +85,7 @@ class Key(object):
|
|||
'''
|
||||
self._list_pre()
|
||||
self._list_accepted()
|
||||
self._list_rejected()
|
||||
|
||||
def _print_key(self, name):
|
||||
'''
|
||||
|
@ -74,88 +94,117 @@ class Key(object):
|
|||
keys = self._keys('pre', True).union(self._keys('acc', True))
|
||||
for key in sorted(keys):
|
||||
if key.endswith(name):
|
||||
print open(key, 'r').read()
|
||||
self._log(open(key, 'r').read())
|
||||
|
||||
def _print_all(self):
|
||||
'''
|
||||
Print out the public keys, all of em'
|
||||
'''
|
||||
print utils.LIGHT_RED + 'Unaccepted keys:' + utils.ENDC
|
||||
self._log(utils.LIGHT_RED + 'Unaccepted keys:' + utils.ENDC)
|
||||
for key in sorted(self._keys('pre', True)):
|
||||
print ' ' + utils.RED + os.path.basename(key) + utils.ENDC
|
||||
print open(key, 'r').read()
|
||||
print utils.LIGHT_GREEN + 'Accepted keys:' + utils.ENDC
|
||||
self._log(' ' + utils.RED + os.path.basename(key) + utils.ENDC)
|
||||
self._log(open(key, 'r').read())
|
||||
self._log(utils.LIGHT_GREEN + 'Accepted keys:' + utils.ENDC)
|
||||
for key in sorted(self._keys('acc', True)):
|
||||
print ' ' + utils.GREEN + os.path.basename(key) + utils.ENDC
|
||||
print open(key, 'r').read()
|
||||
self._log(' ' + utils.GREEN + os.path.basename(key) +
|
||||
utils.ENDC)
|
||||
self._log(open(key, 'r').read())
|
||||
self._log(utils.LIGHT_BLUE + 'Rejected keys:' + utils.ENDC)
|
||||
for key in sorted(self._keys('pre', True)):
|
||||
self._log(' ' + utils.BLUE + os.path.basename(key) +
|
||||
utils.ENDC)
|
||||
self._log(open(key, 'r').read())
|
||||
|
||||
def _accept(self, key):
|
||||
'''
|
||||
Accept a specified host's public key
|
||||
'''
|
||||
pre_dir = os.path.join(self.opts['pki_dir'], 'minions_pre')
|
||||
minions = os.path.join(self.opts['pki_dir'], 'minions')
|
||||
if not os.path.isdir(minions):
|
||||
err = ('The minions directory is not present, ensure that the '
|
||||
'master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
if not os.path.isdir(pre_dir):
|
||||
err = ('The minions_pre directory is not present, ensure '
|
||||
'that the master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
pre = os.listdir(pre_dir)
|
||||
(minions_accepted,
|
||||
minions_pre,
|
||||
minions_rejected) = self._check_minions_directories()
|
||||
pre = os.listdir(minions_pre)
|
||||
if not pre.count(key):
|
||||
err = ('The named host is unavailable, please accept an '
|
||||
'available key')
|
||||
sys.stderr.write(err + '\n')
|
||||
err = ('The key named %s does not exist, please accept an '
|
||||
'available key' %(key))
|
||||
#log.error(err)
|
||||
self._log(err, level='error')
|
||||
sys.exit(43)
|
||||
shutil.move(os.path.join(pre_dir, key), os.path.join(minions, key))
|
||||
shutil.move(os.path.join(minions_pre, key),
|
||||
os.path.join(minions_accepted, key))
|
||||
self._log('Key for %s accepted.' %(key), level='info')
|
||||
|
||||
def _accept_all(self):
|
||||
'''
|
||||
Accept all keys in pre
|
||||
'''
|
||||
pre_dir = os.path.join(self.opts['pki_dir'], 'minions_pre')
|
||||
minions = os.path.join(self.opts['pki_dir'], 'minions')
|
||||
if not os.path.isdir(minions):
|
||||
err = ('The minions directory is not present, ensure that the '
|
||||
'master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
if not os.path.isdir(pre_dir):
|
||||
err = ('The minions_pre directory is not present, ensure that the '
|
||||
'master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
for key in os.listdir(pre_dir):
|
||||
(minions_accepted,
|
||||
minions_pre,
|
||||
minions_rejected) = self._check_minions_directories()
|
||||
for key in os.listdir(minions_pre):
|
||||
self._accept(key)
|
||||
|
||||
def _delete_key(self):
|
||||
'''
|
||||
Delete a key
|
||||
'''
|
||||
pre_dir = os.path.join(self.opts['pki_dir'], 'minions_pre')
|
||||
minions = os.path.join(self.opts['pki_dir'], 'minions')
|
||||
if not os.path.isdir(minions):
|
||||
err = ('The minions directory is not present, ensure that the '
|
||||
'master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
if not os.path.isdir(pre_dir):
|
||||
err = ('The minions_pre directory is not present, ensure that the '
|
||||
'master server has been started')
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
pre = os.path.join(pre_dir, self.opts['delete'])
|
||||
acc = os.path.join(minions, self.opts['delete'])
|
||||
(minions_accepted,
|
||||
minions_pre,
|
||||
minions_rejected) = self._check_minions_directories()
|
||||
pre = os.path.join(minions_pre, self.opts['delete'])
|
||||
acc = os.path.join(minions_accepted, self.opts['delete'])
|
||||
rej= os.path.join(minions_rejected, self.opts['delete'])
|
||||
if os.path.exists(pre):
|
||||
os.remove(pre)
|
||||
print 'Removed pending key %s' % self.opts['delete']
|
||||
self._log('Removed pending key %s' % self.opts['delete'],
|
||||
level='info')
|
||||
if os.path.exists(acc):
|
||||
os.remove(acc)
|
||||
print 'Removed accepted key %s' % self.opts['delete']
|
||||
self._log('Removed accepted key %s' % self.opts['delete'],
|
||||
level='info')
|
||||
if os.path.exists(rej):
|
||||
os.remove(rej)
|
||||
self._log('Removed rejected key %s' % self.opts['delete'],
|
||||
level='info')
|
||||
|
||||
def _reject(self, key):
|
||||
'''
|
||||
Reject a specified host's public key
|
||||
'''
|
||||
(minions_accepted,
|
||||
minions_pre,
|
||||
minions_rejected) = self._check_minions_directories()
|
||||
pre = os.listdir(minions_pre)
|
||||
if not pre.count(key):
|
||||
err = ('The host named %s is unavailable, please accept an '
|
||||
'available key' %(key))
|
||||
self._log(err, level='error')
|
||||
sys.exit(43)
|
||||
shutil.move(os.path.join(minions_pre, key),
|
||||
os.path.join(minions_rejected, key))
|
||||
self._log('%s key rejected.' %(key), level='info')
|
||||
|
||||
def _reject_all(self):
|
||||
'''
|
||||
Reject all keys in pre
|
||||
'''
|
||||
(minions_accepted,
|
||||
minions_pre,
|
||||
minions_rejected) = self._check_minions_directories()
|
||||
for key in os.listdir(minions_pre):
|
||||
self._reject(key)
|
||||
|
||||
def _check_minions_directories(self):
|
||||
minions_accepted = os.path.join(self.opts['pki_dir'], 'minions')
|
||||
minions_pre = os.path.join(self.opts['pki_dir'], 'minions_pre')
|
||||
minions_rejected = os.path.join(self.opts['pki_dir'],
|
||||
'minions_rejected')
|
||||
for dir in [minions_accepted, minions_pre, minions_rejected]:
|
||||
if not os.path.isdir(dir):
|
||||
err = ('The minions directory {0} is not present, ensure '
|
||||
'that the master server has been started'.format(dir))
|
||||
self._log(err, level='error')
|
||||
sys.exit(42)
|
||||
return minions_accepted, minions_pre, minions_rejected
|
||||
|
||||
def run(self):
|
||||
'''
|
||||
|
@ -179,7 +228,11 @@ class Key(object):
|
|||
self._accept(self.opts['accept'])
|
||||
elif self.opts['accept_all']:
|
||||
self._accept_all()
|
||||
elif self.opts['reject']:
|
||||
self._reject(self.opts['reject'])
|
||||
elif self.opts['reject_all']:
|
||||
self._reject_all()
|
||||
elif self.opts['delete']:
|
||||
self._delete_key()
|
||||
else:
|
||||
self._list_all()
|
||||
self._list_all()
|
|
@ -77,7 +77,8 @@ class LocalClient(object):
|
|||
key = open(keyfile, 'r').read()
|
||||
return key
|
||||
except (OSError, IOError):
|
||||
raise SaltClientError('Problem reading the salt root key. Are you root?')
|
||||
raise SaltClientError(('Problem reading the salt root key. Are'
|
||||
' you root?'))
|
||||
|
||||
def _check_glob_minions(self, expr):
|
||||
'''
|
||||
|
@ -131,12 +132,14 @@ class LocalClient(object):
|
|||
tgt,
|
||||
fun,
|
||||
arg=(),
|
||||
timeout=5,
|
||||
timeout=None,
|
||||
expr_form='glob',
|
||||
ret=''):
|
||||
'''
|
||||
Execute a salt command and return.
|
||||
'''
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid = prep_jid(self.opts['cachedir'])
|
||||
pub_data = self.pub(
|
||||
tgt,
|
||||
|
@ -153,12 +156,14 @@ class LocalClient(object):
|
|||
tgt,
|
||||
fun,
|
||||
arg=(),
|
||||
timeout=5,
|
||||
timeout=None,
|
||||
expr_form='glob',
|
||||
ret=''):
|
||||
'''
|
||||
Execute a salt command and return
|
||||
'''
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid = prep_jid(self.opts['cachedir'])
|
||||
pub_data = self.pub(
|
||||
tgt,
|
||||
|
@ -171,11 +176,13 @@ class LocalClient(object):
|
|||
return (self.get_full_returns(pub_data['jid'],
|
||||
pub_data['minions'], timeout))
|
||||
|
||||
def get_returns(self, jid, minions, timeout=5):
|
||||
def get_returns(self, jid, minions, timeout=None):
|
||||
'''
|
||||
This method starts off a watcher looking at the return data for a
|
||||
specified jid
|
||||
'''
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid_dir = os.path.join(self.opts['cachedir'], 'jobs', jid)
|
||||
start = 999999999999
|
||||
gstart = int(time.time())
|
||||
|
@ -209,11 +216,13 @@ class LocalClient(object):
|
|||
return ret
|
||||
time.sleep(0.02)
|
||||
|
||||
def get_full_returns(self, jid, minions, timeout=5):
|
||||
def get_full_returns(self, jid, minions, timeout=None):
|
||||
'''
|
||||
This method starts off a watcher looking at the return data for a
|
||||
specified jid, it returns all of the information for the jid
|
||||
'''
|
||||
if timeout is None:
|
||||
timeout = self.opts['timeout']
|
||||
jid_dir = os.path.join(self.opts['cachedir'], 'jobs', jid)
|
||||
start = 999999999999
|
||||
gstart = int(time.time())
|
||||
|
|
|
@ -3,10 +3,12 @@ All salt configuration loading and defaults should be in this module
|
|||
'''
|
||||
|
||||
# Import python modules
|
||||
import glob
|
||||
import os
|
||||
import tempfile
|
||||
import socket
|
||||
import sys
|
||||
import socket
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
# import third party libs
|
||||
import yaml
|
||||
|
@ -22,19 +24,35 @@ import salt.crypt
|
|||
import salt.loader
|
||||
import salt.utils
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _validate_file_roots(file_roots):
|
||||
'''
|
||||
If the file_roots option has a key that is None then we will error out,
|
||||
just replace it with an empty list
|
||||
'''
|
||||
if not isinstance(file_roots, dict):
|
||||
log.warning(('The file_roots parameter is not properly formatted,'
|
||||
' using defaults'))
|
||||
return {'base': ['/srv/salt']}
|
||||
for env, dirs in file_roots.items():
|
||||
if not isinstance(dirs, list) and not isinstance(dirs, tuple):
|
||||
file_roots[env] = []
|
||||
return file_roots
|
||||
|
||||
|
||||
def load_config(opts, path, env_var):
|
||||
'''
|
||||
Attempts to update ``opts`` dict by parsing either the file described by
|
||||
``path`` or the environment variable described by ``env_var`` as YAML.
|
||||
'''
|
||||
|
||||
if not path or not os.path.isfile(path):
|
||||
path = os.environ.get(env_var, path)
|
||||
# If the configuration file is missing, attempt to copy the template,
|
||||
# after removing the first header line.
|
||||
if not os.path.isfile(path):
|
||||
template = "%s.template" % path
|
||||
template = '{0}.template'.format(path)
|
||||
if os.path.isfile(template):
|
||||
with open(path, 'w') as out:
|
||||
with open(template, 'r') as f:
|
||||
|
@ -54,9 +72,38 @@ def load_config(opts, path, env_var):
|
|||
opts.update(conf_opts)
|
||||
opts['conf_file'] = path
|
||||
except Exception, e:
|
||||
print 'Error parsing configuration file: {0} - {1}'.format(path, e)
|
||||
msg = 'Error parsing configuration file: {0} - {1}'
|
||||
log.warn(msg.format(path, e))
|
||||
else:
|
||||
print 'Missing configuration file: {0}'.format(path)
|
||||
log.debug('Missing configuration file: {0}'.format(path))
|
||||
|
||||
|
||||
def include_config(opts, orig_path):
|
||||
'''
|
||||
Parses an extra configuration file specified in an include list in the
|
||||
main config file.
|
||||
'''
|
||||
include = opts.get('include', [])
|
||||
if isinstance(include, basestring):
|
||||
include = [include]
|
||||
for path in include:
|
||||
if not os.path.isabs(path):
|
||||
path = os.path.join(os.path.dirname(orig_path), path)
|
||||
for fn_ in glob.glob(path):
|
||||
try:
|
||||
conf_opts = yaml.safe_load(open(fn_, 'r'))
|
||||
if conf_opts is None:
|
||||
# The config file is empty and the yaml.load returned None
|
||||
conf_opts = {}
|
||||
else:
|
||||
# allow using numeric ids: convert int to string
|
||||
if 'id' in conf_opts:
|
||||
conf_opts['id'] = str(conf_opts['id'])
|
||||
opts.update(conf_opts)
|
||||
except Exception, e:
|
||||
msg = 'Error parsing configuration file: {0} - {1}'
|
||||
log.warn(msg.format(fn_, e))
|
||||
return opts
|
||||
|
||||
|
||||
def prepend_root_dir(opts, path_options):
|
||||
|
@ -86,6 +133,7 @@ def minion_config(path):
|
|||
'renderer': 'yaml_jinja',
|
||||
'failhard': False,
|
||||
'autoload_dynamic_modules': True,
|
||||
'environment': None,
|
||||
'disable_modules': [],
|
||||
'disable_returners': [],
|
||||
'module_dirs': [],
|
||||
|
@ -107,6 +155,9 @@ def minion_config(path):
|
|||
|
||||
load_config(opts, path, 'SALT_MINION_CONFIG')
|
||||
|
||||
if 'include' in opts:
|
||||
opts = include_config(opts, path)
|
||||
|
||||
opts['master_ip'] = dns_check(opts['master'])
|
||||
|
||||
opts['master_uri'] = 'tcp://' + opts['master_ip'] + ':'\
|
||||
|
@ -137,6 +188,7 @@ def master_config(path):
|
|||
'worker_threads': 5,
|
||||
'sock_dir': os.path.join(tempfile.gettempdir(), '.salt-unix'),
|
||||
'ret_port': '4506',
|
||||
'timeout': 5,
|
||||
'keep_jobs': 24,
|
||||
'root_dir': '/',
|
||||
'pki_dir': '/etc/salt/pki',
|
||||
|
@ -152,6 +204,7 @@ def master_config(path):
|
|||
'renderer': 'yaml_jinja',
|
||||
'failhard': False,
|
||||
'state_top': 'top.sls',
|
||||
'external_nodes': '',
|
||||
'order_masters': False,
|
||||
'log_file': '/var/log/salt/master',
|
||||
'log_level': 'warning',
|
||||
|
@ -164,6 +217,9 @@ def master_config(path):
|
|||
|
||||
load_config(opts, path, 'SALT_MASTER_CONFIG')
|
||||
|
||||
if 'include' in opts:
|
||||
opts = include_config(opts, path)
|
||||
|
||||
opts['aes'] = salt.crypt.Crypticle.generate_key_string()
|
||||
|
||||
# Prepend root_dir to other paths
|
||||
|
@ -173,6 +229,7 @@ def master_config(path):
|
|||
# else!
|
||||
opts['open_mode'] = opts['open_mode'] is True
|
||||
opts['auto_accept'] = opts['auto_accept'] is True
|
||||
opts['file_roots'] = _validate_file_roots(opts['file_roots'])
|
||||
return opts
|
||||
|
||||
|
||||
|
|
|
@ -5,16 +5,16 @@ authenticating peers
|
|||
'''
|
||||
|
||||
# Import python libs
|
||||
import hashlib
|
||||
import hmac
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import hmac
|
||||
import hashlib
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
# Import Cryptography libs
|
||||
from Crypto.Cipher import AES
|
||||
from M2Crypto import RSA
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
# Import zeromq libs
|
||||
import zmq
|
||||
|
@ -320,8 +320,8 @@ class SAuth(Auth):
|
|||
'''
|
||||
creds = self.sign_in()
|
||||
if creds == 'retry':
|
||||
print 'Failed to authenticate with the master, verify that this'\
|
||||
+ ' minion\'s public key has been accepted on the salt master'
|
||||
log.error('Failed to authenticate with the master, verify that this'\
|
||||
+ ' minion\'s public key has been accepted on the salt master')
|
||||
sys.exit(2)
|
||||
return Crypticle(self.opts, creds['aes'])
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ def _virtual(osdata):
|
|||
if grains.get('productname', '') == 'HVM domU':
|
||||
# Requires dmidecode!
|
||||
grains['virtual_subtype'] = 'Xen HVM DomU'
|
||||
elif os.path.isfile('/proc/xen/capabilities'):
|
||||
elif os.path.isfile('/proc/xen/capabilities') and os.access('/proc/xen/capabilities', os.R_OK):
|
||||
caps = open('/proc/xen/capabilities')
|
||||
if 'control_d' not in caps.read():
|
||||
# Tested on CentOS 5.5 / 2.6.18-194.3.1.el5xen
|
||||
|
@ -241,6 +241,9 @@ def _virtual(osdata):
|
|||
sysctl = salt.utils.which('sysctl')
|
||||
if sysctl:
|
||||
model = __salt__['cmd.run']('{0} hw.model'.format(sysctl)).strip()
|
||||
jail = __salt__['cmd.run']('{0} security.jail.jailed'.format(sysctl)).strip()
|
||||
if jail:
|
||||
grains['virtual_subtype'] = 'jail'
|
||||
if 'QEMU Virtual CPU' in model:
|
||||
grains['virtual'] = 'kvm'
|
||||
return grains
|
||||
|
@ -492,6 +495,14 @@ def saltpath():
|
|||
path = os.path.abspath(os.path.join(__file__, os.path.pardir))
|
||||
return {'saltpath': os.path.dirname(path)}
|
||||
|
||||
def saltversion():
|
||||
'''
|
||||
Return the version of salt
|
||||
'''
|
||||
# Provides:
|
||||
# saltversion
|
||||
from salt import __version__
|
||||
return {'saltversion': __version__}
|
||||
|
||||
# Relatively complex mini-algorithm to iterate over the various
|
||||
# sections of dmidecode output and return matches for specific
|
||||
|
|
|
@ -30,7 +30,7 @@ def minion_mods(opts):
|
|||
module_dirs = [
|
||||
os.path.join(salt_base_path, 'modules'),
|
||||
] + extra_dirs
|
||||
load = Loader(module_dirs, opts)
|
||||
load = Loader(module_dirs, opts, 'module')
|
||||
return load.apply_introspection(load.gen_functions())
|
||||
|
||||
|
||||
|
@ -47,7 +47,7 @@ def returners(opts):
|
|||
module_dirs = [
|
||||
os.path.join(salt_base_path, 'returners'),
|
||||
] + extra_dirs
|
||||
load = Loader(module_dirs, opts)
|
||||
load = Loader(module_dirs, opts, 'returner')
|
||||
return load.filter_func('returner')
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ def states(opts, functions):
|
|||
module_dirs = [
|
||||
os.path.join(salt_base_path, 'states'),
|
||||
] + extra_dirs
|
||||
load = Loader(module_dirs, opts)
|
||||
load = Loader(module_dirs, opts, 'state')
|
||||
pack = {'name': '__salt__',
|
||||
'value': functions}
|
||||
return load.gen_functions(pack)
|
||||
|
@ -83,7 +83,7 @@ def render(opts, functions):
|
|||
module_dirs = [
|
||||
os.path.join(salt_base_path, 'renderers'),
|
||||
] + extra_dirs
|
||||
load = Loader(module_dirs, opts)
|
||||
load = Loader(module_dirs, opts, 'render')
|
||||
pack = {'name': '__salt__',
|
||||
'value': functions}
|
||||
rend = load.filter_func('render', pack)
|
||||
|
@ -107,7 +107,7 @@ def grains(opts):
|
|||
module_dirs = [
|
||||
os.path.join(salt_base_path, 'grains'),
|
||||
] + extra_dirs
|
||||
load = Loader(module_dirs, opts)
|
||||
load = Loader(module_dirs, opts, 'grain')
|
||||
grains = load.gen_grains()
|
||||
if 'grains' in opts:
|
||||
grains.update(opts['grains'])
|
||||
|
@ -144,8 +144,11 @@ class Loader(object):
|
|||
used to only load specific functions from a directory, or to call modules
|
||||
in an arbitrary directory directly.
|
||||
'''
|
||||
def __init__(self, module_dirs, opts=dict()):
|
||||
def __init__(self, module_dirs, opts=dict(), tag='module'):
|
||||
self.module_dirs = module_dirs
|
||||
if '_' in tag:
|
||||
raise LoaderError('Cannot tag loader with an "_"')
|
||||
self.tag = tag
|
||||
if 'grains' in opts:
|
||||
self.grains = opts['grains']
|
||||
else:
|
||||
|
@ -203,7 +206,7 @@ class Loader(object):
|
|||
"modules.")
|
||||
return getattr(mod, fun[fun.rindex('.') + 1:])(*arg)
|
||||
|
||||
def gen_functions(self, pack=None):
|
||||
def gen_functions(self, pack=None, virtual_enable=True):
|
||||
'''
|
||||
Return a dict of functions found in the defined module_dirs
|
||||
'''
|
||||
|
@ -242,11 +245,20 @@ class Loader(object):
|
|||
mod = pyximport.load_module(name, names[name], '/tmp')
|
||||
else:
|
||||
fn_, path, desc = imp.find_module(name, self.module_dirs)
|
||||
mod = imp.load_module(name, fn_, path, desc)
|
||||
mod = imp.load_module(
|
||||
'{0}_{1}'.format(name, self.tag),
|
||||
fn_,
|
||||
path,
|
||||
desc
|
||||
)
|
||||
except ImportError as exc:
|
||||
log.debug(('Failed to import module {0}, this is most likely'
|
||||
' NOT a problem: {1}').format(name, exc))
|
||||
continue
|
||||
except Exception as exc:
|
||||
log.warning(('Failed to import module {0}, this is due most'
|
||||
' likely to a syntax error: {1}').format(name, exc))
|
||||
continue
|
||||
modules.append(mod)
|
||||
for mod in modules:
|
||||
virtual = ''
|
||||
|
@ -272,9 +284,10 @@ class Loader(object):
|
|||
except TypeError:
|
||||
pass
|
||||
|
||||
if hasattr(mod, '__virtual__'):
|
||||
if callable(mod.__virtual__):
|
||||
virtual = mod.__virtual__()
|
||||
if virtual_enable:
|
||||
if hasattr(mod, '__virtual__'):
|
||||
if callable(mod.__virtual__):
|
||||
virtual = mod.__virtual__()
|
||||
|
||||
for attr in dir(mod):
|
||||
if attr.startswith('_'):
|
||||
|
@ -288,7 +301,11 @@ class Loader(object):
|
|||
pass
|
||||
else:
|
||||
func = getattr(mod, attr)
|
||||
funcs[mod.__name__ + '.' + attr] = func
|
||||
funcs[
|
||||
'{0}.{1}'.format(
|
||||
mod.__name__[:mod.__name__.rindex('_')],
|
||||
attr)
|
||||
] = func
|
||||
self._apply_outputter(func, mod)
|
||||
for mod in modules:
|
||||
if not hasattr(mod, '__salt__'):
|
||||
|
|
|
@ -13,11 +13,15 @@ import hashlib
|
|||
import tempfile
|
||||
import datetime
|
||||
import multiprocessing
|
||||
import subprocess
|
||||
|
||||
# Import zeromq
|
||||
import zmq
|
||||
from M2Crypto import RSA
|
||||
|
||||
# Import Third Party Libs
|
||||
import yaml
|
||||
|
||||
# Import salt modules
|
||||
import salt.crypt
|
||||
import salt.utils
|
||||
|
@ -42,7 +46,7 @@ def prep_jid(opts, load):
|
|||
os.makedirs(jid_dir)
|
||||
serial.dump(load, open(os.path.join(jid_dir, '.load.p'), 'w+'))
|
||||
else:
|
||||
return prep_jid(cachedir, load)
|
||||
return prep_jid(opts['cachedir'], load)
|
||||
return jid
|
||||
|
||||
|
||||
|
@ -97,6 +101,7 @@ class Master(SMaster):
|
|||
'''
|
||||
Clean out the old jobs
|
||||
'''
|
||||
salt.utils.append_pid(self.opts['pidfile'])
|
||||
while True:
|
||||
cur = "{0:%Y%m%d%H}".format(datetime.datetime.now())
|
||||
|
||||
|
@ -153,6 +158,7 @@ class Publisher(multiprocessing.Process):
|
|||
'''
|
||||
Bind to the interface specified in the configuration file
|
||||
'''
|
||||
salt.utils.append_pid(self.opts['pidfile'])
|
||||
context = zmq.Context(1)
|
||||
pub_sock = context.socket(zmq.PUB)
|
||||
pull_sock = context.socket(zmq.PULL)
|
||||
|
@ -160,14 +166,13 @@ class Publisher(multiprocessing.Process):
|
|||
pull_uri = 'ipc://{0}'.format(
|
||||
os.path.join(self.opts['sock_dir'], 'publish_pull.ipc')
|
||||
)
|
||||
log.info('Starting the Salt Publisher on %s', pub_uri)
|
||||
log.info('Starting the Salt Publisher on {0}'.format(pub_uri))
|
||||
pub_sock.bind(pub_uri)
|
||||
pull_sock.bind(pull_uri)
|
||||
|
||||
try:
|
||||
while True:
|
||||
package = pull_sock.recv()
|
||||
log.info('Publishing command')
|
||||
pub_sock.send(package)
|
||||
except KeyboardInterrupt:
|
||||
pub_sock.close()
|
||||
|
@ -228,6 +233,7 @@ class ReqServer(object):
|
|||
'''
|
||||
Start up the ReqServer
|
||||
'''
|
||||
salt.utils.append_pid(self.opts['pidfile'])
|
||||
self.__bind()
|
||||
|
||||
|
||||
|
@ -316,6 +322,7 @@ class MWorker(multiprocessing.Process):
|
|||
'''
|
||||
Start a Master Worker
|
||||
'''
|
||||
salt.utils.append_pid(self.opts['pidfile'])
|
||||
self.__bind()
|
||||
|
||||
|
||||
|
@ -372,6 +379,44 @@ class AESFuncs(object):
|
|||
.format(id_))
|
||||
return False
|
||||
|
||||
def _ext_nodes(self, load):
|
||||
'''
|
||||
Return the results from an external node classifier if one is
|
||||
specified
|
||||
'''
|
||||
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] = ndata['classes'].keys()
|
||||
elif isinstance(ndata['classes'], list):
|
||||
ret[env] = ndata['classes']
|
||||
else:
|
||||
return ret
|
||||
return ret
|
||||
|
||||
|
||||
def _serve_file(self, load):
|
||||
'''
|
||||
Return a chunk from a file based on the data received
|
||||
|
@ -426,6 +471,19 @@ class AESFuncs(object):
|
|||
)
|
||||
return ret
|
||||
|
||||
def _file_list_emptydirs(self, load):
|
||||
'''
|
||||
Return a list of all empty directories on the master
|
||||
'''
|
||||
ret = []
|
||||
if load['env'] not in self.opts['file_roots']:
|
||||
return ret
|
||||
for path in self.opts['file_roots'][load['env']]:
|
||||
for root, dirs, files in os.walk(path):
|
||||
if len(dirs)==0 and len(files)==0:
|
||||
ret.append(os.path.relpath(root,path))
|
||||
return ret
|
||||
|
||||
def _master_opts(self, load):
|
||||
'''
|
||||
Return the master options to the minion
|
||||
|
@ -536,7 +594,9 @@ class AESFuncs(object):
|
|||
'ret': clear_load['ret'],
|
||||
}
|
||||
expr_form = 'glob'
|
||||
timeout = 0
|
||||
timeout = 5
|
||||
if 'tmo' in clear_load:
|
||||
timeout = int(clear_load['tmo'])
|
||||
if 'tgt_type' in clear_load:
|
||||
load['tgt_type'] = clear_load['tgt_type']
|
||||
expr_form = load['tgt_type']
|
||||
|
@ -551,6 +611,8 @@ class AESFuncs(object):
|
|||
os.path.join(self.opts['sock_dir'], 'publish_pull.ipc')
|
||||
)
|
||||
pub_sock.connect(pull_uri)
|
||||
log.info(('Publishing minion job: #{0[jid]}, func: "{0[fun]}", args:'
|
||||
' "{0[arg]}", target: "{0[tgt]}"').format(load))
|
||||
pub_sock.send(self.serial.dumps(payload))
|
||||
# Run the client get_returns method
|
||||
return self.local.get_returns(
|
||||
|
@ -646,6 +708,9 @@ class ClearFuncs(object):
|
|||
pubfn_pend = os.path.join(self.opts['pki_dir'],
|
||||
'minions_pre',
|
||||
load['id'])
|
||||
pubfn_rejected = os.path.join(self.opts['pki_dir'],
|
||||
'minions_rejected',
|
||||
load['id'])
|
||||
if self.opts['open_mode']:
|
||||
# open mode is turned on, nuts to checks and overwrite whatever
|
||||
# is there
|
||||
|
@ -661,6 +726,12 @@ class ClearFuncs(object):
|
|||
ret = {'enc': 'clear',
|
||||
'load': {'ret': False}}
|
||||
return ret
|
||||
elif os.path.isfile(pubfn_rejected):
|
||||
# The key has been rejected, don't place it in pending
|
||||
log.info('Public key rejected for %(id)s', load)
|
||||
ret = {'enc': 'clear',
|
||||
'load': {'ret': False}}
|
||||
return ret
|
||||
elif not os.path.isfile(pubfn_pend)\
|
||||
and not self.opts['auto_accept']:
|
||||
# This is a new key, stick it in pre
|
||||
|
|
151
salt/minion.py
151
salt/minion.py
|
@ -12,6 +12,7 @@ import hashlib
|
|||
import os
|
||||
import re
|
||||
import shutil
|
||||
import string
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
|
@ -24,12 +25,10 @@ import zmq
|
|||
|
||||
# Import salt libs
|
||||
from salt.exceptions import AuthenticationError, MinionError, \
|
||||
CommandExecutionError, SaltInvocationError
|
||||
CommandExecutionError, CommandNotFoundError, SaltInvocationError
|
||||
import salt.client
|
||||
import salt.crypt
|
||||
import salt.loader
|
||||
import salt.modules
|
||||
import salt.returners
|
||||
import salt.utils
|
||||
import salt.payload
|
||||
|
||||
|
@ -44,6 +43,21 @@ log = logging.getLogger(__name__)
|
|||
# 6. handle publications
|
||||
|
||||
|
||||
def get_proc_dir(cachedir):
|
||||
'''
|
||||
Return the directory that process data is stored in
|
||||
'''
|
||||
fn_ = os.path.join(cachedir, 'proc')
|
||||
if not os.path.isdir(fn_):
|
||||
# proc_dir is not present, create it
|
||||
os.makedirs(fn_)
|
||||
else:
|
||||
# proc_dir is present, clean out old proc files
|
||||
for proc_fn in os.listdir(fn_):
|
||||
os.remove(os.path.join(fn_, proc_fn))
|
||||
return fn_
|
||||
|
||||
|
||||
class SMinion(object):
|
||||
'''
|
||||
Create an object that has loaded all of the minion module functions,
|
||||
|
@ -81,6 +95,7 @@ class Minion(object):
|
|||
self.mod_opts = self.__prep_mod_opts()
|
||||
self.functions, self.returners = self.__load_modules()
|
||||
self.matcher = Matcher(self.opts, self.functions)
|
||||
self.proc_dir = get_proc_dir(opts['cachedir'])
|
||||
if hasattr(self,'_syndic') and self._syndic:
|
||||
log.warn('Starting the Salt Syndic Minion')
|
||||
else:
|
||||
|
@ -102,6 +117,7 @@ class Minion(object):
|
|||
'''
|
||||
Return the functions and the returners loaded up from the loader module
|
||||
'''
|
||||
self.opts['grains'] = salt.loader.grains(self.opts)
|
||||
functions = salt.loader.minion_mods(self.opts)
|
||||
returners = salt.loader.returners(self.opts)
|
||||
return functions, returners
|
||||
|
@ -168,7 +184,7 @@ class Minion(object):
|
|||
self.functions, self.returners = self.__load_modules()
|
||||
|
||||
if self.opts['multiprocessing']:
|
||||
if isinstance(data['fun'], list):
|
||||
if isinstance(data['fun'], tuple) or isinstance(data['fun'], list):
|
||||
multiprocessing.Process(
|
||||
target=lambda: self._thread_multi_return(data)
|
||||
).start()
|
||||
|
@ -177,7 +193,7 @@ class Minion(object):
|
|||
target=lambda: self._thread_return(data)
|
||||
).start()
|
||||
else:
|
||||
if isinstance(data['fun'], list):
|
||||
if isinstance(data['fun'], tuple) or isinstance(data['fun'], list):
|
||||
threading.Thread(
|
||||
target=lambda: self._thread_multi_return(data)
|
||||
).start()
|
||||
|
@ -191,6 +207,11 @@ class Minion(object):
|
|||
This method should be used as a threading target, start the actual
|
||||
minion side execution.
|
||||
'''
|
||||
if self.opts['multiprocessing']:
|
||||
fn_ = os.path.join(self.proc_dir, data['jid'])
|
||||
sdata = {'pid': os.getpid()}
|
||||
sdata.update(data)
|
||||
open(fn_, 'w+').write(self.serial.dumps(sdata))
|
||||
ret = {}
|
||||
for ind in range(0, len(data['arg'])):
|
||||
try:
|
||||
|
@ -208,6 +229,10 @@ class Minion(object):
|
|||
if function_name in self.functions:
|
||||
try:
|
||||
ret['return'] = self.functions[data['fun']](*data['arg'])
|
||||
except CommandNotFoundError as exc:
|
||||
msg = 'Command not found in \'{0}\': {1}'
|
||||
log.debug(msg.format(function_name, str(exc)))
|
||||
ret['return'] = msg.format(function_name, str(exc))
|
||||
except CommandExecutionError as exc:
|
||||
msg = 'A command in {0} had a problem: {1}'
|
||||
log.error(msg.format(function_name, str(exc)))
|
||||
|
@ -289,6 +314,10 @@ class Minion(object):
|
|||
'''
|
||||
Return the data from the executed command to the master server
|
||||
'''
|
||||
if self.opts['multiprocessing']:
|
||||
fn_ = os.path.join(self.proc_dir, ret['jid'])
|
||||
if os.path.isfile(fn_):
|
||||
os.remove(fn_)
|
||||
log.info('Returning information for job: {0}'.format(ret['jid']))
|
||||
context = zmq.Context()
|
||||
socket = context.socket(zmq.REQ)
|
||||
|
@ -356,19 +385,10 @@ class Minion(object):
|
|||
Check to see if the salt refresh file has been laid down, if it has,
|
||||
refresh the functions and returners.
|
||||
'''
|
||||
if os.path.isfile(
|
||||
os.path.join(
|
||||
self.opts['cachedir'],
|
||||
'.module_refresh'
|
||||
)
|
||||
):
|
||||
fn_ = os.path.join(self.opts['cachedir'], 'module_refresh')
|
||||
if os.path.isfile(fn_):
|
||||
os.remove(fn_)
|
||||
self.functions, self.returners = self.__load_modules()
|
||||
os.remove(
|
||||
os.path.join(
|
||||
self.opts['cachedir'],
|
||||
'.module_refresh'
|
||||
)
|
||||
)
|
||||
|
||||
def tune_in(self):
|
||||
'''
|
||||
|
@ -501,7 +521,7 @@ class Matcher(object):
|
|||
else:
|
||||
self.functions = functions
|
||||
|
||||
def confirm_top(self, match, data):
|
||||
def confirm_top(self, match, data, nodegroups=None):
|
||||
'''
|
||||
Takes the data passed to a top file environment and determines if the
|
||||
data matches this minion
|
||||
|
@ -512,9 +532,13 @@ class Matcher(object):
|
|||
if 'match' in item:
|
||||
matcher = item['match']
|
||||
if hasattr(self, matcher + '_match'):
|
||||
if matcher == 'nodegroup':
|
||||
return getattr(self, '{0}_match'.format(matcher))(match, nodegroups)
|
||||
return getattr(self, '{0}_match'.format(matcher))(match)
|
||||
else:
|
||||
log.error('Attempting to match with unknown matcher: %s', matcher)
|
||||
log.error('Attempting to match with unknown matcher: {0}'.format(
|
||||
matcher
|
||||
))
|
||||
return False
|
||||
|
||||
def glob_match(self, tgt):
|
||||
|
@ -540,7 +564,7 @@ class Matcher(object):
|
|||
'''
|
||||
Determines if this host is on the list
|
||||
'''
|
||||
return bool(tgt in self.opts['id'])
|
||||
return bool(self.opts['id'] in tgt)
|
||||
|
||||
def grain_match(self, tgt):
|
||||
'''
|
||||
|
@ -553,7 +577,7 @@ class Matcher(object):
|
|||
if comps[0] not in self.opts['grains']:
|
||||
log.error('Got unknown grain from master: {0}'.format(comps[0]))
|
||||
return False
|
||||
return bool(re.match(comps[1], self.opts['grains'][comps[0]]))
|
||||
return bool(re.match(comps[1], str(self.opts['grains'][comps[0]])))
|
||||
|
||||
def exsel_match(self, tgt):
|
||||
'''
|
||||
|
@ -583,10 +607,13 @@ class Matcher(object):
|
|||
elif match == 'or':
|
||||
results.append('or')
|
||||
continue
|
||||
elif match == 'not':
|
||||
results.append('not')
|
||||
continue
|
||||
# If we are here then it is not a boolean operator, check if the
|
||||
# last member of the result list is a boolean, if no, append and
|
||||
if results:
|
||||
if results[-1] != 'and' or results[-1] != 'or':
|
||||
if results[-1] != 'and' or results[-1] != 'or' or results[-1] != 'not':
|
||||
results.append('and')
|
||||
if match[1] == '@':
|
||||
comps = match.split('@')
|
||||
|
@ -610,6 +637,17 @@ class Matcher(object):
|
|||
|
||||
return eval(' '.join(results))
|
||||
|
||||
def nodegroup_match(self, tgt, nodegroups):
|
||||
'''
|
||||
This is a compatability matcher and is NOT called when using
|
||||
nodegroups for remote execution, but is called when the nodegroups
|
||||
matcher is used in states
|
||||
'''
|
||||
if tgt in nodegroups:
|
||||
return self.compound_match(nodegroups[tgt])
|
||||
return False
|
||||
|
||||
|
||||
class FileClient(object):
|
||||
'''
|
||||
Interact with the salt master file server.
|
||||
|
@ -716,6 +754,43 @@ class FileClient(object):
|
|||
fn_.write(data['data'])
|
||||
return dest
|
||||
|
||||
def get_dir(self, path, dest='', env='base'):
|
||||
'''
|
||||
Get a directory recursively from the salt-master
|
||||
'''
|
||||
ret = []
|
||||
# Strip trailing slash
|
||||
path = string.rstrip(self._check_proto(path), '/')
|
||||
# Break up the path into a list conaining the bottom-level directory
|
||||
# (the one being recursively copied) and the directories preceding it
|
||||
separated = string.rsplit(path,'/',1)
|
||||
if len(separated) != 2:
|
||||
# No slashes in path. (This means all files in env will be copied)
|
||||
prefix = ''
|
||||
else:
|
||||
prefix = separated[0]
|
||||
|
||||
# Copy files from master
|
||||
for fn_ in self.file_list(env):
|
||||
if fn_.startswith(path):
|
||||
# Remove the leading directories from path to derive
|
||||
# the relative path on the minion.
|
||||
minion_relpath = string.lstrip(fn_[len(prefix):],'/')
|
||||
ret.append(self.get_file('salt://{0}'.format(fn_),
|
||||
'%s/%s' % (dest,minion_relpath),
|
||||
True, env))
|
||||
# Replicate empty dirs from master
|
||||
for fn_ in self.file_list_emptydirs(env):
|
||||
if fn_.startswith(path):
|
||||
# Remove the leading directories from path to derive
|
||||
# the relative path on the minion.
|
||||
minion_relpath = string.lstrip(fn_[len(prefix):],'/')
|
||||
minion_mkdir = '%s/%s' % (dest,minion_relpath)
|
||||
os.makedirs(minion_mkdir)
|
||||
ret.append(minion_mkdir)
|
||||
ret.sort()
|
||||
return ret
|
||||
|
||||
def get_url(self, url, dest, makedirs=False, env='base'):
|
||||
'''
|
||||
Get a single file from a URL.
|
||||
|
@ -729,7 +804,7 @@ class FileClient(object):
|
|||
if makedirs:
|
||||
os.makedirs(destdir)
|
||||
else:
|
||||
return False
|
||||
return ''
|
||||
else:
|
||||
dest = os.path.join(
|
||||
self.opts['cachedir'],
|
||||
|
@ -754,7 +829,7 @@ class FileClient(object):
|
|||
*BaseHTTPServer.BaseHTTPRequestHandler.responses[ex.code]))
|
||||
except urllib2.URLError, ex:
|
||||
raise MinionError('Error reading {0}: {1}'.format(url, ex.reason))
|
||||
return False
|
||||
return ''
|
||||
|
||||
def cache_file(self, path, env='base'):
|
||||
'''
|
||||
|
@ -790,7 +865,10 @@ class FileClient(object):
|
|||
path = self._check_proto(path)
|
||||
for fn_ in self.file_list(env):
|
||||
if fn_.startswith(path):
|
||||
ret.append(self.cache_file('salt://{0}'.format(fn_), env))
|
||||
local = self.cache_file('salt://{0}'.format(fn_), env)
|
||||
if not fn_.strip():
|
||||
continue
|
||||
ret.append(local)
|
||||
return ret
|
||||
|
||||
def cache_local_file(self, path, **kwargs):
|
||||
|
@ -818,6 +896,17 @@ class FileClient(object):
|
|||
self.socket.send(self.serial.dumps(payload))
|
||||
return self.auth.crypticle.loads(self.serial.loads(self.socket.recv()))
|
||||
|
||||
def file_list_emptydirs(self, env='base'):
|
||||
'''
|
||||
List the empty dirs on the master
|
||||
'''
|
||||
payload = {'enc': 'aes'}
|
||||
load = {'env': env,
|
||||
'cmd': '_file_list_emptydirs'}
|
||||
payload['load'] = self.auth.crypticle.dumps(load)
|
||||
self.socket.send(self.serial.dumps(payload))
|
||||
return self.auth.crypticle.loads(self.serial.loads(self.socket.recv()))
|
||||
|
||||
def file_local_list(self, env='base'):
|
||||
'''
|
||||
List files in the local minion files and localfiles caches
|
||||
|
@ -906,3 +995,15 @@ class FileClient(object):
|
|||
payload['load'] = self.auth.crypticle.dumps(load)
|
||||
self.socket.send(self.serial.dumps(payload))
|
||||
return self.auth.crypticle.loads(self.serial.loads(self.socket.recv()))
|
||||
|
||||
def ext_nodes(self):
|
||||
'''
|
||||
Return the metadata derived from the external nodes system on the
|
||||
master.
|
||||
'''
|
||||
payload = {'enc': 'aes'}
|
||||
load = {'cmd': '_ext_nodes',
|
||||
'id': self.opts['id']}
|
||||
payload['load'] = self.auth.crypticle.dumps(load)
|
||||
self.socket.send(self.serial.dumps(payload))
|
||||
return self.auth.crypticle.loads(self.serial.loads(self.socket.recv()))
|
||||
|
|
|
@ -77,7 +77,7 @@ def refresh_db():
|
|||
return servers
|
||||
|
||||
|
||||
def install(pkg, refresh=False, repo='', skip_verify=False):
|
||||
def install(pkg, refresh=False, repo='', skip_verify=False, **kwargs):
|
||||
'''
|
||||
Install the passed package
|
||||
|
||||
|
@ -108,8 +108,8 @@ def install(pkg, refresh=False, repo='', skip_verify=False):
|
|||
|
||||
cmd = '{nonint} apt-get -q -y {confold}{verify}{target} install {pkg}'.format(
|
||||
nonint='DEBIAN_FRONTEND=noninteractive',
|
||||
confold='-o DPkg::Options::=--force-confold',
|
||||
verify='--allow-unauthenticated' if skip_verify else '',
|
||||
confold=' -o DPkg::Options::=--force-confold',
|
||||
verify=' --allow-unauthenticated' if skip_verify else '',
|
||||
target=' -t {0}'.format(repo) if repo else '',
|
||||
pkg=pkg)
|
||||
|
||||
|
|
|
@ -10,93 +10,96 @@ import os
|
|||
import subprocess
|
||||
import tempfile
|
||||
import salt.utils
|
||||
import pwd
|
||||
from salt.exceptions import CommandExecutionError
|
||||
try:
|
||||
import pwd
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Set the default working directory to the home directory
|
||||
# of the user salt-minion is running as. Default: /root
|
||||
DEFAULT_CWD = os.path.expanduser('~')
|
||||
|
||||
# Set up the default outputters
|
||||
__outputter__ = {
|
||||
'run': 'txt',
|
||||
}
|
||||
def _run(cmd,
|
||||
cwd=DEFAULT_CWD,
|
||||
cwd=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
quiet=False,
|
||||
runas=None):
|
||||
runas=None,
|
||||
with_env=True):
|
||||
'''
|
||||
Do the DRY thing and only call subprocess.Popen() once
|
||||
'''
|
||||
ret = {}
|
||||
uid = os.getuid()
|
||||
euid = os.geteuid()
|
||||
# Set the default working directory to the home directory
|
||||
# of the user salt-minion is running as. Default: /root
|
||||
if not cwd:
|
||||
cwd = os.path.expanduser('~{0}'.format('' if not runas else runas))
|
||||
|
||||
def su():
|
||||
os.setuid(runas_uid)
|
||||
os.seteuid(runas_uid)
|
||||
# TODO: Figure out the proper way to do this in windows
|
||||
disable_runas = [
|
||||
'Windows',
|
||||
]
|
||||
|
||||
ret = {}
|
||||
|
||||
if runas and __grains__['os'] in disable_runas:
|
||||
msg = 'Sorry, {0} does not support runas functionality'
|
||||
raise CommandExecutionError(msg.format(__grains__['os']))
|
||||
|
||||
if runas:
|
||||
# Save the original command before munging it
|
||||
orig_cmd = cmd
|
||||
try:
|
||||
p = pwd.getpwnam(runas)
|
||||
except KeyError:
|
||||
stderr_str = 'The user {0} is not available'.format(runas)
|
||||
if stderr == subprocess.STDOUT:
|
||||
ret['stdout'] = stderr_str
|
||||
else:
|
||||
ret['stdout'] = ''
|
||||
ret['stderr'] = stderr_str
|
||||
ret['retcode'] = 1
|
||||
return ret
|
||||
runas_uid = p.pw_uid
|
||||
preexec = su
|
||||
else:
|
||||
preexec = None
|
||||
msg = 'User \'{0}\' is not available'.format(runas)
|
||||
raise CommandExecutionError(msg)
|
||||
|
||||
cmd_prefix = 'su'
|
||||
|
||||
# Load the 'nix environment
|
||||
if with_env:
|
||||
cmd_prefix += ' - '
|
||||
|
||||
cmd_prefix += runas + ' -c'
|
||||
cmd = '{0} "{1}"'.format(cmd_prefix, cmd)
|
||||
|
||||
if not quiet:
|
||||
if runas:
|
||||
log.info('Executing command {0} as user {1} in directory {2}'.format(
|
||||
cmd, runas, cwd))
|
||||
else:
|
||||
# Put the most common case first
|
||||
if not runas:
|
||||
log.info('Executing command {0} in directory {1}'.format(cmd, cwd))
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(cmd,
|
||||
cwd=cwd,
|
||||
shell=True,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
preexec_fn=preexec
|
||||
)
|
||||
|
||||
out = proc.communicate()
|
||||
ret['stdout'] = out[0]
|
||||
ret['stderr'] = out[1]
|
||||
ret['retcode'] = proc.returncode
|
||||
ret['pid'] = proc.pid
|
||||
except OSError:
|
||||
stderr_str = 'Unable to change to user {0}: permission denied'.format(runas)
|
||||
if stderr == subprocess.STDOUT:
|
||||
ret['stdout'] = stderr_str
|
||||
else:
|
||||
ret['stdout'] = ''
|
||||
ret['stderr'] = stderr_str
|
||||
ret['retcode'] = 2
|
||||
log.info('Executing command {0} as user {1} in directory {2}'.format(
|
||||
orig_cmd, runas, cwd))
|
||||
|
||||
# This is where the magic happens
|
||||
proc = subprocess.Popen(cmd,
|
||||
cwd=cwd,
|
||||
shell=True,
|
||||
stdout=stdout,
|
||||
stderr=stderr
|
||||
)
|
||||
|
||||
out = proc.communicate()
|
||||
ret['stdout'] = out[0]
|
||||
ret['stderr'] = out[1]
|
||||
ret['pid'] = proc.pid
|
||||
ret['retcode'] = proc.returncode
|
||||
return ret
|
||||
|
||||
|
||||
def _run_quiet(cmd, cwd=DEFAULT_CWD, runas=None):
|
||||
def _run_quiet(cmd, cwd=None, runas=None):
|
||||
'''
|
||||
Helper for running commands quietly for minion startup
|
||||
'''
|
||||
return _run(cmd, runas=runas, cwd=cwd, stderr=subprocess.STDOUT, quiet=True)['stdout']
|
||||
|
||||
|
||||
def run(cmd, cwd=DEFAULT_CWD, runas=None):
|
||||
def run(cmd, cwd=None, runas=None):
|
||||
'''
|
||||
Execute the passed command and return the output as a string
|
||||
|
||||
|
@ -109,7 +112,7 @@ def run(cmd, cwd=DEFAULT_CWD, runas=None):
|
|||
return out
|
||||
|
||||
|
||||
def run_stdout(cmd, cwd=DEFAULT_CWD, runas=None):
|
||||
def run_stdout(cmd, cwd=None, runas=None):
|
||||
'''
|
||||
Execute a command, and only return the standard out
|
||||
|
||||
|
@ -122,7 +125,7 @@ def run_stdout(cmd, cwd=DEFAULT_CWD, runas=None):
|
|||
return stdout
|
||||
|
||||
|
||||
def run_stderr(cmd, cwd=DEFAULT_CWD, runas=None):
|
||||
def run_stderr(cmd, cwd=None, runas=None):
|
||||
'''
|
||||
Execute a command and only return the standard error
|
||||
|
||||
|
@ -135,7 +138,7 @@ def run_stderr(cmd, cwd=DEFAULT_CWD, runas=None):
|
|||
return stderr
|
||||
|
||||
|
||||
def run_all(cmd, cwd=DEFAULT_CWD, runas=None):
|
||||
def run_all(cmd, cwd=None, runas=None):
|
||||
'''
|
||||
Execute the passed command and return a dict of return data
|
||||
|
||||
|
@ -155,7 +158,7 @@ def run_all(cmd, cwd=DEFAULT_CWD, runas=None):
|
|||
return ret
|
||||
|
||||
|
||||
def retcode(cmd, cwd=DEFAULT_CWD, runas=None):
|
||||
def retcode(cmd, cwd=None, runas=None):
|
||||
'''
|
||||
Execute a shell command and return the command's return code.
|
||||
|
||||
|
@ -186,7 +189,7 @@ def which(cmd):
|
|||
'''
|
||||
return salt.utils.which(cmd)
|
||||
|
||||
def exec_code(lang, code, cwd=DEFAULT_CWD):
|
||||
def exec_code(lang, code, cwd=None):
|
||||
'''
|
||||
Pass in two strings, the first naming the executable language, aka -
|
||||
python2, python3, ruby, perl, lua, etc. the second string containing
|
||||
|
|
|
@ -38,19 +38,35 @@ def recv(files, dest):
|
|||
def get_file(path, dest, env='base'):
|
||||
'''
|
||||
Used to get a single file from the salt master
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.get_file salt://path/to/file /minion/dest
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.get_file(path, dest, False, env)
|
||||
|
||||
|
||||
def get_dir(path, dest, env='base'):
|
||||
'''
|
||||
Used to recursively copy a directory from the salt master
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt '*' cp.get_dir salt://path/to/dir/ /minion/dest
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.get_dir(path, dest, env)
|
||||
|
||||
|
||||
def get_url(path, dest, env='base'):
|
||||
'''
|
||||
Used to get a single file from a URL.
|
||||
|
||||
For example::
|
||||
CLI Example::
|
||||
|
||||
cp.get_url salt://my/file /tmp/mine
|
||||
cp.get_url http://www.slashdot.org /tmp/index.html
|
||||
salt '*' cp.get_url salt://my/file /tmp/mine
|
||||
salt '*' cp.get_url http://www.slashdot.org /tmp/index.html
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.get_url(path, dest, False, env)
|
||||
|
@ -59,6 +75,10 @@ def get_url(path, dest, env='base'):
|
|||
def cache_file(path, env='base'):
|
||||
'''
|
||||
Used to cache a single file in the local salt-master file cache.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.cache_file salt://path/to/file
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.cache_file(path, env)
|
||||
|
@ -69,6 +89,10 @@ def cache_files(paths, env='base'):
|
|||
Used to gather many files from the master, the gathered files will be
|
||||
saved in the minion cachedir reflective to the paths retrieved from the
|
||||
master.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.cache_files salt://pathto/file1,salt://pathto/file1
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.cache_files(paths, env)
|
||||
|
@ -77,6 +101,10 @@ def cache_files(paths, env='base'):
|
|||
def cache_dir(path, env='base'):
|
||||
'''
|
||||
Download and cache everything under a directory from the master
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.cache_dir salt://path/to/dir
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.cache_dir(path, env)
|
||||
|
@ -85,6 +113,10 @@ def cache_dir(path, env='base'):
|
|||
def cache_master(env='base'):
|
||||
'''
|
||||
Retrieve all of the files on the master and cache them locally
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.cache_master
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.cache_master(env)
|
||||
|
@ -93,6 +125,10 @@ def cache_master(env='base'):
|
|||
def cache_local_file(path):
|
||||
'''
|
||||
Cache a local file on the minion in the localfiles cache
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.cache_local_file /etc/hosts
|
||||
'''
|
||||
if not os.path.exists(path):
|
||||
return ''
|
||||
|
@ -115,6 +151,10 @@ def cache_local_file(path):
|
|||
def list_master(env='base'):
|
||||
'''
|
||||
List all of the files stored on the master
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.list_master
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.file_list(env)
|
||||
|
@ -123,6 +163,10 @@ def list_master(env='base'):
|
|||
def list_minion(env='base'):
|
||||
'''
|
||||
List all of the files cached on the minion
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.list_minion
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.file_local_list(env)
|
||||
|
@ -132,6 +176,10 @@ def is_cached(path, env='base'):
|
|||
'''
|
||||
Return a boolean if the given path on the master has been cached on the
|
||||
minion
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.is_cached salt://path/to/file
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.is_cached(path, env)
|
||||
|
@ -142,6 +190,10 @@ def hash_file(path, env='base'):
|
|||
Return the hash of a file, to get the hash of a file on the
|
||||
salt master file server prepend the path with salt://<file on server>
|
||||
otherwise, prepend the file with / for a local file.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' cp.hash_file salt://path/to/file
|
||||
'''
|
||||
client = salt.minion.FileClient(__opts__)
|
||||
return client.hash_file(path, env)
|
||||
|
|
|
@ -12,7 +12,6 @@ def echo(text):
|
|||
CLI Example:
|
||||
salt '*' test.echo 'foo bar baz quo qux'
|
||||
'''
|
||||
print 'Echo got called!'
|
||||
return text
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
Module for gathering disk information
|
||||
'''
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on posix-like systems
|
||||
|
@ -34,13 +39,17 @@ def usage():
|
|||
if line.startswith('Filesystem'):
|
||||
continue
|
||||
comps = line.split()
|
||||
ret[comps[5]] = {
|
||||
'filesystem': comps[0],
|
||||
'1K-blocks': comps[1],
|
||||
'used': comps[2],
|
||||
'available': comps[3],
|
||||
'capacity': comps[4],
|
||||
}
|
||||
try:
|
||||
ret[comps[5]] = {
|
||||
'filesystem': comps[0],
|
||||
'1K-blocks': comps[1],
|
||||
'used': comps[2],
|
||||
'available': comps[3],
|
||||
'capacity': comps[4],
|
||||
}
|
||||
except IndexError:
|
||||
log.warn("Problem parsing disk usage information")
|
||||
ret = {}
|
||||
return ret
|
||||
|
||||
def inodeusage():
|
||||
|
@ -71,5 +80,6 @@ def inodeusage():
|
|||
'filesystem': comps[0],
|
||||
}
|
||||
except IndexError:
|
||||
print "DEBUG: comps='%s'" % comps
|
||||
log.warn("Problem parsing inode usage information")
|
||||
ret = {}
|
||||
return ret
|
||||
|
|
|
@ -155,12 +155,13 @@ def set_mode(path, mode):
|
|||
|
||||
salt '*' file.set_mode /etc/passwd 0644
|
||||
'''
|
||||
mode = str(mode)
|
||||
mode = str(mode).lstrip('0')
|
||||
if not mode:
|
||||
mode = '0'
|
||||
if not os.path.exists(path):
|
||||
return 'File not found'
|
||||
try:
|
||||
os.chmod(path, int(mode, 8))
|
||||
# FIXME: don't use a catch-all, be more specific...
|
||||
except:
|
||||
return 'Invalid Mode ' + mode
|
||||
return get_mode(path)
|
||||
|
|
115
salt/modules/freebsdservice.py
Normal file
115
salt/modules/freebsdservice.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
'''
|
||||
The service module for FreeBSD
|
||||
'''
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on systems which default to systemd
|
||||
'''
|
||||
# Disable on these platforms, specific service modules exist:
|
||||
if __grains__['os'] == 'FreeBSD':
|
||||
return 'service'
|
||||
return False
|
||||
|
||||
def get_enabled():
|
||||
'''
|
||||
Return what services are set to run on boot
|
||||
'''
|
||||
ret = []
|
||||
for rcfn in ('/etc/rc.conf', '/etc/rc.conf.local'):
|
||||
if os.path.isfile(rcfn):
|
||||
for line in open(rcfn, 'r').readlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
if not '_enable' in line:
|
||||
continue
|
||||
if not '=' in line:
|
||||
continue
|
||||
comps = line.split('=')
|
||||
if 'YES' in comps[1]:
|
||||
# Is enabled!
|
||||
ret.append(comps[0].split('_')[0])
|
||||
return ret
|
||||
|
||||
|
||||
def get_disabled():
|
||||
'''
|
||||
Return what services are available but not enabled to start at boot
|
||||
'''
|
||||
en_ = get_enabled()
|
||||
all_ = get_all()
|
||||
return sorted(set(all_).difference(set(en_)))
|
||||
|
||||
|
||||
def get_all():
|
||||
'''
|
||||
Return a list of all available services
|
||||
'''
|
||||
ret = set()
|
||||
for rcdir in ('/etc/rc.d/', '/usr/local/etc/rc.d/'):
|
||||
ret.update(os.listdir(rcdir))
|
||||
rm_ = set()
|
||||
for srv in ret:
|
||||
if srv.isupper():
|
||||
rm_.add(srv)
|
||||
ret.difference(rm_)
|
||||
return sorted(ret)
|
||||
|
||||
|
||||
def start(name):
|
||||
'''
|
||||
Start the specified service
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' service.start <service name>
|
||||
'''
|
||||
cmd = 'service {0} onestart'.format(name)
|
||||
return not __salt__['cmd.retcode'](cmd)
|
||||
|
||||
|
||||
def stop(name):
|
||||
'''
|
||||
Stop the specified service
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' service.stop <service name>
|
||||
'''
|
||||
cmd = 'service {0} onestop'.format(name)
|
||||
return not __salt__['cmd.retcode'](cmd)
|
||||
|
||||
|
||||
def restart(name):
|
||||
'''
|
||||
Restart the named service
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' service.restart <service name>
|
||||
'''
|
||||
cmd = 'service {0} onerestart'.format(name)
|
||||
return not __salt__['cmd.retcode'](cmd)
|
||||
|
||||
|
||||
def status(name, sig=None):
|
||||
'''
|
||||
Return the status for a service, returns the PID or an empty string if the
|
||||
service is running or not, pass a signature to use to find the service via
|
||||
ps
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' service.status <service name> [service signature]
|
||||
'''
|
||||
sig = name if not sig else sig
|
||||
cmd = "{0[ps]} | grep {1} | grep -v grep | awk '{{print $2}}'".format(
|
||||
__grains__, sig)
|
||||
return __salt__['cmd.run'](cmd).strip()
|
||||
|
||||
|
|
@ -5,6 +5,11 @@ Manage the information in the hosts file
|
|||
import os
|
||||
|
||||
def __get_hosts_filename():
|
||||
'''
|
||||
Return the path to the appropriate hosts file
|
||||
'''
|
||||
if 'hosts.file' in __opts__:
|
||||
return __opts__['hosts.file']
|
||||
if __grains__['kernel'].startswith('Windows'):
|
||||
return 'C:\Windows\System32\drivers\etc\hosts'
|
||||
else:
|
||||
|
|
456
salt/modules/kvm_hyper.py
Normal file
456
salt/modules/kvm_hyper.py
Normal file
|
@ -0,0 +1,456 @@
|
|||
'''
|
||||
Provide the hyper module for kvm hypervisors. This is the interface used to
|
||||
interact with kvm on behalf of the salt-virt interface
|
||||
'''
|
||||
|
||||
# This is a test interface for the salt-virt system. The api in this file is
|
||||
# VERY likely to change.
|
||||
|
||||
|
||||
# Import Python Libs
|
||||
from xml.dom import minidom
|
||||
import StringIO
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
# Import libvirt
|
||||
import libvirt
|
||||
|
||||
# Import Third party modules
|
||||
import yaml
|
||||
|
||||
# Import Salt Modules
|
||||
import salt.utils
|
||||
|
||||
VIRT_STATE_NAME_MAP = {0: "running",
|
||||
1: "running",
|
||||
2: "running",
|
||||
3: "paused",
|
||||
4: "shutdown",
|
||||
5: "shutdown",
|
||||
6: "crashed"}
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Apply this module as the hyper module if the minion is a kvm hypervisor
|
||||
'''
|
||||
if __grains__['virtual'] != 'physical':
|
||||
return False
|
||||
if 'kvm_' not in open('/proc/modules').read():
|
||||
return False
|
||||
#libvirt_ret = __salt__['cmd.run'](__grains__['ps']).count('libvirtd')
|
||||
try:
|
||||
libvirt_conn = libvirt.open('qemu:///system')
|
||||
libvirt_conn.close()
|
||||
return 'hyper'
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def __get_conn():
|
||||
'''
|
||||
Connects to the libvirt daemon for qemu/kvm
|
||||
'''
|
||||
return libvirt.open("qemu:///system")
|
||||
|
||||
|
||||
def _get_dom(vm_):
|
||||
'''
|
||||
Return a domain object for the named vm
|
||||
'''
|
||||
conn = __get_conn()
|
||||
if vm_ not in list_virts():
|
||||
raise Exception('The specified vm is not present')
|
||||
return conn.lookupByName(vm_)
|
||||
|
||||
# Define tier 1 Virt functions, all hyper interfaces have these:
|
||||
# hyper_type
|
||||
# list_virts
|
||||
# hyper_info
|
||||
# get_conf
|
||||
|
||||
|
||||
def hyper_type():
|
||||
'''
|
||||
Return that type of hypervisor this is
|
||||
'''
|
||||
return 'kvm'
|
||||
|
||||
|
||||
def freemem():
|
||||
'''
|
||||
Return an int representing the amount of memory that has not been given
|
||||
to virtual machines on this node
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.freemem
|
||||
'''
|
||||
conn = __get_conn()
|
||||
mem = conn.getInfo()[1]
|
||||
# Take off just enough to sustain the hypervisor
|
||||
mem -= 256
|
||||
for vm_ in list_virts():
|
||||
dom = _get_dom(vm_)
|
||||
if dom.ID() > 0:
|
||||
mem -= dom.info()[2] / 1024
|
||||
return mem
|
||||
|
||||
|
||||
def freecpu():
|
||||
'''
|
||||
Return an int representing the number of unallocated cpus on this
|
||||
hypervisor
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.freemem
|
||||
'''
|
||||
conn = __get_conn()
|
||||
cpus = conn.getInfo()[2]
|
||||
for vm_ in list_virts():
|
||||
dom = _get_dom(vm_)
|
||||
if dom.ID() > 0:
|
||||
cpus -= dom.info()[3]
|
||||
return cpus
|
||||
|
||||
|
||||
def list_virts():
|
||||
'''
|
||||
Return a list of virtual machine names on the minion
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.list_virts
|
||||
'''
|
||||
# Expand to include down vms
|
||||
conn = __get_conn()
|
||||
vms = []
|
||||
for id_ in conn.listDomainsID():
|
||||
vms.append(conn.lookupByID(id_).name())
|
||||
return vms
|
||||
|
||||
|
||||
def virt_info():
|
||||
'''
|
||||
Return detailed information about the vms on this hyper in a dict::
|
||||
|
||||
{'cpu': <int>,
|
||||
'maxMem': <int>,
|
||||
'mem': <int>,
|
||||
'state': '<state>',
|
||||
'cputime' <int>}
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.vm_info
|
||||
'''
|
||||
info = {}
|
||||
for vm_ in list_virts():
|
||||
dom = _get_dom(vm_)
|
||||
raw = dom.info()
|
||||
info[vm_] = {
|
||||
'state': VIRT_STATE_NAME_MAP.get(raw[0], 'unknown'),
|
||||
'maxMem': int(raw[1]),
|
||||
'mem': int(raw[2]),
|
||||
'cpu': raw[3],
|
||||
'cputime': int(raw[4]),
|
||||
'disks': get_disks(vm_),
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
def hyper_info():
|
||||
'''
|
||||
Return a dict with information about this hypervisor
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.node_info
|
||||
'''
|
||||
conn = __get_conn()
|
||||
raw = conn.getInfo()
|
||||
info = {
|
||||
'phymemory': raw[1],
|
||||
'cpus': raw[2],
|
||||
'cpumhz': raw[3],
|
||||
'cpucores': raw[6],
|
||||
'cputhreads': raw[7],
|
||||
'type': hyper_type(),
|
||||
'freecpu': freecpu(),
|
||||
'freemem': freemem(),
|
||||
'virt_info': virt_info(),
|
||||
}
|
||||
return info
|
||||
|
||||
# Level 2 - vm class specific
|
||||
# init - Create a vm from options
|
||||
# start - Start a down vm
|
||||
# halt
|
||||
# purge
|
||||
# pause
|
||||
# resume
|
||||
# set_autostart
|
||||
# get_disks
|
||||
# get_conf
|
||||
|
||||
def _get_image(image, vda):
|
||||
'''
|
||||
Copy the image into place
|
||||
'''
|
||||
if ':' in image:
|
||||
if not os.path.isabs(image) or not image.startswith('file://'):
|
||||
# The image is on a network resource
|
||||
env = 'base'
|
||||
if not image.rindex(':') == 4:
|
||||
env = image.split(':')[-1]
|
||||
image = image[:image.rindex(':')]
|
||||
__salt__['cp.get_url'](image, vda, env)
|
||||
if os.path.isabs(image) or image.startswith('file://'):
|
||||
# This is a local file, copy it into place
|
||||
if image.startswith('file://'):
|
||||
# Condition this into a standard path
|
||||
for ind in range(6, len(image)):
|
||||
if image[ind].isalpha():
|
||||
image = os.path.join('/', image[ind:])
|
||||
break
|
||||
shutil.copy2(image, vda)
|
||||
|
||||
|
||||
def _gen_xml(name,
|
||||
cpus,
|
||||
mem,
|
||||
vmdir,
|
||||
disks,
|
||||
network,
|
||||
desc,
|
||||
opts):
|
||||
'''
|
||||
Generate the xml used for the libvirt configuration
|
||||
'''
|
||||
# Don't generate the libvirt config if it already exists
|
||||
vda = os.path.join(vmdir, 'vda')
|
||||
data = '''
|
||||
<domain type='kvm'>
|
||||
<name>%%NAME%%</name>
|
||||
<vcpu>%%CPU%%</vcpu>
|
||||
<memory>%%MEM%%</memory>
|
||||
<os>
|
||||
<type>hvm</type>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
<devices>
|
||||
<emulator>/usr/bin/kvm</emulator>
|
||||
<disk type='file' device='disk'>
|
||||
<source file='%%VDA%%'/>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
<driver name='qemu' cache='writeback' io='native'/>
|
||||
</disk>
|
||||
%%DISK%%
|
||||
%%NICS%%
|
||||
<graphics type='vnc' listen='0.0.0.0' autoport='yes'/>
|
||||
</devices>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
</domain>
|
||||
'''
|
||||
data = data.replace('%%NAME%%', name)
|
||||
data = data.replace('%%CPU%%', str(cpus))
|
||||
data = data.replace('%%MEM%%', str(int(mem) * 1024))
|
||||
data = data.replace('%%VDA%%', vda)
|
||||
nics = ''
|
||||
for interface, data in network.items():
|
||||
for bridge, mac in data.items():
|
||||
if not mac:
|
||||
# Generate this interface's mac addr, use the qemu default
|
||||
# prefix, 52:54
|
||||
mac = salt.utils.gen_mac('52:54:')
|
||||
nic = '''
|
||||
<interface type='bridge'>
|
||||
<source bridge='%%BRIDGE%%'/>
|
||||
<mac address='%%MAC%%'/>
|
||||
<model type='virtio'/>
|
||||
</interface>\n'''
|
||||
nic = nic.replace('%%BRIDGE%%', bridge)
|
||||
nic = nic.replace('%%MAC%%', mac)
|
||||
nics += nic
|
||||
data = data.replace('%%NICS%%', nics)
|
||||
|
||||
if disks:
|
||||
letters = salt.utils.gen_letters()
|
||||
disk_str = ''
|
||||
for ind in range(0, len(disks)):
|
||||
disk = disks[ind]
|
||||
disk_d = '''
|
||||
<disk type='file' device='disk'>
|
||||
<source file='%%DISK_PATH%%'/>
|
||||
<target dev='%%VD%%' bus='virtio'/>
|
||||
<driver name='qemu' type='%%TYPE%%' cache='writeback' io='native'/>
|
||||
</disk>
|
||||
'''
|
||||
|
||||
disk_d = disk_d.replace('%%DISK_PATH%%', disk['path'])
|
||||
disk_d = disk_d.replace('%%TYPE%%', disk['format'])
|
||||
disk_d = disk_d.replace('%%VD%%', 'vd' + letters[ind + 1])
|
||||
|
||||
disk_str += disk_d
|
||||
data = data.replace('%%DISK%%', disk_str)
|
||||
else:
|
||||
data = data.replace('%%DISK%%', '')
|
||||
return data
|
||||
|
||||
|
||||
def init(
|
||||
name,
|
||||
cpus,
|
||||
mem,
|
||||
image,
|
||||
storage_dir,
|
||||
network={'eth0': {'bridge': 'br0', 'mac': ''}},
|
||||
desc='',
|
||||
opts={}):
|
||||
'''
|
||||
Create a KVM virtual machine based on these passed options, the virtual
|
||||
machine will be started upon creation
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt node1 webserver 2 2048 salt://fedora/f16.img:virt /srv/vm/images
|
||||
'''
|
||||
vmdir = os.path.join(storage_dir, name)
|
||||
if not os.path.exists(vmdir):
|
||||
os.makedirs(vmdir)
|
||||
vda = os.path.join(vmdir, 'vda')
|
||||
_get_image(image, vda)
|
||||
# The image is in place
|
||||
xml = _gen_xml(name, cpus, mem, vmdir, network, desc, opts)
|
||||
config = os.path.join(vmdir, 'config.xml')
|
||||
open(config, 'w+').write(xml)
|
||||
return start(config)
|
||||
|
||||
|
||||
def start(config):
|
||||
'''
|
||||
Start an already defined virtual machine that has been shut down
|
||||
'''
|
||||
# change this to use the libvirt api and add more logging and a verbose
|
||||
# return
|
||||
cmd = 'virsh create {0}'.format(config)
|
||||
return not __salt__['cmd.retcode'](cmd)
|
||||
|
||||
def halt(name):
|
||||
'''
|
||||
Hard power down a virtual machine
|
||||
'''
|
||||
try:
|
||||
dom = _get_dom(name)
|
||||
dom.destroy()
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def purge(name):
|
||||
'''
|
||||
Hard power down and purge a virtual machine, this will destroy a vm and
|
||||
all associated vm data
|
||||
'''
|
||||
disks = get_disks(name)
|
||||
halt(name)
|
||||
directories = set()
|
||||
for disk in disks:
|
||||
os.remove(disks[disk]['file'])
|
||||
directories.add(os.path.dirname(disks[disk]['file']))
|
||||
if directories:
|
||||
for dir_ in directories:
|
||||
shutil.rmtree(dir_)
|
||||
return True
|
||||
|
||||
|
||||
def pause(name):
|
||||
'''
|
||||
Pause the named virtual machine
|
||||
'''
|
||||
dom = _get_dom(name)
|
||||
dom.suspend()
|
||||
return True
|
||||
|
||||
|
||||
def resume(name):
|
||||
'''
|
||||
Resume the named virtual machine
|
||||
'''
|
||||
dom = _get_dom(name)
|
||||
dom.resume()
|
||||
return True
|
||||
|
||||
|
||||
def set_autostart(name):
|
||||
'''
|
||||
Set the named virtual machine to autostart when the hypervisor boots
|
||||
'''
|
||||
dom = _get_dom(name)
|
||||
|
||||
if state == 'on':
|
||||
if dom.setAutostart(1) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
elif state == 'off':
|
||||
if dom.setAutostart(0) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
else:
|
||||
# return False if state is set to something other then on or off
|
||||
return False
|
||||
|
||||
|
||||
def get_disks(name):
|
||||
'''
|
||||
Return the disks of a named virt
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.get_disks <vm name>
|
||||
'''
|
||||
disks = {}
|
||||
doc = minidom.parse(StringIO.StringIO(get_conf(name)))
|
||||
for elem in doc.getElementsByTagName('disk'):
|
||||
sources = elem.getElementsByTagName('source')
|
||||
targets = elem.getElementsByTagName('target')
|
||||
if len(sources) > 0:
|
||||
source = sources[0]
|
||||
else:
|
||||
continue
|
||||
if len(targets) > 0:
|
||||
target = targets[0]
|
||||
else:
|
||||
continue
|
||||
if 'dev' in target.attributes.keys() \
|
||||
and 'file' in source.attributes.keys():
|
||||
disks[target.getAttribute('dev')] = \
|
||||
{'file': source.getAttribute('file')}
|
||||
for dev in disks:
|
||||
disks[dev].update(yaml.safe_load(subprocess.Popen('qemu-img info ' \
|
||||
+ disks[dev]['file'],
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE).communicate()[0]))
|
||||
return disks
|
||||
|
||||
|
||||
def get_conf(name):
|
||||
'''
|
||||
Returns the xml for a given vm
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' virt.get_conf <vm name>
|
||||
'''
|
||||
dom = _get_dom(name)
|
||||
return dom.XMLDesc(0)
|
|
@ -65,6 +65,8 @@ def assign(name, value):
|
|||
cmd = 'sysctl -w {0}={1}'.format(name, value)
|
||||
ret = {}
|
||||
out = __salt__['cmd.run'](cmd).strip()
|
||||
if ' = ' not in out:
|
||||
raise CommandExecutionError('sysctl -w failed: {0}'.format(out))
|
||||
comps = out.split(' = ')
|
||||
ret[comps[0]] = comps[1]
|
||||
return ret
|
||||
|
|
148
salt/modules/mysql.py
Normal file → Executable file
148
salt/modules/mysql.py
Normal file → Executable file
|
@ -23,6 +23,15 @@ import MySQLdb.cursors
|
|||
log = logging.getLogger(__name__)
|
||||
__opts__ = {}
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load this module if the mysql config is set
|
||||
'''
|
||||
if 'mysql' in __opts__:
|
||||
return 'mysql'
|
||||
return False
|
||||
|
||||
|
||||
def __check_table(name, table):
|
||||
db = connect()
|
||||
cur = db.cursor(MySQLdb.cursors.DictCursor)
|
||||
|
@ -120,7 +129,7 @@ def slave_lag():
|
|||
'''
|
||||
Return the number of seconds that a slave SQL server is lagging behind the
|
||||
master, if the host is not a slave it will return -1. If the server is
|
||||
configured to be a slave but replication but slave IO is not running then
|
||||
configured to be a slave for replication but slave IO is not running then
|
||||
-2 will be returned.
|
||||
|
||||
CLI Example::
|
||||
|
@ -189,7 +198,7 @@ def db_list():
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_list
|
||||
salt '*' mysql.db_list
|
||||
'''
|
||||
ret = []
|
||||
db = connect()
|
||||
|
@ -208,7 +217,7 @@ def db_tables(name):
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_tables 'database'
|
||||
salt '*' mysql.db_tables 'database'
|
||||
'''
|
||||
if not db_exists(name):
|
||||
log.info("Database '{0}' does not exist".format(name,))
|
||||
|
@ -233,7 +242,7 @@ def db_exists(name):
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_exists 'dbname'
|
||||
salt '*' mysql.db_exists 'dbname'
|
||||
'''
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
@ -252,7 +261,7 @@ def db_create(name):
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_create 'dbname'
|
||||
salt '*' mysql.db_create 'dbname'
|
||||
'''
|
||||
# check if db exists
|
||||
if db_exists(name):
|
||||
|
@ -262,7 +271,7 @@ def db_create(name):
|
|||
# db doesnt exist, proceed
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
query = "CREATE DATABASE %s;" % name
|
||||
query = "CREATE DATABASE `%s`;" % name
|
||||
log.debug("Query: {0}".format(query,))
|
||||
if cur.execute( query ):
|
||||
log.info("DB '{0}' created".format(name,))
|
||||
|
@ -275,7 +284,7 @@ def db_remove(name):
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_remove 'dbname'
|
||||
salt '*' mysql.db_remove 'dbname'
|
||||
'''
|
||||
# check if db exists
|
||||
if not db_exists(name):
|
||||
|
@ -289,7 +298,7 @@ def db_remove(name):
|
|||
# db doesnt exist, proceed
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
query = "DROP DATABASE %s;" % name
|
||||
query = "DROP DATABASE `%s`;" % name
|
||||
log.debug("Doing query: {0}".format(query,))
|
||||
cur.execute( query )
|
||||
|
||||
|
@ -309,7 +318,7 @@ def user_list():
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.user_list
|
||||
salt '*' mysql.user_list
|
||||
'''
|
||||
db = connect()
|
||||
cur = db.cursor(MySQLdb.cursors.DictCursor)
|
||||
|
@ -325,7 +334,7 @@ def user_exists(user,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.user_exists 'username' 'hostname'
|
||||
salt '*' mysql.user_exists 'username' 'hostname'
|
||||
'''
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
@ -343,7 +352,7 @@ def user_info(user,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.user_info root localhost
|
||||
salt '*' mysql.user_info root localhost
|
||||
'''
|
||||
db = connect()
|
||||
cur = db.cursor (MySQLdb.cursors.DictCursor)
|
||||
|
@ -362,7 +371,7 @@ def user_create(user,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.user_create 'username' 'hostname' 'password'
|
||||
salt '*' mysql.user_create 'username' 'hostname' 'password'
|
||||
'''
|
||||
if user_exists(user,host):
|
||||
log.info("User '{0}'@'{1}' already exists".format(user,host,))
|
||||
|
@ -392,7 +401,7 @@ def user_chpass(user,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.user_chpass frank localhost newpassword
|
||||
salt '*' mysql.user_chpass frank localhost newpassword
|
||||
'''
|
||||
if password is None:
|
||||
log.error('No password provided')
|
||||
|
@ -410,13 +419,13 @@ def user_chpass(user,
|
|||
return False
|
||||
|
||||
def user_remove(user,
|
||||
host='localhost'):
|
||||
host='localhost'):
|
||||
'''
|
||||
Delete MySQL user
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.user_remove frank localhost
|
||||
salt '*' mysql.user_remove frank localhost
|
||||
'''
|
||||
db = connect()
|
||||
cur = db.cursor ()
|
||||
|
@ -441,7 +450,7 @@ def db_check(name,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_check dbname
|
||||
salt '*' mysql.db_check dbname
|
||||
'''
|
||||
ret = []
|
||||
if table is None:
|
||||
|
@ -462,7 +471,7 @@ def db_repair(name,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_repair dbname
|
||||
salt '*' mysql.db_repair dbname
|
||||
'''
|
||||
ret = []
|
||||
if table is None:
|
||||
|
@ -483,7 +492,7 @@ def db_optimize(name,
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysqldb.db_optimize dbname
|
||||
salt '*' mysql.db_optimize dbname
|
||||
'''
|
||||
ret = []
|
||||
if table is None:
|
||||
|
@ -496,3 +505,106 @@ def db_optimize(name,
|
|||
log.info("Optimizing table '%s' in db '%s'..".format(name,table,))
|
||||
ret = __optimize_table(name,table)
|
||||
return ret
|
||||
|
||||
'''
|
||||
Grants
|
||||
'''
|
||||
def __grant_generate(grant,
|
||||
database,
|
||||
user,
|
||||
host='localhost'):
|
||||
# todo: Re-order the grant so it is according to the SHOW GRANTS for xxx@yyy query (SELECT comes first, etc)
|
||||
grant = grant.replace(',', ', ').upper()
|
||||
|
||||
db_part = database.rpartition('.')
|
||||
db = db_part[0]
|
||||
table = db_part[2]
|
||||
|
||||
query = "GRANT %s ON `%s`.`%s` TO '%s'@'%s'" % (grant, db, table, user, host,)
|
||||
log.debug("Query generated: {0}".format(query,))
|
||||
return query
|
||||
|
||||
def user_grants(user,
|
||||
host='localhost'):
|
||||
'''
|
||||
Shows the grants for the given MySQL user (if it exists)
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysql.user_grants 'frank' 'localhost'
|
||||
'''
|
||||
if not user_exists(user):
|
||||
log.info("User '{0}' does not exist".format(user,))
|
||||
return False
|
||||
|
||||
ret = []
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
query = "SHOW GRANTS FOR '%s'@'%s'" % (user,host,)
|
||||
log.debug("Doing query: {0}".format(query,))
|
||||
|
||||
cur.execute(query)
|
||||
results = cur.fetchall()
|
||||
for grant in results:
|
||||
ret.append(grant[0])
|
||||
log.debug(ret)
|
||||
return ret
|
||||
|
||||
def grant_exists(grant,
|
||||
database,
|
||||
user,
|
||||
host='localhost'):
|
||||
# todo: This function is a bit tricky, since it requires the ordering to be exactly the same.
|
||||
# perhaps should be replaced/reworked with a better/cleaner solution.
|
||||
target = __grant_generate(grant, database, user, host)
|
||||
|
||||
if target in user_grants(user, host):
|
||||
log.debug("Grant exists.")
|
||||
return True
|
||||
|
||||
log.debug("Grant does not exist, or is perhaps not ordered properly?")
|
||||
return False
|
||||
|
||||
def grant_add(grant,
|
||||
database,
|
||||
user,
|
||||
host='localhost'):
|
||||
'''
|
||||
Adds a grant to the MySQL server.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysql.grant_add 'SELECT|INSERT|UPDATE|...' 'database.*' 'frank' 'localhost'
|
||||
'''
|
||||
# todo: validate grant
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
||||
query = __grant_generate(grant, database, user, host)
|
||||
log.debug("Query: {0}".format(query,))
|
||||
if cur.execute( query ):
|
||||
log.info("Grant '{0}' created")
|
||||
return True
|
||||
return False
|
||||
|
||||
def grant_revoke(grant,
|
||||
database,
|
||||
user,
|
||||
host='localhost'):
|
||||
'''
|
||||
Removes a grant from the MySQL server.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' mysql.grant_revoke 'SELECT,INSERT,UPDATE' 'database.*' 'frank' 'localhost'
|
||||
'''
|
||||
# todo: validate grant
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
query = "REVOKE %s ON %s FROM '%s'@'%s';" % (grant, database, user, host,)
|
||||
log.debug("Query: {0}".format(query,))
|
||||
if cur.execute( query ):
|
||||
log.info("Grant '{0}' revoked")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ def ping(host):
|
|||
|
||||
CLI Example::
|
||||
|
||||
salt '*' network.ping archlinux.org -c 4
|
||||
salt '*' network.ping archlinux.org
|
||||
'''
|
||||
cmd = 'ping -c 4 %s' % _sanitize_host(host)
|
||||
return __salt__['cmd.run'](cmd)
|
||||
|
@ -154,10 +154,14 @@ def _cidr_to_ipv4_netmask(cidr_bits):
|
|||
return netmask
|
||||
|
||||
|
||||
def _interfaces():
|
||||
def interfaces():
|
||||
'''
|
||||
Returns interface info
|
||||
|
||||
Returns a dictionary of interfaces with various information about each
|
||||
(up/down state, ip address, netmask, and hwaddr)
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' network.interfaces
|
||||
'''
|
||||
ret = {}
|
||||
|
||||
|
@ -187,11 +191,13 @@ def _interfaces():
|
|||
netmask = _cidr_to_ipv4_netmask(int(cidr))
|
||||
elif type.startswith('link'):
|
||||
hwaddr = value
|
||||
|
||||
if iface:
|
||||
ret[iface] = (up,ipaddr,netmask,hwaddr)
|
||||
ret[iface] = {}
|
||||
ret[iface]['up'] = up
|
||||
ret[iface]['ipaddr'] = ipaddr
|
||||
ret[iface]['netmask'] = netmask
|
||||
ret[iface]['hwaddr'] = hwaddr
|
||||
del iface,up
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -203,9 +209,9 @@ def up(interface):
|
|||
|
||||
salt '*' network.up eth0
|
||||
'''
|
||||
data = _interfaces().get(interface)
|
||||
data = interfaces().get(interface)
|
||||
if data:
|
||||
return data[0]
|
||||
return data['up']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -217,9 +223,9 @@ def ipaddr(interface):
|
|||
|
||||
salt '*' network.ipaddr eth0
|
||||
'''
|
||||
data = _interfaces().get(interface)
|
||||
data = interfaces().get(interface)
|
||||
if data:
|
||||
return data[1]
|
||||
return data['ipaddr']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -231,9 +237,9 @@ def netmask(interface):
|
|||
|
||||
salt '*' network.netmask eth0
|
||||
'''
|
||||
data = _interfaces().get(interface)
|
||||
data = interfaces().get(interface)
|
||||
if data:
|
||||
return data[2]
|
||||
return data['netmask']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -245,10 +251,9 @@ def hwaddr(interface):
|
|||
|
||||
salt '*' network.hwaddr eth0
|
||||
'''
|
||||
data = _interfaces().get(interface)
|
||||
data = interfaces().get(interface)
|
||||
if data:
|
||||
return data[3]
|
||||
return data['hwaddr']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
'''
|
||||
Support for nginx
|
||||
'''
|
||||
|
||||
import salt.utils
|
||||
|
||||
__outputter__ = {
|
||||
'signal': 'txt',
|
||||
}
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load the module if nginx is installed
|
||||
'''
|
||||
cmd = __detect_os()
|
||||
if salt.utils.which(cmd):
|
||||
return 'nginx'
|
||||
return False
|
||||
|
||||
def __detect_os():
|
||||
return 'nginx'
|
||||
|
||||
def version():
|
||||
'''
|
||||
Return server version from nginx -v
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' nginx.version
|
||||
'''
|
||||
cmd = __detect_os() + ' -v'
|
||||
out = __salt__['cmd.run'](cmd).split('\n')
|
||||
ret = out[0].split(': ')
|
||||
return ret[2]
|
||||
|
||||
def signal(signal=None):
|
||||
'''
|
||||
Signals httpd to start, restart, or stop.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' nginx.signal reload
|
||||
'''
|
||||
valid_signals = ('reopen', 'stop', 'quit', 'reload')
|
||||
|
||||
if signal not in valid_signals:
|
||||
return
|
||||
|
||||
# Make sure you use the right arguments
|
||||
if signal in valid_signals:
|
||||
arguments = ' -s {0}'.format(signal)
|
||||
else:
|
||||
arguments = ' {0}'.format(signal)
|
||||
cmd = __detect_os() + arguments
|
||||
out = __salt__['cmd.run_all'](cmd)
|
||||
|
||||
# A non-zero return code means fail
|
||||
if out['retcode'] and out['stderr']:
|
||||
ret = out['stderr'].strip()
|
||||
# 'nginxctl configtest' returns 'Syntax OK' to stderr
|
||||
elif out['stderr']:
|
||||
ret = out['stderr'].strip()
|
||||
elif out['stdout']:
|
||||
ret = out['stdout'].strip()
|
||||
# No output for something like: nginxctl graceful
|
||||
else:
|
||||
ret = 'Command: "{0}" completed successfully!'.format(cmd)
|
||||
return ret
|
||||
'''
|
||||
Support for nginx
|
||||
'''
|
||||
|
||||
import salt.utils
|
||||
|
||||
__outputter__ = {
|
||||
'signal': 'txt',
|
||||
}
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load the module if nginx is installed
|
||||
'''
|
||||
cmd = __detect_os()
|
||||
if salt.utils.which(cmd):
|
||||
return 'nginx'
|
||||
return False
|
||||
|
||||
def __detect_os():
|
||||
return 'nginx'
|
||||
|
||||
def version():
|
||||
'''
|
||||
Return server version from nginx -v
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' nginx.version
|
||||
'''
|
||||
cmd = __detect_os() + ' -v'
|
||||
out = __salt__['cmd.run'](cmd).split('\n')
|
||||
ret = out[0].split(': ')
|
||||
return ret[2]
|
||||
|
||||
def signal(signal=None):
|
||||
'''
|
||||
Signals httpd to start, restart, or stop.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' nginx.signal reload
|
||||
'''
|
||||
valid_signals = ('reopen', 'stop', 'quit', 'reload')
|
||||
|
||||
if signal not in valid_signals:
|
||||
return
|
||||
|
||||
# Make sure you use the right arguments
|
||||
if signal in valid_signals:
|
||||
arguments = ' -s {0}'.format(signal)
|
||||
else:
|
||||
arguments = ' {0}'.format(signal)
|
||||
cmd = __detect_os() + arguments
|
||||
out = __salt__['cmd.run_all'](cmd)
|
||||
|
||||
# A non-zero return code means fail
|
||||
if out['retcode'] and out['stderr']:
|
||||
ret = out['stderr'].strip()
|
||||
# 'nginxctl configtest' returns 'Syntax OK' to stderr
|
||||
elif out['stderr']:
|
||||
ret = out['stderr'].strip()
|
||||
elif out['stdout']:
|
||||
ret = out['stdout'].strip()
|
||||
# No output for something like: nginxctl graceful
|
||||
else:
|
||||
ret = 'Command: "{0}" completed successfully!'.format(cmd)
|
||||
return ret
|
||||
|
|
|
@ -224,3 +224,23 @@ def boot_time():
|
|||
salt '*' ps.boot_time
|
||||
'''
|
||||
return psutil.BOOT_TIME
|
||||
|
||||
def network_io_counters():
|
||||
'''
|
||||
Return network I/O statisitics.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' ps.network_io_counters
|
||||
'''
|
||||
return dict(psutil.network_io_counters()._asdict())
|
||||
|
||||
def disk_io_counters():
|
||||
'''
|
||||
Return disk I/O statisitics.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' ps.disk_io_counters
|
||||
'''
|
||||
return dict(psutil.disk_io_counters()._asdict())
|
||||
|
|
|
@ -18,7 +18,7 @@ def _get_socket():
|
|||
return socket
|
||||
|
||||
|
||||
def publish(tgt, fun, arg=None, expr_form='glob', returner=''):
|
||||
def publish(tgt, fun, arg=None, expr_form='glob', returner='', timeout=5):
|
||||
'''
|
||||
Publish a command from the minion out to other minions, publications need
|
||||
to be enabled on the Salt master and the minion needs to have permission
|
||||
|
@ -61,6 +61,7 @@ def publish(tgt, fun, arg=None, expr_form='glob', returner=''):
|
|||
'tgt': tgt,
|
||||
'ret': returner,
|
||||
'tok': tok,
|
||||
'tmo': timeout,
|
||||
'id': __opts__['id']}
|
||||
payload['load'] = auth.crypticle.dumps(load)
|
||||
socket = _get_socket()
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
Execute puppet routines
|
||||
'''
|
||||
|
||||
from salt.exceptions import CommandNotFoundError
|
||||
|
||||
__outputter__ = {
|
||||
'run': 'txt',
|
||||
'noop': 'txt',
|
||||
'fact': 'txt',
|
||||
'facts':None,
|
||||
}
|
||||
|
||||
def _check_puppet():
|
||||
'''
|
||||
|
@ -10,19 +18,106 @@ def _check_puppet():
|
|||
# I thought about making this a virtual module, but then I realized that I
|
||||
# would require the minion to restart if puppet was installed after the
|
||||
# minion was started, and that would be rubbish
|
||||
return __salt__['cmd.has_exec']('puppet')
|
||||
return __salt__['cmd.has_exec']('puppetd')
|
||||
|
||||
|
||||
def run():
|
||||
def _check_facter():
|
||||
'''
|
||||
Execute a puppet run and return a dict with the stderr,stdout,return code
|
||||
etc.
|
||||
Checks if facter is installed
|
||||
'''
|
||||
return __salt__['cmd.has_exec']('facter')
|
||||
|
||||
def _format_fact(output):
|
||||
try:
|
||||
fact, value = output.split(' => ', 1)
|
||||
value = value.strip()
|
||||
except ValueError:
|
||||
fact = None
|
||||
value = None
|
||||
return (fact, value)
|
||||
|
||||
|
||||
def run(tags=None):
|
||||
'''
|
||||
Execute a puppet run and return a dict with the stderr, stdout,
|
||||
return code, etc. If an argument is specified, it is treated as
|
||||
a comma separated list of tags passed to puppetd --test --tags:
|
||||
http://projects.puppetlabs.com/projects/1/wiki/Using_Tags
|
||||
|
||||
CLI Examples::
|
||||
|
||||
salt '*' puppet.run
|
||||
|
||||
salt '*' puppet.run basefiles::edit,apache::server
|
||||
'''
|
||||
if not _check_puppet():
|
||||
raise CommandNotFoundError('puppetd not available')
|
||||
|
||||
if not tags:
|
||||
cmd = 'puppetd --test'
|
||||
else:
|
||||
cmd = 'puppetd --test --tags "{0}"'.format(tags)
|
||||
|
||||
return __salt__['cmd.run_all'](cmd)
|
||||
|
||||
def noop(tags=None):
|
||||
'''
|
||||
Execute a puppet noop run and return a dict with the stderr, stdout,
|
||||
return code, etc. If an argument is specified, it is treated as a
|
||||
comma separated list of tags passed to puppetd --test --noop --tags
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' puppet.run
|
||||
salt '*' puppet.noop
|
||||
|
||||
salt '*' puppet.noop web::server,django::base
|
||||
'''
|
||||
if _check_puppet():
|
||||
return __salt__['cmd.run_all']('puppetd --test')
|
||||
if not _check_puppet():
|
||||
raise CommandNotFoundError('puppetd not available')
|
||||
|
||||
if not tags:
|
||||
cmd = 'puppetd --test --noop'
|
||||
else:
|
||||
return {}
|
||||
cmd = 'puppetd --test --tags "{0}" --noop'.format(tags)
|
||||
|
||||
return __salt__['cmd.run_all'](cmd)
|
||||
|
||||
def facts():
|
||||
'''
|
||||
Run facter and return the results
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' puppet.facts
|
||||
'''
|
||||
if not _check_facter():
|
||||
raise CommandNotFoundError('facter not available')
|
||||
|
||||
ret = {}
|
||||
output = __salt__['cmd.run']('facter')
|
||||
|
||||
# Loop over the facter output and properly
|
||||
# parse it into a nice dictionary for using
|
||||
# elsewhere
|
||||
for line in output.split('\n'):
|
||||
if not line: continue
|
||||
fact, value = _format_fact(line)
|
||||
if not fact:
|
||||
continue
|
||||
ret[fact] = value
|
||||
return ret
|
||||
|
||||
def fact(name):
|
||||
'''
|
||||
Run facter for a specific fact
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' puppet.fact kernel
|
||||
'''
|
||||
if not _check_facter():
|
||||
raise CommandNotFoundError('facter not available')
|
||||
|
||||
ret = __salt__['cmd.run']('facter {0}'.format(name))
|
||||
if not ret:
|
||||
return ''
|
||||
return ret.rstrip()
|
||||
|
|
|
@ -127,10 +127,8 @@ def status(name, sig=None):
|
|||
|
||||
salt '*' service.status <service name> [service signature]
|
||||
'''
|
||||
sig = name if not sig else sig
|
||||
cmd = "{0[ps]} | grep {1} | grep -v grep | awk '{{print $2}}'".format(
|
||||
__grains__, sig)
|
||||
return __salt__['cmd.run'](cmd).strip()
|
||||
cmd = 'service {0} status'.format(name)
|
||||
return not __salt__['cmd.retcode'](cmd)
|
||||
|
||||
|
||||
def enable(name):
|
||||
|
|
|
@ -3,11 +3,16 @@ The Saltutil module is used to manage the state of the salt minion itself. It is
|
|||
used to manage minion modules as well as automate updates to the salt minion
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
import os
|
||||
import hashlib
|
||||
import shutil
|
||||
import signal
|
||||
import logging
|
||||
|
||||
# Import Salt libs
|
||||
import salt.payload
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def _sync(form, env):
|
||||
|
@ -42,7 +47,7 @@ def _sync(form, env):
|
|||
shutil.copyfile(fn_, dest)
|
||||
ret.append('{0}.{1}'.format(form, os.path.basename(fn_)))
|
||||
if ret:
|
||||
open(os.path.join(__opts__['cachedir'], '.module_refresh'), 'w+').write('')
|
||||
open(os.path.join(__opts__['cachedir'], 'module_refresh'), 'w+').write('')
|
||||
if __opts__.get('clean_dynamic_modules', True):
|
||||
current = set(os.listdir(mod_dir))
|
||||
for fn_ in current.difference(remote):
|
||||
|
@ -138,3 +143,94 @@ def sync_all(env='base'):
|
|||
ret.append(sync_renderers(env))
|
||||
ret.append(sync_returners(env))
|
||||
return ret
|
||||
|
||||
|
||||
def running():
|
||||
'''
|
||||
Return the data on all running processes salt on the minion
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' saltutil.running
|
||||
'''
|
||||
procs = __salt__['status.procs']()
|
||||
ret = []
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
pid = os.getpid()
|
||||
proc_dir = os.path.join(__opts__['cachedir'], 'proc')
|
||||
if not os.path.isdir(proc_dir):
|
||||
return []
|
||||
for fn_ in os.listdir(proc_dir):
|
||||
path = os.path.join(proc_dir, fn_)
|
||||
data = serial.loads(open(path, 'rb').read())
|
||||
if not procs.get(str(data['pid'])):
|
||||
# The process is no longer running, clear out the file and
|
||||
# continue
|
||||
os.remove(path)
|
||||
continue
|
||||
if data.get('pid') == pid:
|
||||
continue
|
||||
ret.append(data)
|
||||
return ret
|
||||
|
||||
|
||||
def find_job(jid):
|
||||
'''
|
||||
Return the data for a specific job id
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' saltutil.find_job <job id>
|
||||
'''
|
||||
for data in running():
|
||||
if data['jid'] == jid:
|
||||
return data
|
||||
return {}
|
||||
|
||||
|
||||
def signal_job(jid, sig):
|
||||
'''
|
||||
Sends a signal to the named salt job's process
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' saltutil.signal_job <job id> 15
|
||||
'''
|
||||
for data in running():
|
||||
if data['jid'] == jid:
|
||||
try:
|
||||
os.kill(int(data['pid']), sig)
|
||||
return 'Signal {0} sent to job {1} at pid {2}'.format(
|
||||
int(sig),
|
||||
jid,
|
||||
data['pid']
|
||||
)
|
||||
except OSError:
|
||||
path = os.path.join(__opts__['cachedir'], 'proc', str(jid))
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
return ('Job {0} was not running and job data has been '
|
||||
' cleaned up').format(jid)
|
||||
return ''
|
||||
|
||||
|
||||
def term_job(jid):
|
||||
'''
|
||||
Sends a termination signal (SIGTERM 15) to the named salt job's process
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' saltutil.term_job <job id>
|
||||
'''
|
||||
return signal_job(jid, signal.SIGTERM)
|
||||
|
||||
|
||||
def kill_job(jid):
|
||||
'''
|
||||
Sends a termination signal (SIGTERM 15) to the named salt job's process
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' saltutil.kill_job <job id>
|
||||
'''
|
||||
return signal_job(jid, signal.SIGKILL)
|
||||
|
|
|
@ -25,6 +25,7 @@ def __virtual__():
|
|||
'CentOS',
|
||||
'Fedora',
|
||||
'Gentoo',
|
||||
'FreeBSD',
|
||||
'Windows',
|
||||
]
|
||||
if __grains__['os'] in disable:
|
||||
|
|
|
@ -800,9 +800,7 @@ def backup(host=None, core_name=None, append_core_to_path=False):
|
|||
salt '*' solr.backup music
|
||||
'''
|
||||
path = __opts__['solr.backup_path']
|
||||
print path
|
||||
numBackups = __opts__['solr.num_backups']
|
||||
print numBackups
|
||||
if path is not None:
|
||||
if not path.endswith(os.path.sep):
|
||||
path += os.path.sep
|
||||
|
@ -1027,7 +1025,6 @@ def core_status(host=None, core_name=None):
|
|||
return ret
|
||||
extra = ['action=STATUS', 'core={0}'.format(core_name)]
|
||||
url = _format_url('admin/cores', host=host, core_name=None, extra=extra)
|
||||
print url
|
||||
return _http_request(url)
|
||||
|
||||
################### DIH (Direct Import Handler) COMMANDS #####################
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue