Merge branch '0.9.7'

This commit is contained in:
Thomas S Hatch 2012-02-15 10:21:19 -07:00
commit 1d1ab88d03
133 changed files with 5536 additions and 2365 deletions

View file

@ -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'

View file

@ -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
View file

@ -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
View file

@ -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
View file

@ -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
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,2 +0,0 @@
usr/share/salt/salt-minion /usr/bin/salt-minion
usr/share/salt/salt-call /usr/bin/salt-call

View file

@ -1 +1 @@
scripts/salt-syndic /usr/share/salt
scripts/salt-syndic /usr/bin

View file

@ -1 +1 @@
usr/share/salt/salt-syndic /usr/bin/salt-syndic
usr/lib/python2*/dist-packages/salt/salt-syndic /usr/bin/salt-syndic

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -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 %}

View file

@ -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>

View file

@ -1,6 +1,6 @@
{% extends "!layout.html" %}
{% block rootrellink %}
<li><a href="{{ pathto('index') }}">Salt home</a>&nbsp;|&nbsp;</li>
<li><a href="{{ pathto('home') }}">Documentation</a> &raquo;</li>
{% endblock %}
{%- block rootrellink %}
<li><a href="http://saltstack.org">&laquo; SaltStack.org</a>&nbsp;|&nbsp;</li>
<li><a href="{{ pathto('index') }}">Documentation home</a></li>
{%- endblock %}

View file

@ -1,186 +0,0 @@
{%- block doctype -%}
<!DOCTYPE html>
{%- endblock %}
{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' 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 = " &mdash; "|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>&nbsp;|&nbsp;</li>
<li><a href="{{ pathto('home') }}">Documentation</a> &raquo;</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 %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
{%- else %}
{% trans copyright=copyright|e %}&copy; 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>

View file

@ -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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -1,3 +0,0 @@
[theme]
inherit = default
stylesheet = base-salt.css

View file

@ -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/'

View file

@ -6,10 +6,7 @@ Full Table of Contents
:maxdepth: 3
:glob:
home
topics/index
topics/installation
topics/configuration
topics/tutorials/modules
topics/tutorials/states*

View file

@ -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
View 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

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

View file

@ -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.
.\"
.

File diff suppressed because it is too large Load diff

View file

@ -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
````````````````````

View file

@ -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

View file

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

View file

@ -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

View file

@ -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``

View file

@ -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
------------------

View file

@ -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.

View file

@ -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

View 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`

View 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.

View file

@ -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
-----------------

View file

@ -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`

View file

@ -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>

View file

@ -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

View file

@ -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
View 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

View file

@ -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.

View file

@ -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
View 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.

View file

@ -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
------------

View file

@ -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
------------

View file

@ -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
------------

View file

@ -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
------------

View file

@ -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
============

View 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.

View 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.

View 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.

View file

@ -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?

View file

@ -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

View file

@ -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.

View file

@ -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
View file

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

View file

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

View file

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

View file

@ -19,6 +19,22 @@ except ImportError as e:
if e.message != 'No module named _msgpack':
raise
def set_pidfile(pidfile):
'''
Save the pidfile
'''
pdir = os.path.dirname(pidfile)
if not os.path.isdir(pdir):
os.makedirs(pdir)
try:
open(pidfile, 'w+').write(str(os.getpid()))
except IOError:
err = ('Failed to commit the pid file to location {0}, please verify'
' that the location is available').format(pidfile)
log.error(err)
def verify_env(dirs):
'''
Verify that the named directories are in place and that the environment
@ -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')

View file

@ -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()

View file

@ -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

View file

@ -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()

View file

@ -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())

View file

@ -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

View file

@ -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'])

View file

@ -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

View file

@ -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__'):

View file

@ -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

View file

@ -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()))

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -12,7 +12,6 @@ def echo(text):
CLI Example:
salt '*' test.echo 'foo bar baz quo qux'
'''
print 'Echo got called!'
return text

View file

@ -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

View file

@ -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)

View 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()

View file

@ -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
View 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)

View file

@ -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
View 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

View file

@ -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

View file

@ -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

View file

@ -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())

View file

@ -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()

View file

@ -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()

View file

@ -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):

View file

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

View file

@ -25,6 +25,7 @@ def __virtual__():
'CentOS',
'Fedora',
'Gentoo',
'FreeBSD',
'Windows',
]
if __grains__['os'] in disable:

View file

@ -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