Fix pylint (again)

This commit is contained in:
C. R. Oldham 2014-06-18 15:38:11 -06:00
commit 48b498bb2f
59 changed files with 2758 additions and 568 deletions

View File

@ -1,4 +1,4 @@
Salt - Remote execution system
Salt - Remote execution system
Copyright 2014 SaltStack Team

View File

@ -2,7 +2,9 @@ include AUTHORS
include HACKING.rst
include LICENSE
include README.rst
include requirements.txt
include _requirements.txt
include raet-requirements.txt
include zeromq-requirements.txt
include tests/*.py
recursive-include tests *
include tests/integration/modules/files/*

6
_requirements.txt Normal file
View File

@ -0,0 +1,6 @@
Jinja2
msgpack-python > 0.1.13
PyYAML
MarkupSafe
apache-libcloud >= 0.14.0
requests

View File

@ -160,7 +160,7 @@
# Works like autosign_file, but instead allows you to specify minion IDs for
# which keys will automatically be rejected. Will override both membership in
# the autosign_file and the auto_accept setting.
#autoreject_file: /etc/salt/autosign.conf
#autoreject_file: /etc/salt/autoreject.conf
# Enable permissive access to the salt keys. This allows you to run the
# master or minion as root, but have a non-root group be given access to

View File

@ -92,7 +92,6 @@
{% endif %}
<link rel="stylesheet" href="{{ pathto('_static/basic.css', 1) }}">
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}">
<link rel="stylesheet" href="{{ pathto('_static/css/bootstrap.css', 1) }}">
<style>
body { padding-top: 20px; }
@ -118,6 +117,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="google-site-verification" content="1Y-ojT3ndjxA9coB77iUDyXPWxeuQ3T4_r0j-QG6QHg" />
<link href='http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,600,700,800,300' rel='stylesheet' type='text/css'>
{{ css() }}
{%- if not embedded %}
@ -159,7 +159,6 @@
{%- endblock %}
{%- block extrahead %} {% endblock %}
<link href='http://fonts.googleapis.com/css?family=Open+Sans:300italic,800italic,300,800' rel='stylesheet' type='text/css'>
<script src="{{ pathto('_static/js/vendor/modernizr-2.6.2-respond-1.1.0.min.js', 1) }}"></script>
{%- block analytics %}
@ -262,7 +261,7 @@
<script src="{{ pathto('_static/js/main.js', 1) }}"></script>
{% if on_saltstack %}
<script type="text/javascript" language="javascript">llactid=23943</script>
<script type="text/javascript" language="javascript">llactid=23943</script>
<script type="text/javascript" language="javascript" src="http://t6.trackalyzer.com/trackalyze.js"></script>
<script>

View File

@ -1,321 +1,347 @@
/* ==========================================================================
Author's custom styles
========================================================================== */
Author's custom styles
========================================================================== */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {
-webkit-transition: all 0.2s ease;
-moz-transition: all 0.2s ease;
-ms-transition: all 0.2s ease;
-o-transition: all 0.2s ease;
transition: all 0.2s ease;
-webkit-transition: all 0.2s ease;
-moz-transition: all 0.2s ease;
-ms-transition: all 0.2s ease;
-o-transition: all 0.2s ease;
transition: all 0.2s ease;
}
.navbar .nav {
float:right;
margin: 0;
padding-top: 18px;
body {
font-family: 'Open Sans', sans-serif;
}
.navbar .nav {
float:right;
margin: 0;
padding-top: 18px;
}
.navbar-inverse .brand, .navbar-inverse .nav > li > a {
color: #484c51;
font: 14px/24px 'Open Sans Light';
color: #484c51;
font: 14px/24px 'Open Sans';
font-weight: 200;
}
.navbar .nav li.currentNav {
background: url(../img/navCurrentArrow.png) center 32px no-repeat;
background: url(../img/navCurrentArrow.png) center 32px no-repeat;
}
.hero-unit {
margin-bottom: 0;
margin-bottom: 0;
}
.shaded {
background: #f7f9f8;
background: #f7f9f8;
}
.shaded img {
margin: 8px 12px;
margin: 8px 12px;
}
.articleCredits {
padding-left: 12px;
font: 12px/24px 'Open Sans Light';
white-space: pre;
padding-left: 12px;
font: 12px/24px 'Open Sans';
font-weight: 100;
white-space: pre;
}
.fullwidth {
padding: 30px;
padding: 30px;
}
body.contact .shaded img {
margin: 0;
body.contact .shaded img {
margin: 0;
}
.map img {
margin-top: 30px;
float: left;
margin-top: 30px;
float: left;
}
body.index .success, body.index .clients {
margin-top: 0;
margin-top: 0;
}
.success h2 {
margin-top: 25px;
margin-top: 25px;
}
.testimonialAuthor {
display: block;
margin-bottom: 30px;
font: 12px/24px 'Open Sans Extrabold';
color: #aa2b39;
text-transform: uppercase;
display: block;
margin-bottom: 30px;
font: 12px/24px 'Open Sans';
color: #aa2b39;
font-weight: 800;
text-transform: uppercase;
}
.success hr, .events hr, .productnews hr {
margin-top: 0;
margin-top: 0;
}
.clients {
background-color: #415a72;
background-color: #415a72;
}
.clients img {
margin: 15px 21px;
margin: 15px 21px;
}
.news {
margin-bottom: 40px;
margin-bottom: 40px;
}
h2.homeSecTitles {
color: #8d9caa;
font-family: 'Open Sans Extrabold Italic';
text-transform: capitalize;
margin-bottom: 0;
color: #8d9caa;
font-style: italic;
font-family: 'Open Sans';
font-weight: 800;
text-transform: capitalize;
margin-bottom: 0;
}
.success h3 {
font: 18px/30px 'Open Sans Light Italic'; color: #4f575b; margin: 25px 0;
font-weight: 100;
font-style: italic;
font: 18px/30px 'Open Sans';
color: #4f575b; margin: 25px 0;
}
.success img {
margin: 30px 10px 10px 80px;
margin: 30px 10px 10px 80px;
}
.about img {
margin-bottom: 21px;
margin-bottom: 21px;
}
.carousel-inner {
max-height: 387px;
.carousel-inner {
max-height: 387px;
}
.carousel-control {
opacity: 1;
background: none;
border: none;
border-radius: 0;
opacity: 1;
background: none;
border: none;
border-radius: 0;
}
.carousel-caption h1 {
text-transform:uppercase;
margin-top: 20px;
display:none;
text-transform:uppercase;
margin-top: 20px;
display:none;
}
.carousel-caption p {
margin-top: 25px;
font: 16px/24px 'Open Sans Light';
color: #000;
display: none;
margin-top: 25px;
font-weight: 100;
font: 16px/24px 'Open Sans';
color: #000;
display: none;
}
.carousel-caption img {
margin-top: 10px;
margin-top: 10px;
}
#myCarousel img {
width: 100%;
height: auto;
width: 100%;
height: auto;
}
#myCarousel .carousel-control img {
width: auto;
width: auto;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Open Sans Light';
font-weight: 100;
font-family: 'Open Sans';
font-weight: normal;
}
h2 {
font: 18px/28px 'Open Sans Extrabold';
text-transform: uppercase;
color: #373e4b;
margin-bottom: 0;
font: 18px/28px 'Open Sans';
text-transform: uppercase;
font-weight: 800;
color: #373e4b;
margin-bottom: 0;
}
h3 {
font-size: 16px; line-height:26px;
font-size: 16px; line-height:26px;
}
p {
font-size: 14px; line-height: 24px; color: #313131;
font-size: 14px; line-height: 24px; color: #313131;
}
.lede {
text-align: center;
margin-bottom:20px;
max-width: 80%;
margin-left: 10%;
text-align: center;
margin-bottom:20px;
max-width: 80%;
margin-left: 10%;
}
body.services .lede {
max-width: 95%;
margin-left: 2.5%;
margin-top: 30px;
max-width: 95%;
margin-left: 2.5%;
margin-top: 30px;
}
body.products .lede {
margin-top: 30px;
margin-top: 30px;
}
.lede h3 {
font: 24px/30px 'Open Sans Light';
color: #aa2b39;
font-weight: 100;
font: 24px/30px 'Open Sans';
color: #aa2b39;
}
.lede h4 {
font: 20px/30px 'Open Sans Light Italic';
color: #373e4b;
font-weight: 100;
font-style: italic;
font: 20px/30px 'Open Sans';
color: #373e4b;
}
body.services .lede h4 {
font-size: 16px;
line-height: 150%;
font-size: 16px;
line-height: 150%;
}
body.services .lede {
margin-bottom: 30px;
margin-bottom: 30px;
}
body.about h1, body.services h1, body.contact h1 {
font: 20px/30px 'Open Sans Extrabold Italic';
color: #4f575b;
margin-top: 0;
font: 20px/30px 'Open Sans';
font-style: italic;
font-weight: 800;
color: #4f575b;
margin-top: 0;
}
.row-fluid {
margin-top: 50px;
margin-top: 50px;
}
body.about h6 {
margin: 0;
font-family: 'Open Sans Light Italic';
text-transform: uppercase;
line-height: 80%;
margin-bottom: 10px;
color: #AA2B39;
margin: 0;
font-weight: 100;
font-style: italic;
font-family: 'Open Sans';
text-transform: uppercase;
line-height: 80%;
margin-bottom: 10px;
color: #AA2B39;
}
body.services table {
margin: 0;
padding: 0;
table-layout: fixed;
body.services table {
margin: 0;
padding: 0;
table-layout: fixed;
}
body.services table th, body.services table td {
padding: 10px 0;
text-align: left;
padding: 10px 0;
text-align: left;
}
body.services table td {
color: #373e4b;
font-family: 'Open Sans Light Italic';
width: 206px;
body.services table td {
color: #373e4b;
font-style: italic;
font-weight: 100;
font-family: 'Open Sans';
width: 206px;
}
body.services table tr:last-child th, body.services table tr:last-child td {
border-bottom: none;
body.services table tr:last-child th, body.services table tr:last-child td {
border-bottom: none;
}
body.services table tr:nth-child(even) {
background: #eff3f1;
}
body.services table td:first-child {
padding-left: 30px;
body.services table tr:nth-child(even) {
background: #eff3f1;
}
body.services table td:first-child {
padding-left: 30px;
}
body.services table td.tableCenter {
text-align: center;
text-align: center;
}
.singleProduct ul {
margin-left: 15px;
margin-left: 15px;
}
.singleProduct li {
padding: 5px 0;
padding: 5px 0;
}
.btn-red {
display:block;
padding: 5px 0;
background: #aa2b39;
color: #FFF;
font-family: 'Open Sans Light';
text-shadow: none;
font-weight: normal;
border: none;
border-radius: 0;
box-shadow: none;
width: 100px;
margin-left: 75px;
display:block;
padding: 5px 0;
background: #aa2b39;
color: #FFF;
font-style: italic;
font-weight: 100;
font-family: 'Open Sans';
text-shadow: none;
border: none;
border-radius: 0;
box-shadow: none;
width: 100px;
margin-left: 75px;
}
.btn-red:hover, .bth-red:focus {
background-color: #9B1C2E;
color: white;
background-color: #9B1C2E;
color: white;
}
body.contact .btn-red {
margin-left: 0; margin-top: 5px;
margin-left: 0; margin-top: 5px;
}
#myCarousel .btn-red {
margin-left: 12px; margin-top:-7px;
margin-left: 12px; margin-top:-7px;
}
.map {
border: 0; border-right: 1px solid #eee;
.map {
border: 0; border-right: 1px solid #eee;
}
.map h2, .connect h2 {
margin: 0;
margin: 0;
}
.map img {
padding-right: 30px;
padding-right: 30px;
}
.connect a {
margin: 0 8px 0 0; display: block; float: left;
margin: 0 8px 0 0; display: block; float: left;
}
.connect a img:hover {
margin-top: -5px;
-webkit-transition: margin 0.2s ease-out;
-moz-transition: margin 0.2s ease-out;
-o-transition: margin 0.2s ease-out;
transition: margin 0.2s ease-out;
margin-top: -5px;
-webkit-transition: margin 0.2s ease-out;
-moz-transition: margin 0.2s ease-out;
-o-transition: margin 0.2s ease-out;
transition: margin 0.2s ease-out;
}
footer {
background: url(../images/footerBG.jpg) 50% 0 no-repeat;
min-height: 340px;
margin-top: 50px;
position:relative;
-webkit-background-size: cover; /*for webKit*/
-moz-background-size: cover; /*Mozilla*/
-o-background-size: cover; /*opera*/
background-size: cover; /*generic*/
background: url(../images/footerBG.jpg) 50% 0 no-repeat;
min-height: 340px;
margin-top: 50px;
position:relative;
-webkit-background-size: cover; /*for webKit*/
-moz-background-size: cover; /*Mozilla*/
-o-background-size: cover; /*opera*/
background-size: cover; /*generic*/
}
footer p, footer a {
color: #FFF;
font: 12px 'Open Sans Light';
color: #FFF;
font-weight: 100;
font: 12px 'Open Sans';
}
footer a {
display:block;
float: left;
margin: 0 0 6px 0;
clear: left;
display:block;
float: left;
margin: 0 0 6px 0;
clear: left;
}
footer .row-fluid {
margin-top: 30px;
margin-top: 30px;
}
.footerCol {
width: 20%;
float:left;
width: 20%;
float:left;
}
.footerCol h4 {
color: #FFF;
font: 14px/24px 'Open Sans Extrabold';
text-transform: uppercase;
margin-bottom: 0;
color: #FFF;
font-weight: 800;
font: 14px/24px 'Open Sans';
text-transform: uppercase;
margin-bottom: 0;
}
footer .social a {
clear: none;
margin: 0 4px;
clear: none;
margin: 0 4px;
}
footer .social img:hover {
margin-top: -5px;
-webkit-transition: margin 0.2s ease-out;
-moz-transition: margin 0.2s ease-out;
-o-transition: margin 0.2s ease-out;
transition: margin 0.2s ease-out;
margin-top: -5px;
-webkit-transition: margin 0.2s ease-out;
-moz-transition: margin 0.2s ease-out;
-o-transition: margin 0.2s ease-out;
transition: margin 0.2s ease-out;
}
body.contact footer, body.index footer {
margin-top: 0;
margin-top: 0;
}
.news img {
max-width: 275px;
height: auto;
max-width: 275px;
height: auto;
}
.news h2, .news p, .events hr {
max-width: 275px;
max-width: 275px;
}
.productnews hr {
max-width: 595px;
max-width: 595px;
}
/* ==========================================================================
Sphinx custom styles
========================================================================== */
Sphinx custom styles
========================================================================== */
h1:hover > .headerlink,
h2:hover > .headerlink,
@ -326,53 +352,50 @@ h6:hover > .headerlink,
dt:hover > .headerlink { visibility: visible; }
.sidebar .toctree-l1.current a {
border-right: 5px solid #fcaf3e; }
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;
color: #fcaf3e;
letter-spacing: .1em;
text-transform: uppercase;
}
.descname {
font-weight: bold; }
font-weight: bold;
}
.literal {
background-color: #eeeeec; }
background-color: #eeeeec;
}
blockquote {
margin: 1em; }
margin: 1em;
}
.footer, .footer a {
color: #888a85; }
color: #888a85;
}
div.admonition {
font-size: 0.9em;
margin: 1em 0 1em 0;
padding: 0.5em 1em 0.5em 1em;
border: 1px solid #ddd; }
border: 1px solid #ddd;
}
div.admonition p.admonition-title {
font-weight: bold; color: #3465a4; }
font-weight: bold; color: #3465a4;
}
div.warning {
border-color: #940000; }
border-color: #940000;
}
div.warning p.admonition-title {
color: #940000; }
color: #940000;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
@ -380,8 +403,8 @@ div.viewcode-block:target {
}
/* ==========================================================================
SaltStack custom styles
========================================================================== */
SaltStack custom styles
========================================================================== */
.lit-docs {
list-style-type: none;
@ -407,6 +430,7 @@ div.viewcode-block:target {
}
/* Override a few Bootstrap-isms */
.lit-annotation pre,
.lit-content pre {
word-break: normal;
@ -441,8 +465,9 @@ div.viewcode-block:target {
}
/* rST automatically puts the .container class on ``.. container`` directives
* which conflicts with the bootstrap class of the same name. >.<
*/
* which conflicts with the bootstrap class of the same name. >.<
*/
.scrollable.container {
width: auto;
}

View File

@ -122,7 +122,7 @@ copyright = '2014 SaltStack, Inc.'
version = salt.version.__version__
#release = '.'.join(map(str, salt.version.__version_info__))
release = '2014.1.4'
release = '2014.1.5'
language = 'en'
locale_dirs = [

View File

@ -109,23 +109,30 @@ arbitrary commands on remote hosts.
Targeting
---------
:doc:`Targeting </topics/targeting/index>` is specifying which minions
should execute commands or manage server configuration.
:ref:`Targeting <targeting>` is the method of specifying which minions should
execute commands or manage server configuration.
:doc:`Globbing and regex </topics/targeting/globbing>`
:ref:`Globbing and regex <targeting-glob>`
Match minions using globbing and regular expressions.
:doc:`Grains </topics/targeting/grains>`
:ref:`Grains <targeting-grains>`
Match minions using bits of static information about the minion such as
OS, software versions, virtualization, CPU, memory, and much more.
:doc:`Node groups </topics/targeting/nodegroups>`
Statically define groups of minions.
:ref:`Pillar <targeting-pillar>`
Match minions using user-defined variables.
:doc:`Compound matchers </topics/targeting/compound>`
Combine the above matchers as a single target.
:ref:`Subnet/IP Address <targeting-ipcidr>`
Match minions by Subnet or IP address (currently IPv4 only).
:doc:`Batching execution </topics/targeting/batch>`
:ref:`Compound matching <targeting-compound>`
Combine any of the above matchers into a single expression.
:ref:`Node groups <targeting-nodegroups>`
Statically define groups of minions in the master config file using the
:ref:`compound <targeting-compound>` matching syntax.
:ref:`Batching execution <targeting-batch>`
Loop through all matching minions so that only a subset are executing a
command at one time.

View File

@ -15,6 +15,7 @@ Full list of builtin pillar modules
cobbler
django_orm
etcd_pillar
foreman
git_pillar
hiera
libvirt

View File

@ -0,0 +1,6 @@
===================
salt.pillar.foreman
===================
.. automodule:: salt.pillar.foreman
:members:

View File

@ -5,6 +5,14 @@ Troubleshooting Salt Cloud
This page describes various steps for troubleshooting problems that may arise
while using Salt Cloud.
Virtual Machines Are Created, But Do Not Respond
================================================
Are TCP ports 4505 and 4506 open on the master? This is easy to overlook on new
masters. Information on how to open firewall ports on various platforms can be
found :doc:`here </topics/tutorials/firewall>`.
Generic Troubleshooting Steps
=============================
This section describes a set of instructions that are useful to a large number

View File

@ -44,21 +44,46 @@ Dependencies
Salt should run on any Unix-like platform so long as the dependencies are met.
* `Python 2.6`_ >= 2.6 <3.0
* `ZeroMQ`_ >= 3.2.0
* `pyzmq`_ >= 2.2.0 - ZeroMQ Python bindings
* `PyCrypto`_ - The Python cryptography toolkit
* `M2Crypto`_ - "Me Too Crypto" - Python OpenSSL wrapper
* `msgpack-python`_ - High-performance message interchange format
* `YAML`_ - Python YAML bindings
* `Jinja2`_ - parsing Salt States (configurable in the master settings)
* `MarkupSafe`_ - Implements a XML/HTML/XHTML Markup safe string for Python
* `apache-libcloud`_ - Python lib for interacting with many of the popular
cloud service providers using a unified API
The upcoming feature release will include a new dependency:
* `Requests`_ - HTTP library
Depending on the chosen Salt transport, `ZeroMQ`_ or `RAET`_, dependencies
vary:
* ZeroMQ:
* `ZeroMQ`_ >= 3.2.0
* `pyzmq`_ >= 2.2.0 - ZeroMQ Python bindings
* `PyCrypto`_ - The Python cryptography toolkit
* `M2Crypto`_ - "Me Too Crypto" - Python OpenSSL wrapper
* RAET:
* `libnacl`_ - Python bindings to `libsodium`_
* `ioflo`_ - The flo programming interface raet and salt-raet is built on
* `RAET`_ - The worlds most awesome UDP protocol
Salt defaults to the `ZeroMQ`_ transport, and the choice can be made at install
time, for example:
.. code-block:: console
python setup.py --salt-transport=raet install
This way, only the required dependencies are pulled by the setup script if need
be.
If installing using pip, the ``--salt-transport`` global option can be provided
like:
.. code-block:: console
pip install --global-option="--salt-transport=raet" salt
Optional Dependencies
---------------------
@ -79,6 +104,10 @@ Optional Dependencies
.. _`Cython`: http://cython.org/
.. _`apache-libcloud`: http://libcloud.apache.org
.. _`Requests`: http://docs.python-requests.org/en/latest
.. _`libnacl`: https://github.com/saltstack/libnacl
.. _`ioflo`: https://github.com/ioflo/ioflo
.. _`RAET`: https://github.com/saltstack/raet
.. _`libsodium`: https://github.com/jedisct1/libsodium
Upgrading Salt

View File

@ -236,6 +236,8 @@ locally. This is done with the ``saltutil.refresh_pillar`` function.
This function triggers the minion to asynchronously refresh the pillar and will
always return ``None``.
.. _targeting-pillar:
Targeting with Pillar
=====================

View File

@ -0,0 +1,48 @@
===========================
Salt 2014.1.5 Release Notes
===========================
:release: 2014-06-11
Version 2014.1.5 is another bugfix release for :doc:`2014.1.0
</topics/releases/2014.1.0>`. Changes include:
- Add function for finding cached job on the minion
- Fix iptables save file location for Debian (:issue:`11730`)
- Fix for minion caching jobs when master is down
- Bump default ``syndic_wait`` to 5 to fix syndic-related problems
(:issue:`12262`)
- Add OpenBSD, FreeBSD, and NetBSD support for ``network.netstat``
(:issue:`12121`)
- Fix false positive error in logs for ``makeconf`` state (:issue:`9762`)
- Fix for yum ``fromrepo`` package installs when repo is disabled by default
(:issue:`12466`)
- Fix for extra blank lines in ``file.blockreplace`` (:issue:`12422`)
- Fix grain detection for OpenVZ guests (:issue:`11877`)
- Fix ``get_dns_servers`` function for Windows ``win_dns_client``
- Use system locale for ports package installations
- Use correct stop/restart procedure for Debian networking in ``debian_ip``
(:issue:`12614`)
- Fix for ``cmd_iter``/``cmd_iter_no_block`` blocking issues (:issue:`12617`)
- Fix traceback when syncing custom types (:issue:`12883`)
- Fix cleaning directory symlinks in ``file.directory``
- Add performance optimizations for ``saltutil.sync_all`` and
``state.highstate``
- Fix possible error in ``saltutil.running``
- Fix for kmod modules with dashes (:issue:`13239`)
- Fix possible race condition for Windows minions in state module reloading
(:issue:`12370`)
- Fix bug with roster for ``passwd``s that are loaded as non-string objects
(:issue:`13249`)
- Keep duplicate version numbers from showing up in ``pkg.list_pkgs`` output
- Fixes for Jinja renderer, timezone :mod:`module
<salt.modules.timezone>`/:mod:`state <salt.states.timezone>` (:issue:`12724`)
- Fix timedatectl parsing for systemd>=210 (:issue:`12728`)
- Fix ``saltenv`` being written to YUM repo config files (:issue:`12887`)
- Removed the deprecated external nodes classifier (originally accessible by
setting a value for external_nodes in the master configuration file). Note
that this functionality has been marked deprecated for some time and was
replaced by the more general :doc:`master tops <topics/master_tops>` system.
- More robust escaping of ldap filter strings.
- Fix trailing slash in :conf_master:`gitfs_root` causing files not to be
available (:issue:`13185`)

95
doc/topics/sdb/index.rst Normal file
View File

@ -0,0 +1,95 @@
.. _sdb:
===============================
Storing Data in Other Databases
===============================
The SDB interface is designed to store and retrieve data that, unlike pillars
and grains, is not necessarily minion-specific. The initial design goal was to
allow passwords to be stored in a secure database, such as one managed by the
keyring package, rather than as plain-text files. However, as a generic database
interface, it could conceptually be used for a number of other purposes.
SDB was added to Salt in version Helium. SDB is currently experimental, and
should probably not be used in production.
SDB Configuration
================
In order to use the SDB interface, a configuration profile must be set up in
either the master or minion configuration file. The configuration stanza
includes the name/ID that the profile will be referred to as, a ``driver``
setting, and any other arguments that are necessary for the SDB module that will
be used. For instance, a profile called ``mykeyring``, which uses the
``system`` service in the ``keyring`` module would look like:
.. code-block:: yaml
mykeyring:
driver: keyring
service: system
It is recommended to keep the name of the profile simple, as it is used in the
SDB URI as well.
SDB URIs
========
SDB is designed to make small database queries (hence the name, SDB) using a
compact URL. This allows users to reference a database value quickly inside
a number of Salt configuration areas, without a lot of overhead. The basic
format of an SDB URI is:
.. code-block:: yaml
sdb://<profile>/<args>
The profile refers to the configuration profile defined in either the master or
the minion configuration file. The args are specific to the module referred to
in the profile, but will typically only need to refer to the key of a
key/value pair inside the database. This is because the profile itself should
define as many other parameters as possible.
For example, a profile might be set up to reference credentials for a specific
OpenStack account. The profile might look like:
.. code-block:: yaml
kevinopenstack:
driver: keyring
service salt.cloud.openstack.kevin
And the URI used to reference the password might look like:
.. code-block:: yaml
sdb://kevinopenstack/password
Writing SDB Modules
===================
There is currently one function that MUST exist in any SDB module (``get()``)
and one that MAY exist (``set_()``). If using a (``set_()``) function, a
``__func_alias__`` dictionary MUST be declared in the module as well:
.. code-block:: python
__func_alias__ = {
'set_': 'set',
}
This is because ``set`` is a Python built-in, and therefore functions should not
be created which are called ``set()``. The ``__func_alias__`` functionality is
provided via Salt's loader interfaces, and allows legally-named functions to be
referred to using names that would otherwise be unwise to use.
The ``get()`` function is required, as it will be called via functions in other
areas of the code which make use of the ``sdb://`` URI. For example, the
``config.get`` function in the ``config`` execution module uses this function.
The ``set_()`` function may be provided, but is not required, as some sources
may be read-only, or may be otherwise unwise to access via a URI (for instance,
because of SQL injection attacks).
A simple example of an SDB module is ``salt/sdb/keyring_db.py``, as it provides
basic examples of most, if not all, of the types of functionality that are
available not only for SDB modules, but for Salt modules in general.

View File

@ -1,3 +1,5 @@
.. _targeting-batch:
Batch Size
----------

View File

@ -28,6 +28,7 @@ There are many ways to target individual minions or groups of minions in Salt:
globbing
grains
nodegroups
ipcidr
compound
nodegroups
batch

View File

@ -0,0 +1,19 @@
.. _targeting-ipcidr:
==========================
Subnet/IP Address Matching
==========================
Minions can easily be matched based on IP address, or by subnet (using CIDR_
notation).
.. code-block:: bash
salt -S 192.168.40.20 test.ping
salt -S 10.0.0.0/24 test.ping
.. _CIDR: http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
.. note::
Only IPv4 matching is supported at this time.

View File

@ -90,11 +90,10 @@ master:
gitfs_remotes:
- https://github.com/saltstack-formulas/salt-formula.git
3. *Restart the master* so that the git repository cache on the master
is updated, and
new ``salt://`` requests will send the latest files from
the remote git repository.
This step is not necessary with a standalone minion configuration.
3. Restart the master so that the git repository cache on the master is
updated, and new ``salt://`` requests will send the latest files from the
remote git repository. This step is not necessary with a standalone minion
configuration.
.. note::

5
raet-requirements.txt Normal file
View File

@ -0,0 +1,5 @@
-r _requirements.txt
libnacl
ioflo
raet

View File

@ -1,9 +0,0 @@
Jinja2
M2Crypto
msgpack-python > 0.1.13
pycrypto
PyYAML
pyzmq >= 2.2.0
MarkupSafe
apache-libcloud >= 0.14.0
requests

View File

@ -52,10 +52,12 @@ class LocalClient(salt.client.LocalClient):
timeout=timeout,
**kwargs)
yid = salt.utils.gen_jid()
basedirpath = os.path.join(self.opts['cachedir'], 'raet')
stack = LaneStack(
name=('client' + yid),
yid=yid,
lanename='master',
basedirpath=basedirpath,
sockdirpath=self.opts['sock_dir'])
stack.Pk = raeting.packKinds.pack
router_yard = RemoteYard(

View File

@ -281,6 +281,12 @@ def create(vm_):
)
)
if key_filename is None:
raise SaltCloudConfigError(
'The Digital Ocean driver requires a ssh_key_file because it does not supply a root password '
'upon building the server'
)
private_networking = config.get_cloud_config_value(
'private_networking', vm_, __opts__, search_global=False, default=None,
)

View File

@ -1438,6 +1438,7 @@ def apply_cloud_providers_config(overrides, defaults=None):
break
providers = {}
ext_count = 0
for key, val in config.items():
if key in ('conf_file', 'include', 'default_include', 'user'):
continue
@ -1473,21 +1474,14 @@ def apply_cloud_providers_config(overrides, defaults=None):
for entry in val:
if 'provider' not in entry:
entry['provider'] = '-only-extendable-'
entry['provider'] = '-only-extendable-{0}'.format(ext_count)
ext_count += 1
if key not in providers:
providers[key] = {}
provider = entry['provider']
if provider in providers[key] and provider == '-only-extendable-':
raise salt.cloud.exceptions.SaltCloudConfigError(
'There\'s multiple entries under {0!r} which do not set '
'a provider setting. This is most likely just a holder '
'for data to be extended from, however, there can be '
'only one entry which does not define it\'s \'provider\' '
'setting.'.format(key)
)
elif provider not in providers[key]:
if provider not in providers[key]:
providers[key][provider] = entry
# Is any provider extending data!?
@ -1530,6 +1524,8 @@ def apply_cloud_providers_config(overrides, defaults=None):
)
)
details['extends'] = '{0}:{1}'.format(alias, provider)
# # change provider details '-only-extendable-' to extended provider name
details['provider'] = provider
elif providers.get(extends) and len(providers[extends]) > 1:
raise salt.cloud.exceptions.SaltCloudConfigError(
'The {0!r} cloud provider entry in {1!r} is trying '
@ -1549,12 +1545,11 @@ def apply_cloud_providers_config(overrides, defaults=None):
)
)
else:
provider = providers.get(extends)
if driver in providers.get(extends):
details['extends'] = '{0}:{1}'.format(extends, driver)
elif '-only-extendable-' in providers.get(extends):
elif providers.get(extends).startswith('-only-extendable-'):
details['extends'] = '{0}:{1}'.format(
extends, '-only-extendable-'
extends, '-only-extendable-{0}'.format(ext_count)
)
else:
# We're still not aware of what we're trying to extend
@ -1590,6 +1585,11 @@ def apply_cloud_providers_config(overrides, defaults=None):
extended.update(details)
# Update the providers dictionary with the merged data
providers[alias][driver] = extended
# Update name of the driver, now that it's populated with extended information
if driver.startswith('-only-extendable-'):
providers[alias][ext_driver] = providers[alias][driver]
# Delete driver with old name to maintain dictionary size
del providers[alias][driver]
if not keep_looping:
break
@ -1598,7 +1598,7 @@ def apply_cloud_providers_config(overrides, defaults=None):
# extend from
for provider_alias, entries in providers.copy().items():
for driver, details in entries.copy().iteritems():
if driver != '-only-extendable-':
if not driver.startswith('-only-extendable-'):
continue
log.info(

View File

@ -111,11 +111,12 @@ class SaltRaetRoadStack(ioflo.base.deeding.Deed):
localname=localname,
auto=auto,
main=main,
dirpath=dirpath,
basedirpath=dirpath,
safe=safe,
txMsgs=txMsgs,
rxMsgs=rxMsgs)
self.stack.value.Bk = raeting.bodyKinds.msgpack
self.stack.value.JoinentTimeout = 0.0
class SaltRaetRoadStackCloser(ioflo.base.deeding.Deed): # pylint: disable=W0232
@ -162,7 +163,7 @@ class SaltRaetRoadStackJoiner(ioflo.base.deeding.Deed):
'''
stack = self.stack.value
if stack and isinstance(stack, RoadStack):
stack.join(mha=self.mha, timeout=0.0)
stack.join(ha=self.mha, timeout=0.0)
class SaltRaetRoadStackJoined(ioflo.base.deeding.Deed):
@ -721,13 +722,13 @@ class NixExecutor(ioflo.base.deeding.Deed):
Send the return data back via the uxd socket
'''
stackname = self.opts['id'] + ret['jid']
dirpath = os.path.join(self.opts['cachedir'], stackname)
dirpath = os.path.join(self.opts['cachedir'], 'raet')
ret_stack = LaneStack(
name=stackname,
lanename=self.opts['id'],
yid=ret['jid'],
sockdirpath=self.opts['sock_dir'],
dirpath=dirpath)
basedirpath=dirpath)
ret_stack.Pk = raeting.packKinds.pack
main_yard = RemoteYard(

View File

@ -14,7 +14,7 @@ from ioflo.base.odicting import odict
from ioflo.base.consoling import getConsole
console = getConsole()
from raet import raeting, nacling, keeping
from raet import raeting, nacling
from raet.road.keeping import RoadKeep
from salt.key import RaetKey
@ -134,10 +134,16 @@ class SaltSafe(object):
Load and Return the data from the remote estate file
Override this in sub class to change uid
'''
status='accepted'
#status = raeting.ACCEPTANCE_NAMES.get(remote.acceptance, 'accepted')
#status='accepted'
mid = remote.name
keydata = self.saltRaetKey.read_remote(mid, status)
statae = raeting.ACCEPTANCES.keys()
for status in statae:
keydata = self.saltRaetKey.read_remote(mid, status)
if keydata:
break
if not keydata:
return None
@ -152,12 +158,25 @@ class SaltSafe(object):
def clearRemote(self, remote):
'''
Clear the remote estate file
Override this in sub class to change uid
Salt level keys should not be auto removed with cache changes
'''
#mid = str(remote.eid)
mid = remote.name
self.saltRaetKey.delete_key(mid)
pass
def replaceRemote(self, remote, old):
'''
Replace the safe keep key file at old name given remote.name has changed
Assumes name uniqueness already taken care of
'''
new = remote.name
if new != old:
self.dumpRemote(remote) #will be pending by default unless autoaccept
# manually fix up acceptance if not pending
if remote.acceptance == raeting.acceptances.accepted:
self.acceptRemote(remote)
elif remote.acceptance == raeting.acceptances.rejected:
self.rejectRemote(remote)
self.saltRaetKey.delete_key(old) #now delete old key file
def statusRemote(self, remote, verhex, pubhex, main=True):
'''

View File

@ -903,6 +903,14 @@ class RemoteClient(Client):
else:
self.auth = ''
def _get_channel(self):
'''
Return the right channel
'''
if self.auth:
return self.channel
return salt.transport.Channel.factory(self.opts)
def get_file(self,
path,
dest='',
@ -976,7 +984,8 @@ class RemoteClient(Client):
else:
load['loc'] = fn_.tell()
try:
data = self.channel.send(load)
channel = self._get_channel()
data = channel.send(load)
except SaltReqTimeoutError:
return ''
if not data:
@ -1045,7 +1054,8 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_file_list'}
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1067,7 +1077,8 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_file_list_emptydirs'}
try:
self.channel.send(load)
channel = self._get_channel()
channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1089,7 +1100,8 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_dir_list'}
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1101,7 +1113,8 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_symlink_list'}
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1139,7 +1152,8 @@ class RemoteClient(Client):
'saltenv': saltenv,
'cmd': '_file_hash'}
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1160,7 +1174,8 @@ class RemoteClient(Client):
load = {'saltenv': saltenv,
'cmd': '_file_list'}
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1170,7 +1185,8 @@ class RemoteClient(Client):
'''
load = {'cmd': '_master_opts'}
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''
@ -1182,8 +1198,10 @@ class RemoteClient(Client):
load = {'cmd': '_ext_nodes',
'id': self.opts['id'],
'opts': self.opts}
load['tok'] = self.auth.gen_token('salt')
if self.auth:
load['tok'] = self.auth.gen_token('salt')
try:
return self.channel.send(load)
channel = self._get_channel()
return channel.send(load)
except SaltReqTimeoutError:
return ''

View File

@ -1150,6 +1150,8 @@ def file_hash(load, fnd):
'{0}.hash.{1}'.format(relpath,
__opts__['hash_type']))
if not os.path.isfile(hashdest):
if not os.path.exists(os.path.dirname(hashdest)):
os.makedirs(os.path.dirname(hashdest))
with salt.utils.fopen(path, 'rb') as fp_:
ret['hsum'] = getattr(hashlib, __opts__['hash_type'])(
fp_.read()).hexdigest()

View File

@ -370,8 +370,7 @@ def grains(opts, force_refresh=False):
opts['grains'] = {}
else:
opts['grains'] = {}
load = _create_loader(opts, 'grains', 'grain')
load = _create_loader(opts, 'grains', 'grain', ext_type_dirs='grains_dirs')
grains_info = load.gen_grains(force_refresh)
grains_info.update(opts['grains'])
return grains_info
@ -408,6 +407,20 @@ def queues(opts):
return load.gen_functions()
def sdb(opts, functions=None, whitelist=None):
'''
Make a very small database call
'''
load = _create_loader(opts, 'sdb', 'sdb')
pack = {'name': '__sdb__',
'value': functions}
return LazyLoader(load,
functions,
pack,
whitelist=whitelist,
)
def clouds(opts):
'''
Return the cloud functions

View File

@ -1152,7 +1152,7 @@ class AESFuncs(object):
file_recv_max_size = 1024*1024 * self.opts.get('file_recv_max_size', 100)
if 'loc' in load and load['loc'] < 0:
log.error("load['loc'] is < 0, this should not happen.")
log.error('Should not happen: load[loc] < 0')
return False
if len(load['data']) + load.get('loc', 0) > file_recv_max_size:

View File

@ -557,6 +557,7 @@ class Minion(MinionBase):
This class instantiates a minion, runs connections for a minion,
and loads all of the functions into the minion
'''
def __init__(self, opts, timeout=60, safe=True):
'''
Pass in the options dict
@ -575,79 +576,10 @@ class Minion(MinionBase):
# module
opts['grains'] = salt.loader.grains(opts)
# check if master_type was altered from its default
if opts['master_type'] != 'str':
# check for a valid keyword
if opts['master_type'] == 'func':
# split module and function and try loading the module
mod, fun = opts['master'].split('.')
try:
master_mod = salt.loader.raw_mod(opts, mod, fun)
if not master_mod:
raise TypeError
# we take whatever the module returns as master address
opts['master'] = master_mod[mod + '.' + fun]()
except TypeError:
msg = ('Failed to evaluate master address from '
'module \'{0}\''.format(opts['master']))
log.error(msg)
sys.exit(1)
log.info('Evaluated master from module: {0}'.format(master_mod))
# if failover is set, master has to be of type list
elif opts['master_type'] == 'failover':
if type(opts['master']) is list:
log.info('Got list of available master addresses:'
' {0}'.format(opts['master']))
else:
msg = ('master_type set to \'failover\' but \'master\' '
'is not of type list but of type '
'{0}'.format(type(opts['master'])))
log.error(msg)
sys.exit(1)
else:
msg = ('Invalid keyword \'{0}\' for variable '
'\'master_type\''.format(opts['master_type']))
log.error(msg)
sys.exit(1)
# if we have a list of masters, loop through them and be
# happy with the first one that allows us to connect
if type(opts['master']) is list:
conn = False
# shuffle the masters and then loop through them
local_masters = copy.copy(opts['master'])
if opts['master_shuffle']:
shuffle(local_masters)
for master in local_masters:
opts['master'] = master
opts.update(resolve_dns(opts))
super(Minion, self).__init__(opts)
try:
if self.authenticate(timeout, safe) != 'full':
conn = True
break
except SaltClientError:
msg = ('Master {0} could not be reached, trying '
'next master (if any)'.format(opts['master']))
log.info(msg)
continue
if not conn:
msg = ('No master could be reached or all masters denied '
'the minions connection attempt.')
log.error(msg)
# single master sign in
else:
opts.update(resolve_dns(opts))
super(Minion, self).__init__(opts)
if self.authenticate(timeout, safe) == 'full':
msg = ('master {0} rejected the minions connection because too '
'many minions are already connected.'.format(opts['master']))
log.error(msg)
sys.exit(1)
# evaluate the master to connect to and authenticate with it
opts['master'] = self.eval_master(opts,
timeout,
safe)
self.opts['pillar'] = salt.pillar.get_pillar(
opts,
@ -684,7 +616,8 @@ class Minion(MinionBase):
'function': 'status.master',
'seconds': opts['master_alive_interval'],
'jid_include': True,
'maxrunning': 2
'maxrunning': 1,
'args': [True]
}
})
@ -706,6 +639,121 @@ class Minion(MinionBase):
log.debug('I am {0} and I am not supposed to start any proxies. '
'(Likely not a problem)'.format(self.opts['id']))
# __init__() from MinionBase is called in Minion.eval_master()
# pylint: disable=W0231
def eval_master(self,
opts,
timeout=60,
safe=True,
failed=False):
'''
Evaluates and returns the current master address. In standard mode, just calls
authenticate() with the given master address.
With master_type=func evaluates the current master address from the given
module and then calls authenticate().
With master_type=failover takes the list of masters and loops through them.
The first one that allows the minion to connect is used to authenticate() and
then returned. If this function is called outside the minions initialisation
phase (for example from the minions main event-loop when a master connection
loss was detected), 'failed' should be set to True. The current
(possibly failed) master will then be removed from the list of masters.
'''
# check if master_type was altered from its default
if opts['master_type'] != 'str':
# check for a valid keyword
if opts['master_type'] == 'func':
# split module and function and try loading the module
mod, fun = opts['master'].split('.')
try:
master_mod = salt.loader.raw_mod(opts, mod, fun)
if not master_mod:
raise TypeError
# we take whatever the module returns as master address
opts['master'] = master_mod[mod + '.' + fun]()
except TypeError:
msg = ('Failed to evaluate master address from '
'module \'{0}\''.format(opts['master']))
log.error(msg)
sys.exit(1)
log.info('Evaluated master from module: {0}'.format(master_mod))
# if failover is set, master has to be of type list
elif opts['master_type'] == 'failover':
if isinstance(opts['master'], list):
log.info('Got list of available master addresses:'
' {0}'.format(opts['master']))
if opts['master_shuffle']:
shuffle(opts['master'])
# if failed=True, the minion was previously connected
# we're probably called from the minions main-event-loop
# because a master connection loss was detected. remove
# the possibly failed master from the list of masters.
elif failed:
log.info('Removing possibly failed master {0} from list of'
' masters'.format(opts['master']))
# create new list of master with the possibly failed one removed
opts['master'] = [x for x in opts['master_list'] if opts['master'] != x]
else:
msg = ('master_type set to \'failover\' but \'master\' '
'is not of type list but of type '
'{0}'.format(type(opts['master'])))
log.error(msg)
sys.exit(1)
else:
msg = ('Invalid keyword \'{0}\' for variable '
'\'master_type\''.format(opts['master_type']))
log.error(msg)
sys.exit(1)
# if we have a list of masters, loop through them and be
# happy with the first one that allows us to connect
if isinstance(opts['master'], list):
conn = False
# shuffle the masters and then loop through them
local_masters = copy.copy(opts['master'])
for master in local_masters:
opts['master'] = master
opts.update(resolve_dns(opts))
super(Minion, self).__init__(opts)
# make a backup of the master list for later use
self.opts['master_list'] = local_masters
try:
if self.authenticate(timeout, safe) != 'full':
conn = True
break
except SaltClientError:
msg = ('Master {0} could not be reached, trying '
'next master (if any)'.format(opts['master']))
log.info(msg)
continue
if not conn:
msg = ('No master could be reached or all masters denied '
'the minions connection attempt.')
log.error(msg)
else:
return opts['master']
# single master sign in
else:
opts.update(resolve_dns(opts))
super(Minion, self).__init__(opts)
if self.authenticate(timeout, safe) == 'full':
msg = ('master {0} rejected the minions connection because too '
'many minions are already connected.'.format(opts['master']))
log.error(msg)
sys.exit(1)
else:
return opts['master']
def _prep_mod_opts(self):
'''
Returns a copy of the opts with key bits stripped out
@ -1476,6 +1524,7 @@ class Minion(MinionBase):
ping_interval = self.opts.get('ping_interval', 0) * 60
ping_at = None
self.connected = True
while self._running is True:
loop_interval = self.process_schedule(self, loop_interval)
try:
@ -1512,9 +1561,46 @@ class Minion(MinionBase):
tag, data = salt.utils.event.MinionEvent.unpack(package)
log.debug('Forwarding master event tag={tag}'.format(tag=data['tag']))
self._fire_master(data['data'], data['tag'], data['events'], data['pretag'])
elif package.startswith('__master_disconnect'):
log.debug('handling master disconnect')
elif package.startswith('__master_disconnected'):
# handle this event only once. otherwise it will polute the log
if self.connected:
log.info('Connection to master {0} lost'.format(self.opts['master']))
if self.opts['master_type'] == 'failover':
log.info('Trying to tune in to next master from master-list')
self.eval_master(opts=self.opts,
failed=True)
# modify the __master_alive job to only fire,
# once the connection was re-established
schedule = {
'function': 'status.master',
'seconds': self.opts['master_alive_interval'],
'jid_include': True,
'maxrunning': 2,
'kwargs': {'connected': False}
}
self.schedule.modify_job(name='__master_alive',
schedule=schedule)
self.connected = False
elif package.startswith('__master_connected'):
# handle this event only once. otherwise it will polute the log
if not self.connected:
log.info('Connection to master {0} re-established'.format(self.opts['master']))
self.connected = True
# modify the __master_alive job to only fire,
# if the connection is lost again
schedule = {
'function': 'status.master',
'seconds': self.opts['master_alive_interval'],
'jid_include': True,
'maxrunning': 2,
'kwargs': {'connected': True}
}
self.schedule.modify_job(name='__master_alive',
schedule=schedule)
self.connected = True
self.epub_sock.send(package)
except Exception:
log.debug('Exception while handling events', exc_info=True)

View File

@ -1223,14 +1223,16 @@ def exec_code(lang, code, cwd=None):
def run_chroot(root, cmd):
'''
Chroot into a directory and run a cmd, this routines calls cmd.run_all
wrapped in `chroot` with dev and proc mounted in the chroot
.. versionadded:: Helium
This function runs :mod:`cmd.run_all <salt.modules.cmdmod.run_all>` wrapped
within a chroot, with dev and proc mounted in the chroot
CLI Example:
.. code-block:: bash
salt '*' cmd.chroot_run /var/lib/lxc/container_name/rootfs 'sh /tmp/bootstrap.sh'
salt '*' cmd.run_chroot /var/lib/lxc/container_name/rootfs 'sh /tmp/bootstrap.sh'
'''
__salt__['mount.mount'](
os.path.join(root, 'dev'),

View File

@ -11,6 +11,7 @@ import os
import salt.utils
import salt._compat
import salt.syspaths as syspaths
import salt.utils.sdb as sdb
__proxyenabled__ = ['*']
@ -226,16 +227,20 @@ def get(key, default=''):
'''
ret = salt.utils.traverse_dict_and_list(__opts__, key, '_|-')
if ret != '_|-':
return ret
return sdb.sdb_get(ret, __opts__)
ret = salt.utils.traverse_dict_and_list(__grains__, key, '_|-')
if ret != '_|-':
return ret
return sdb.sdb_get(ret, __opts__)
ret = salt.utils.traverse_dict_and_list(__pillar__, key, '_|-')
if ret != '_|-':
return ret
return sdb.sdb_get(ret, __opts__)
ret = salt.utils.traverse_dict_and_list(__pillar__.get('master', {}), key, '_|-')
if ret != '_|-':
return ret
return sdb.sdb_get(ret, __opts__)
return default

View File

@ -117,6 +117,26 @@ _DEB_CONFIG_BRIDGEING_OPTS = [
'pathcost', 'portprio', 'ports',
'stp', 'waitport'
]
_DEB_CONFIG_PPPOE_OPTS = {
'user': 'user',
'password': 'password',
'provider': 'provider',
'pppoe_iface': 'pppoe_iface',
'noipdefault': 'noipdefault',
'usepeerdns': 'usepeerdns',
'defaultroute': 'defaultroute',
'holdoff': 'holdoff',
'maxfail': 'maxfail',
'hide-password': 'hide-password',
'lcp-echo-interval': 'lcp-echo-interval',
'lcp-echo-failure': 'lcp-echo-failure',
'connect': 'connect',
'noauth': 'noauth',
'persist': 'persist',
'mtu': 'mtu',
'noaccomp': 'noaccomp',
}
_DEB_ROUTES_FILE = '/etc/network/routes'
_DEB_NETWORK_FILE = '/etc/network/interfaces'
_DEB_NETWORK_DIR = '/etc/network/interfaces.d'
@ -126,12 +146,13 @@ _DEB_NETWORK_CONF_FILES = '/etc/modprobe.d'
_DEB_NETWORKING_FILE = '/etc/default/networking'
_DEB_HOSTNAME_FILE = '/etc/hostname'
_DEB_RESOLV_FILE = '/etc/resolv.conf'
_DEB_PPP_DIR = '/etc/ppp/peers'
_CONFIG_TRUE = ['yes', 'on', 'true', '1', True]
_CONFIG_FALSE = ['no', 'off', 'false', '0', False]
_IFACE_TYPES = [
'eth', 'bond', 'alias', 'clone',
'ipsec', 'dialup', 'bridge', 'slave', 'vlan',
'ipsec', 'dialup', 'bridge', 'slave', 'vlan', 'pppoe',
]
@ -339,6 +360,9 @@ def _parse_interfaces():
if sline[0] == 'vlan-raw-device':
adapters[iface_name]['data'][context]['vlan_raw_device'] = sline[1]
if sline[0] == 'provider':
adapters[iface_name]['data'][context]['provider'] = sline[1]
if sline[0] in _REV_ETHTOOL_CONFIG_OPTS:
ethtool_key = sline[0]
if 'ethtool' not in adapters[iface_name]['data'][context]:
@ -452,6 +476,38 @@ def _parse_ethtool_opts(opts, iface):
return config
def _parse_ethtool_pppoe_opts(opts, iface):
'''
Filters given options and outputs valid settings for ETHTOOLS_PPPOE_OPTS
If an option has a value that is not expected, this
function will log what the Interface, Setting and what it was
expecting.
'''
config = {}
for opt in _DEB_CONFIG_PPPOE_OPTS:
if opt in opts:
config[opt] = opts[opt]
if 'provider' in opts:
if opts['provider']:
pass
else:
_raise_error_iface(iface, 'provider', _CONFIG_TRUE + _CONFIG_FALSE)
valid = _CONFIG_TRUE + _CONFIG_FALSE
for option in ('noipdefault', 'usepeerdns', 'defaultroute', 'hide-password', 'noauth', 'persist', 'noaccomp'):
if option in opts:
if opts[option] in _CONFIG_TRUE:
config.update({option: 'True'})
elif opts[option] in _CONFIG_FALSE:
config.update({option: 'False'})
else:
_raise_error_iface(iface, option, valid)
return config
def _parse_settings_bond(opts, iface):
'''
Filters given options and outputs valid settings for requested
@ -936,6 +992,12 @@ def _parse_settings_eth(opts, iface_type, enabled, iface):
if iface_type == 'vlan':
adapters[iface]['data']['inet']['vlan_raw_device'] = re.sub(r'\.\d*', '', iface)
if iface_type == 'pppoe':
tmp_ethtool = _parse_ethtool_pppoe_opts(opts, iface)
if tmp_ethtool:
for item in tmp_ethtool:
adapters[iface]['data']['inet'][_DEB_CONFIG_PPPOE_OPTS[item]] = tmp_ethtool[item]
if iface_type == 'bridge':
bridgeing = _parse_bridge_opts(opts, iface)
if bridgeing:
@ -943,7 +1005,7 @@ def _parse_settings_eth(opts, iface_type, enabled, iface):
adapters[iface]['data']['inet']['bridgeing_keys'] = sorted(bridgeing.keys())
if 'proto' in opts:
valid = ['bootp', 'dhcp', 'none', 'static', 'manual', 'loopback']
valid = ['bootp', 'dhcp', 'none', 'static', 'manual', 'loopback', 'ppp']
if opts['proto'] in valid:
# no 'none' proto for Debian, set to static
if opts['proto'] == 'none':
@ -1188,6 +1250,37 @@ def _write_file_ifaces(iface, data):
return saved_ifcfg.split('\n')
def _write_file_ppp_ifaces(iface, data):
'''
Writes a file to disk
'''
try:
template = JINJA.get_template('debian_ppp_eth.jinja')
except jinja2.exceptions.TemplateNotFound:
log.error('Could not load template debian_ppp_eth.jinja')
return ''
adapters = _parse_interfaces()
adapters[iface] = data
ifcfg = ''
tmp = template.render({'data': adapters[iface]})
ifcfg = tmp + ifcfg
filename = _DEB_PPP_DIR + '/' + adapters[iface]['data']['inet']['provider']
if not os.path.exists(os.path.dirname(filename)):
msg = '{0} cannot be written.'
msg = msg.format(os.path.dirname(filename))
log.error(msg)
raise AttributeError(msg)
fout = salt.utils.fopen(filename, 'w')
fout.write(ifcfg)
fout.close()
# Return as a array so the difflib works
return filename
def build_bond(iface, **settings):
'''
Create a bond script in /etc/modprobe.d with the passed settings
@ -1259,6 +1352,11 @@ def build_interface(iface, iface_type, enabled, **settings):
if iface_type == 'vlan':
settings['vlan'] = 'yes'
if iface_type == 'pppoe':
settings['pppoe'] = 'yes'
if not __salt__['pkg.version']("ppp"):
inst = __salt__['pkg.install']("ppp")
if iface_type is 'bond':
if 'slaves' not in settings:
msg = 'slaves is a required setting for bond interfaces'
@ -1272,7 +1370,7 @@ def build_interface(iface, iface_type, enabled, **settings):
raise AttributeError(msg)
__salt__['pkg.install']('bridge-utils')
if iface_type in ['eth', 'bond', 'bridge', 'slave', 'vlan']:
if iface_type in ['eth', 'bond', 'bridge', 'slave', 'vlan', 'pppoe']:
opts = _parse_settings_eth(settings, iface_type, enabled, iface)
if 'test' in settings and settings['test']:
@ -1280,6 +1378,9 @@ def build_interface(iface, iface_type, enabled, **settings):
ifcfg = _write_file_ifaces(iface, opts[iface])
if iface_type == 'pppoe':
_write_file_ppp_ifaces(iface, opts[iface])
# ensure lines in list end with newline, so difflib works
return [item + '\n' for item in ifcfg]

View File

@ -480,6 +480,7 @@ def check(table='filter', chain=None, rule=None, family='ipv4'):
if not rule:
return 'Error: Rule needs to be specified'
HAS_CHECK = False
if '--check' in salt_cmd.run('iptables --help', output_loglevel='quiet'):
HAS_CHECK = True

View File

@ -107,6 +107,8 @@ def purge():
schedule.update(__pillar__['schedule'])
for name in schedule.keys():
if name == 'enabled':
continue
if name.startswith('__'):
continue

View File

@ -545,7 +545,7 @@ def version():
return ret
def master():
def master(connected=True):
'''
.. versionadded:: Helium
@ -562,6 +562,11 @@ def master():
port = int(__salt__['config.option']('publish_port'))
ips = _remote_port_tcp(port)
if ip not in ips:
event = salt.utils.event.get_event('minion', opts=__opts__, listen=False)
event.fire_event({'master': ip}, '__master_disconnected')
if connected:
if ip not in ips:
event = salt.utils.event.get_event('minion', opts=__opts__, listen=False)
event.fire_event({'master': ip}, '__master_disconnected')
else:
if ip in ips:
event = salt.utils.event.get_event('minion', opts=__opts__, listen=False)
event.fire_event({'master': ip}, '__master_connected')

View File

@ -34,8 +34,9 @@ def echo(text):
def ping():
'''
Just used to make sure the minion is up and responding
Return True
Used to make sure the minion is up and responding. Not an ICMP ping.
Returns ``True``.
CLI Example:

View File

@ -178,8 +178,12 @@ class SREQ(object):
delete socket if you have it
'''
if hasattr(self, '_socket'):
if self._socket in self.poller:
self.poller.unregister(self._socket)
if isinstance(self.poller.sockets, dict):
for socket in self.poller.sockets.keys():
self.poller.unregister(socket)
else:
for socket in self.poller.sockets:
self.poller.unregister(socket[0])
del self._socket
def send(self, enc, load, tries=1, timeout=60):

View File

@ -11,7 +11,7 @@ configuration file:
.. code-block:: yaml
my_etd_config:
my_etcd_config:
etcd.host: 127.0.0.1
etcd.port: 4001

View File

@ -8,6 +8,8 @@ from __future__ import print_function
import os
import sys
import time
import traceback
import logging
# Import salt libs
import salt
@ -21,6 +23,27 @@ except ImportError:
HAS_SALTCLOUD = False
log = logging.getLogger(__name__)
def _handle_interrupt(exc, original_exc, hardfail=False, trace=''):
'''
if hardfalling:
If we got the original stacktrace, log it
If all cases, raise the original exception
but this is logically part the initial
stack.
else just let salt exit gracefully
'''
if hardfail:
if trace:
log.error(trace)
raise original_exc
else:
raise exc
def salt_master():
'''
Start the salt-master.
@ -65,11 +88,20 @@ def salt_key():
'''
Manage the authentication keys with salt-key.
'''
client = None
try:
saltkey = salt.cli.SaltKey()
saltkey.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
client = salt.cli.SaltKey()
client.run()
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)
def salt_cp():
@ -77,11 +109,20 @@ def salt_cp():
Publish commands to the salt system from the command line on the
master.
'''
client = None
try:
cp_ = salt.cli.SaltCP()
cp_.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
client = salt.cli.SaltCP()
client.run()
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)
def salt_call():
@ -91,11 +132,20 @@ def salt_call():
'''
if '' in sys.path:
sys.path.remove('')
client = None
try:
client = salt.cli.SaltCall()
client.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)
def salt_run():
@ -104,11 +154,20 @@ def salt_run():
'''
if '' in sys.path:
sys.path.remove('')
client = None
try:
client = salt.cli.SaltRun()
client.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)
def salt_ssh():
@ -117,13 +176,30 @@ def salt_ssh():
'''
if '' in sys.path:
sys.path.remove('')
client = None
try:
client = salt.cli.SaltSSH()
client.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)
except salt.exceptions.SaltClientError as err:
raise SystemExit(err)
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit(err),
err,
hardcrash, trace=trace)
def salt_cloud():
@ -137,11 +213,20 @@ def salt_cloud():
print('salt-cloud is not available in this system')
sys.exit(os.EX_UNAVAILABLE)
client = None
try:
cloud = salt.cloud.cli.SaltCloud()
cloud.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
client = salt.cloud.cli.SaltCloud()
client.run()
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)
def salt_main():
@ -151,8 +236,17 @@ def salt_main():
'''
if '' in sys.path:
sys.path.remove('')
client = None
try:
client = salt.cli.SaltCMD()
client.run()
except KeyboardInterrupt:
raise SystemExit('\nExiting gracefully on Ctrl-c')
except KeyboardInterrupt, err:
trace = traceback.format_exc()
try:
hardcrash = client.options.hard_crash
except (AttributeError, KeyError):
hardcrash = False
_handle_interrupt(
SystemExit('\nExiting gracefully on Ctrl-c'),
err,
hardcrash, trace=trace)

4
salt/sdb/__init__.py Normal file
View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
'''
SDB Module Directory
'''

98
salt/sdb/keyring_db.py Normal file
View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
'''
Keyring Database Module
:maintainer: SaltStack
:maturity: New
:depends: keyring
:platform: all
This module allows access to the keyring package using an ``sdb://`` URI. This
package is located at ``https://pypi.python.org/pypi/keyring``.
Care must be taken when using keyring. Not all keyend backends are supported on
all operating systems. Also, many backends require an agent to be running in
order to work. For instance, the "Secret Service" backend requires a compatible
agent such as ``gnome-keyring-daemon`` or ``kwallet`` to be running. The
keyczar backend does not seem to enjoy the benefits of an agent, and so using
it will require either that the password is typed in manually (which is
unreasonable for the salt-minion and salt-master daemons, especially in
production) or an agent is written for it.
Like all sdb modules, the keyring module requires a configuration profile to
be configured in either the minion or master configuration file. This profile
requires very little. In the example:
.. code-block:: yaml
mykeyring:
- driver: keyring
- service: system
The ``driver`` refers to the keyring module, ``service`` refers to the service
that will be used inside of keyring (which may be likened unto a database
table) and ``mykeyring`` refers to the name that will appear in the URI:
.. code-block:: yaml
password: sdb://mykeyring/mypassword
The underlying backend configuration must be configured via keyring itself. For
examples and documentation, see keyring:
https://pypi.python.org/pypi/keyring
.. versionadded:: 2014.1.4 (Hydrogen)
'''
# import python libs
import logging
try:
import keyring
HAS_LIBS = True
except ImportError:
HAS_LIBS = False
log = logging.getLogger(__name__)
__func_alias__ = {
'set_': 'set'
}
__virtualname__ = 'keyring'
def __virtual__():
'''
Only load the module if keyring is installed
'''
if HAS_LIBS:
return __virtualname__
return False
def set_(key, value, service=None, profile=None):
'''
Set a key/value pair in a keyring service
'''
service = _get_service(service, profile)
keyring.set_password(service, key, value)
def get(key, service=None, profile=None):
'''
Get a value from a keyring service
'''
service = _get_service(service, profile)
return keyring.get_password(service, key)
def _get_service(service, profile):
'''
Get a service name
'''
if isinstance(profile, dict) and 'service' in profile:
return profile['service']
return service

View File

@ -73,6 +73,13 @@ STATE_INTERNAL_KEYWORDS = frozenset([
])
def _odict_hashable(self):
return id(self)
OrderedDict.__hash__ = _odict_hashable
def split_low_tag(tag):
'''
Take a low tag and split it back into the low dict that it came from
@ -477,9 +484,14 @@ class Compiler(object):
chunk.update(arg)
if names:
name_order = 1
for low_name in names:
for entry in names:
live = copy.deepcopy(chunk)
live['name'] = low_name
if isinstance(entry, dict):
low_name = entry.keys()[0]
live['name'] = low_name
live.update(entry[low_name][0])
else:
live['name'] = entry
live['name_order'] = name_order
name_order = name_order + 1
for fun in funcs:
@ -1059,9 +1071,14 @@ class State(object):
chunk[key] = val
if names:
name_order = 1
for low_name in names:
for entry in names:
live = copy.deepcopy(chunk)
live['name'] = low_name
if isinstance(entry, dict):
low_name = entry.keys()[0]
live['name'] = low_name
live.update(entry[low_name][0])
else:
live['name'] = entry
live['name_order'] = name_order
name_order = name_order + 1
for fun in funcs:

View File

@ -82,7 +82,6 @@ def managed(name, **kwargs):
The name of the package repo, as it would be referred to when running
the regular package manager commands.
For yum-based systems, take note of the following configuration values:
humanname
@ -148,8 +147,13 @@ def managed(name, **kwargs):
- name: deb http://us.archive.ubuntu.com/ubuntu precise main
disabled
On apt-based systems, disabled toggles whether or not the repo is
used for resolving dependencies and/or installing packages
Toggles whether or not the repo is used for resolving dependencies
and/or installing packages.
enabled
Enables the repository, even if the repository has been disabled, in
order for the respective package requiring the repository can be found
and installed.
comps
On apt-based systems, comps dictate the types of packages to be

View File

@ -52,6 +52,9 @@ def __virtual__():
def _enabled_used_error(ret):
'''
Warn of potential typo.
'''
ret['result'] = False
ret['comment'] = (
'Service {0} uses non-existent option "enabled". ' +
@ -241,7 +244,9 @@ def _disable(name, started, result=True, **kwargs):
def _available(name, ret):
# Check if the service is available
'''
Check if the service is available
'''
avail = False
if 'service.available' in __salt__:
avail = __salt__['service.available'](name)
@ -420,8 +425,7 @@ def mod_watch(name, sfun=None, sig=None, reload=False, full_restart=False):
sfun
The original function which triggered the mod_watch call
(`service.running`, for example). Currently not used, but must be
supported for the future.
(`service.running`, for example).
sig
The string to search for when looking for the service process with ps
@ -430,31 +434,44 @@ def mod_watch(name, sfun=None, sig=None, reload=False, full_restart=False):
'changes': {},
'result': True,
'comment': ''}
action = ''
if __salt__['service.status'](name, sig):
if 'service.reload' in __salt__ and reload:
restart_func = __salt__['service.reload']
action = 'reload'
elif 'service.full_restart' in __salt__ and full_restart:
restart_func = __salt__['service.full_restart']
action = 'fully restart'
if sfun == 'dead':
verb = 'stop'
past_participle = verb + 'ped'
if __salt__['service.status'](name, sig):
func = __salt__['service.stop']
else:
restart_func = __salt__['service.restart']
action = 'restart'
else:
restart_func = __salt__['service.start']
action = 'start'
ret['result'] = True
ret['comment'] = 'Service is already {0}'.format(past_participle)
return ret
elif sfun == 'running':
if __salt__['service.status'](name, sig):
if 'service.reload' in __salt__ and reload:
func = __salt__['service.reload']
verb = 'reload'
past_participle = verb + 'ed'
elif 'service.full_restart' in __salt__ and full_restart:
func = __salt__['service.full_restart']
verb = 'fully restart'
past_participle = verb + 'ed'
else:
func = __salt__['service.restart']
verb = 'restart'
past_participle = verb + 'ed'
else:
func = __salt__['service.start']
verb = 'start'
past_participle = verb + 'ed'
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Service is set to be {0}ed'.format(action)
ret['comment'] = 'Service is set to be {0}'.format(past_participle)
return ret
result = restart_func(name)
result = func(name)
ret['changes'] = {name: result}
ret['result'] = result
ret['comment'] = 'Service {0}ed'.format(action) if result else \
'Failed to {0} the service'.format(action)
ret['comment'] = 'Service {0}'.format(past_participle) if result else \
'Failed to {0} the service'.format(verb)
return ret

View File

@ -27,8 +27,13 @@ def _check_error(result, success_message):
ret = {}
if 'ERROR' in result:
ret['comment'] = result
ret['result'] = False
if 'already running' in result:
ret['comment'] = success_message
elif 'not running' in result:
ret['comment'] = success_message
else:
ret['comment'] = result
ret['result'] = False
else:
ret['comment'] = success_message

View File

@ -9,6 +9,7 @@ iface {{name}} {{interface.inet_type}} {{interface.proto}}
{%endif%}{% if interface.gateway %} gateway {{interface.gateway}}
{%endif%}{% if interface.addr %} hwaddress {{interface.addr}}
{%endif%}{% if interface.vlan_raw_device %} vlan-raw-device {{interface.vlan_raw_device}}
{%endif%}{% if interface.provider %} provider {{interface.provider}}
{%endif%}{% if interface.mtu %} mtu {{interface.mtu}}
{%endif%}{% if interface.dns %} dns-nameservers {%for item in interface.dns %}{{item}} {%endfor%}
{%endif%}{% if interface.ethtool %}{%for item in interface.ethtool_keys %} {{item}} {{interface.ethtool[item]}}

View File

@ -0,0 +1,22 @@
{%- set iface = data.data.inet -%}
{% if iface['user'] %}user {{ iface['user']}}
{% if iface['password']%}password {{ iface['password'] }} {% endif %}
plugin rp-pppoe.so {{iface['pppoe_iface']|default('eth0')}}
{% if iface['noipdefault']|default(True) %}noipdefault{% endif %}
{% if iface['usepeerdns']|default(True) %}usepeerdns {% endif %}
{% if iface['defaultroute']|default(True) %}defaultroute{% endif %}
{% if iface['holdoff']|default('15') %}holdoff {{iface['holdoff']|default('15')}}{% endif %}
{% if iface['maxfail']|default('0') %}maxfail {{iface['maxfail']|default('0')}}{% endif %}
{% if iface['hide-password']|default(True) %}hide-password{% endif %}
{% if iface['lcp-echo-interval']|default('20') %}lcp-echo-interval {{ iface['lcp-echo-interval']|default('20')}} {% endif %}
{% if iface['lcp-echo-failure']|default('3') %}lcp-echo-failure {{iface['lcp-echo-failure']|default('3')}} {% endif %}
{% if iface['noauth']|default(True) %}noauth{% endif %}
{% if iface['persist']|default(True) %}persist{% endif %}
{% if iface['mtu']|default('1492') %}mtu {{iface['mtu']|default('1492')}} {% endif %}
{% if iface['noaccomp']|default(True) %}noaccomp{% endif %}
{% if iface['default-asyncmap'] %}default-asyncmap{% endif %}
{% endif %}

View File

@ -1,5 +1,5 @@
#!/bin/sh
# {{route_type}}
{% for route in routes %}{% if route.name %}# {{route.name}}
{%endif%}route {{route_type}} -net {% if route.ipaddr %}{{route.ipaddr}}{%endif%} {% if route.netmask %}netmask {{route.netmask}}{%endif%} {% if route.gateway %}gateway {{route.gateway}}{%endif%}dev {{iface}}
{%endif%}route {{route_type}} -net {% if route.ipaddr %}{{route.ipaddr}}{%endif%} {% if route.netmask %}netmask {{route.netmask}}{%endif%} {% if route.gateway %}gateway {{route.gateway}}{%endif%} dev {{iface}}
{% endfor %}

View File

@ -62,12 +62,12 @@ class RAETChannel(Channel):
'''
yid = salt.utils.gen_jid()
stackname = self.opts['id'] + yid
dirpath = os.path.join(self.opts['cachedir'], stackname)
dirpath = os.path.join(self.opts['cachedir'], 'raet')
self.stack = LaneStack(
name=stackname,
lanename=self.opts['id'],
yid=yid,
dirpath=dirpath,
basedirpath=dirpath,
sockdirpath=self.opts['sock_dir'])
self.stack.Pk = raeting.packKinds.pack
self.router_yard = yarding.RemoteYard(
@ -93,12 +93,19 @@ class RAETChannel(Channel):
'''
msg = {'route': self.route, 'load': load}
self.stack.transmit(msg, self.stack.uids['yard0'])
tried = 1
start = time.time()
while True:
time.sleep(0.01)
self.stack.serviceAll()
if self.stack.rxMsgs:
for msg in self.stack.rxMsgs:
return msg.get('return', {})
if time.time() - start > timeout:
if tried >= tries:
raise ValueError
self.stack.transmit(msg, self.stack.uids['yard0'])
tried += 1
class ZeroMQChannel(Channel):

View File

@ -15,7 +15,7 @@ import subprocess
import multiprocessing
import logging
import pipes
import json
import msgpack
import traceback
import copy
import re
@ -1869,10 +1869,10 @@ def request_minion_cachedir(
'provider': provider,
}
fname = '{0}.json'.format(minion_id)
fname = '{0}.pp'.format(minion_id)
path = os.path.join(base, 'requested', fname)
with salt.utils.fopen(path, 'w') as fh_:
json.dump(data, fh_)
msgpack.dump(data, fh_)
def change_minion_cachedir(
@ -1900,16 +1900,16 @@ def change_minion_cachedir(
if base is None:
base = os.path.join(syspaths.CACHE_DIR, 'cloud')
fname = '{0}.json'.format(minion_id)
fname = '{0}.pp'.format(minion_id)
path = os.path.join(base, cachedir, fname)
with salt.utils.fopen(path, 'r') as fh_:
cache_data = json.load(fh_)
cache_data = msgpack.load(fh_)
cache_data.update(data)
with salt.utils.fopen(path, 'w') as fh_:
json.dump(cache_data, fh_)
msgpack.dump(cache_data, fh_)
def activate_minion_cachedir(minion_id, base=None):
@ -1921,7 +1921,7 @@ def activate_minion_cachedir(minion_id, base=None):
if base is None:
base = os.path.join(syspaths.CACHE_DIR, 'cloud')
fname = '{0}.json'.format(minion_id)
fname = '{0}.pp'.format(minion_id)
src = os.path.join(base, 'requested', fname)
dst = os.path.join(base, 'active')
shutil.move(src, dst)
@ -1940,7 +1940,7 @@ def delete_minion_cachedir(minion_id, provider, opts, base=None):
base = os.path.join(syspaths.CACHE_DIR, 'cloud')
driver = opts['providers'][provider].keys()[0]
fname = '{0}.json'.format(minion_id)
fname = '{0}.pp'.format(minion_id)
for cachedir in ('requested', 'active'):
path = os.path.join(base, cachedir, driver, provider, fname)
log.debug('path: {0}'.format(path))
@ -2079,9 +2079,9 @@ def cache_node_list(nodes, provider, opts):
for node in nodes:
diff_node_cache(prov_dir, node, nodes[node], opts)
path = os.path.join(prov_dir, '{0}.json'.format(node))
path = os.path.join(prov_dir, '{0}.pp'.format(node))
with salt.utils.fopen(path, 'w') as fh_:
json.dump(nodes[node], fh_)
msgpack.dump(nodes[node], fh_)
def cache_node(node, provider, opts):
@ -2101,9 +2101,9 @@ def cache_node(node, provider, opts):
prov_dir = os.path.join(base, driver, provider)
if not os.path.exists(prov_dir):
os.makedirs(prov_dir)
path = os.path.join(prov_dir, '{0}.json'.format(node['name']))
path = os.path.join(prov_dir, '{0}.pp'.format(node['name']))
with salt.utils.fopen(path, 'w') as fh_:
json.dump(node, fh_)
msgpack.dump(node, fh_)
def missing_node_cache(prov_dir, node_list, provider, opts):
@ -2122,7 +2122,7 @@ def missing_node_cache(prov_dir, node_list, provider, opts):
'''
cached_nodes = []
for node in os.listdir(prov_dir):
cached_nodes.append(node.replace('.json', ''))
cached_nodes.append(node.replace('.pp', ''))
log.debug(sorted(cached_nodes))
log.debug(sorted(node_list))
@ -2157,7 +2157,7 @@ def diff_node_cache(prov_dir, node, new_data, opts):
return
path = os.path.join(prov_dir, node)
path = '{0}.json'.format(path)
path = '{0}.pp'.format(path)
if not os.path.exists(path):
event_data = _strip_cache_events(new_data, opts)
@ -2173,7 +2173,7 @@ def diff_node_cache(prov_dir, node, new_data, opts):
with salt.utils.fopen(path, 'r') as fh_:
try:
cache_data = json.load(fh_)
cache_data = msgpack.load(fh_)
except ValueError as exc:
log.warning('Cache for {0} was corrupt: Deleting'.format(node))
cache_data = {}

View File

@ -376,6 +376,20 @@ class SaltfileMixIn(object):
cli_config[option.dest])
class HardCrashMixin(object):
__metaclass__ = MixInMeta
_mixin_prio_ = 40
_config_filename_ = None
def _mixin_setup(self):
hc = os.environ.get('SALT_HARD_CRASH', False)
self.add_option(
'--hard-crash', action='store_true', default=hc,
help=('Raise any original exception rather than exiting gracefully'
' Default: %default')
)
class ConfigDirMixIn(object):
__metaclass__ = MixInMeta
_mixin_prio_ = -10
@ -1518,7 +1532,7 @@ class SyndicOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
TimeoutMixIn, ExtendedTargetOptionsMixIn,
OutputOptionsMixIn, LogLevelMixIn):
OutputOptionsMixIn, LogLevelMixIn, HardCrashMixin):
__metaclass__ = OptionParserMeta
@ -1724,7 +1738,8 @@ class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
class SaltCPOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
TimeoutMixIn, TargetOptionsMixIn, LogLevelMixIn):
TimeoutMixIn, TargetOptionsMixIn, LogLevelMixIn,
HardCrashMixin):
__metaclass__ = OptionParserMeta
description = (
@ -1766,7 +1781,8 @@ class SaltCPOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
class SaltKeyOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
LogLevelMixIn, OutputOptionsMixIn, RunUserMixin):
LogLevelMixIn, OutputOptionsMixIn, RunUserMixin,
HardCrashMixin):
__metaclass__ = OptionParserMeta
@ -1979,7 +1995,7 @@ class SaltKeyOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
class SaltCallOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
LogLevelMixIn, OutputOptionsMixIn):
LogLevelMixIn, OutputOptionsMixIn, HardCrashMixin):
__metaclass__ = OptionParserMeta
description = ('Salt call is used to execute module functions locally '
@ -2110,7 +2126,7 @@ class SaltCallOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
class SaltRunOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
TimeoutMixIn, LogLevelMixIn):
TimeoutMixIn, LogLevelMixIn, HardCrashMixin):
__metaclass__ = OptionParserMeta
default_timeout = 1
@ -2172,7 +2188,7 @@ class SaltRunOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
class SaltSSHOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
LogLevelMixIn, TargetOptionsMixIn,
OutputOptionsMixIn, SaltfileMixIn):
OutputOptionsMixIn, SaltfileMixIn, HardCrashMixin):
__metaclass__ = OptionParserMeta
usage = '%prog [options]'
@ -2313,7 +2329,8 @@ class SaltCloudParser(OptionParser,
CloudQueriesMixIn,
ExecutionOptionsMixIn,
CloudProvidersListsMixIn,
CloudCredentialsMixIn):
CloudCredentialsMixIn,
HardCrashMixin):
__metaclass__ = OptionParserMeta

View File

@ -319,6 +319,9 @@ class Schedule(object):
Reload the schedule from saved schedule file.
'''
# Remove all jobs from self.intervals
self.intervals = {}
if 'schedule' in self.opts:
self.opts['schedule'].update(schedule['schedule'])
else:

60
salt/utils/sdb.py Normal file
View File

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
'''
Basic functions for accessing the SDB interface
'''
import salt.loader
from salt._compat import string_types
def sdb_get(uri, opts):
'''
Get a value from a db, using a uri in the form of sdb://<profile>/<key>. If
the uri provided does not start with sdb://, then it will be returned as-is.
'''
if not isinstance(uri, string_types):
return uri
if not uri.startswith('sdb://'):
return uri
comps = uri.replace('sdb://', '').split('/')
if len(comps) < 2:
return uri
profile = opts.get(comps[0], {})
if 'driver' not in profile:
return uri
fun = '{0}.get'.format(profile['driver'])
query = comps[1]
loaded_db = salt.loader.sdb(opts, fun)
return loaded_db[fun](query, profile=profile)
def sdb_set(uri, value, opts):
'''
Get a value from a db, using a uri in the form of sdb://<profile>/<key>. If
the uri provided does not start with sdb://, then it will be returned as-is.
'''
if not isinstance(uri, string_types):
return uri
if not uri.startswith('sdb://'):
return False
comps = uri.replace('sdb://', '').split('/')
if len(comps) < 2:
return False
profile = opts.get(comps[0], {})
if 'driver' not in profile:
return False
fun = '{0}.set'.format(profile['driver'])
query = comps[1]
loaded_db = salt.loader.sdb(opts, fun)
return loaded_db[fun](query, value, profile=profile)

286
setup.py
View File

@ -18,9 +18,11 @@ from datetime import datetime
from distutils import log
from distutils.cmd import Command
from distutils.command.build import build
from distutils.command.check import check
from distutils.command.clean import clean
from distutils.command.sdist import sdist
from distutils.command.install_lib import install_lib
from distutils.dist import Distribution as BaseDistribution
# pylint: enable=E0611
try:
@ -63,6 +65,7 @@ WITH_SETUPTOOLS = False
if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules:
try:
from setuptools import setup
from setuptools.dist import Distribution as BaseDistribution
from setuptools.command.install import install
from setuptools.command.sdist import sdist
WITH_SETUPTOOLS = True
@ -92,17 +95,11 @@ try:
except ImportError:
HAS_ESKY = False
SALT_VERSION = os.path.join(
os.path.abspath(SETUP_DIRNAME), 'salt', 'version.py'
)
SALT_REQS = os.path.join(
os.path.abspath(SETUP_DIRNAME), 'requirements.txt'
)
SALT_SYSPATHS = os.path.join(
os.path.abspath(SETUP_DIRNAME), 'salt', 'syspaths.py'
)
SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'version.py')
SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), '_requirements.txt')
SALT_ZEROMQ_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'zeromq-requirements.txt')
SALT_RAET_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'raet-requirements.txt')
SALT_SYSPATHS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'syspaths.py')
# pylint: disable=W0122
exec(compile(open(SALT_VERSION).read(), SALT_VERSION, 'exec'))
@ -110,12 +107,24 @@ exec(compile(open(SALT_SYSPATHS).read(), SALT_SYSPATHS, 'exec'))
# pylint: enable=W0122
# ----- Helper Functions -------------------------------------------------------------------------------------------->
def _parse_requirements_file(requirements_file):
parsed_requirements = []
with open(requirements_file) as rfh:
for line in rfh.readlines():
line = line.strip()
if not line or line.startswith(('#', '-r')):
continue
if IS_WINDOWS_PLATFORM and 'libcloud' in line:
continue
parsed_requirements.append(line)
return parsed_requirements
# <---- Helper Functions ---------------------------------------------------------------------------------------------
# ----- Custom Distutils/Setuptools Commands ------------------------------------------------------------------------>
class CloudSdist(sdist):
user_options = sdist.user_options + [
('skip-bootstrap-download', None,
'[DEPRECATED] Skip downloading the bootstrap-salt.sh script. This '
'can also be triggered by having `SKIP_BOOTSTRAP_DOWNLOAD=1` as an '
'environment variable.'),
('download-bootstrap-script', None,
'Download the latest stable bootstrap-salt.sh script. This '
'can also be triggered by having `DOWNLOAD_BOOTSTRAP_SCRIPT=1` as an '
@ -123,21 +132,15 @@ class CloudSdist(sdist):
]
boolean_options = sdist.boolean_options + [
'skip-bootstrap-download',
'download-bootstrap-script'
]
def initialize_options(self):
sdist.initialize_options(self)
self.skip_bootstrap_download = True
self.download_bootstrap_script = False
def finalize_options(self):
sdist.finalize_options(self)
if 'SKIP_BOOTSTRAP_DOWNLOAD' in os.environ:
log('Please stop using \'SKIP_BOOTSTRAP_DOWNLOAD\' and use '
'\'DOWNLOAD_BOOTSTRAP_SCRIPT\' instead')
if 'DOWNLOAD_BOOTSTRAP_SCRIPT' in os.environ:
download_bootstrap_script = os.environ.get(
'DOWNLOAD_BOOTSTRAP_SCRIPT', '0'
@ -154,7 +157,6 @@ class CloudSdist(sdist):
BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION
)
)
req = urllib2.urlopen(url)
deploy_path = os.path.join(
SETUP_DIRNAME,
'salt',
@ -162,28 +164,45 @@ class CloudSdist(sdist):
'deploy',
'bootstrap-salt.sh'
)
if req.getcode() == 200:
try:
log.info(
'Updating bootstrap-salt.sh.'
'\n\tSource: {0}'
'\n\tDestination: {1}'.format(
url,
deploy_path
log.info(
'Updating bootstrap-salt.sh.'
'\n\tSource: {0}'
'\n\tDestination: {1}'.format(
url,
deploy_path
)
)
try:
import requests
req = requests.get(url)
if req.status_code == 200:
script_contents = req.text.encode(req.encoding)
else:
log.error(
'Failed to update the bootstrap-salt.sh script. HTTP '
'Error code: {0}'.format(
req.status_code
)
)
with open(deploy_path, 'w') as fp_:
fp_.write(req.read())
except (OSError, IOError) as err:
except ImportError:
req = urllib2.urlopen(url)
if req.getcode() == 200:
script_contents = req.read()
else:
log.error(
'Failed to write the updated script: {0}'.format(err)
'Failed to update the bootstrap-salt.sh script. HTTP '
'Error code: {0}'.format(
req.getcode()
)
)
else:
try:
with open(deploy_path, 'w') as fp_:
fp_.write(script_contents)
except (OSError, IOError) as err:
log.error(
'Failed to update the bootstrap-salt.sh script. HTTP '
'Error code: {0}'.format(
req.getcode()
)
'Failed to write the updated script: {0}'.format(err)
)
# Let's the rest of the build command
@ -403,32 +422,122 @@ class InstallLib(install_lib):
os.chmod(filename, 0755)
class Check(check):
'''
Since check is always executed, or at least on most of the commands we use
we simply override it to adapt the install_requires distribution parameter
based on the chosen salt transport
'''
def run(self):
if self.distribution.salt_transport == 'zeromq':
self.distribution.install_requires.extend(_parse_requirements_file(SALT_ZEROMQ_REQS))
elif self.distribution.salt_transport == 'raet':
self.distribution.install_requires.extend(_parse_requirements_file(SALT_RAET_REQS))
return check.run(self)
# <---- Custom Distutils/Setuptools Commands -------------------------------------------------------------------------
class SaltDistribution(BaseDistribution):
global_options = BaseDistribution.global_options + [
('salt-transport=', None,
'The transport to prepare salt for. Choices are \'zeromq\' '
'and \'raet\'. Defaults to \'zeromq\''),
]
def __init__(self, attrs=None):
BaseDistribution.__init__(self, attrs=attrs)
# At this point options haven't been parsed yet. Provide defaults
self.salt_transport = 'zeromq'
# This flag is required to tweak the salt paths at install time
self.running_salt_install = False
self.cmdclass['check'] = Check
self.cmdclass['test'] = TestCommand
self.cmdclass['clean'] = Clean
self.cmdclass['build'] = Build
self.cmdclass['install'] = Install
if IS_WINDOWS_PLATFORM is False:
self.cmdclass['sdist'] = CloudSdist
self.cmdclass['install_lib'] = InstallLib
self.setup_requires = ['requests']
self.install_requires = _parse_requirements_file(SALT_REQS)
if IS_WINDOWS_PLATFORM is False:
# self.packages.extend(['salt.cloud',
# 'salt.cloud.clouds'])
self.package_data['salt.cloud'] = ['deploy/*.sh']
self.data_files[0][1].extend([
'doc/man/salt-master.1',
'doc/man/salt-key.1',
'doc/man/salt.1',
'doc/man/salt-syndic.1',
'doc/man/salt-run.1',
'doc/man/salt-ssh.1',
'doc/man/salt-cloud.1'
])
else:
self.install_requires.append('WMI')
if WITH_SETUPTOOLS:
self.entry_points = {
'console_scripts': ['salt-call = salt.scripts:salt_call',
'salt-cp = salt.scripts:salt_cp',
'salt-minion = salt.scripts:salt_minion',
]
}
if IS_WINDOWS_PLATFORM is False:
self.entry_points['console_scripts'].extend([
'salt = salt.scripts:salt_main',
'salt-cloud = salt.scripts:salt_cloud',
'salt-key = salt.scripts:salt_key',
'salt-master = salt.scripts:salt_master',
'salt-run = salt.scripts:salt_run',
'salt-ssh = salt.scripts:salt_ssh',
'salt-syndic = salt.scripts:salt_syndic',
])
# Required for running the tests suite
self.dependency_links = [
'https://github.com/saltstack/salt-testing/tarball/develop#egg=SaltTesting'
]
self.tests_require = ['SaltTesting']
else:
self.scripts = [
'scripts/salt-call',
'scripts/salt-cp',
'scripts/salt-minion',
'scripts/salt-unity',
]
if IS_WINDOWS_PLATFORM is False:
self.scripts.extend([
'scripts/salt',
'scripts/salt-cloud',
'scripts/salt-key',
'scripts/salt-master',
'scripts/salt-run',
'scripts/salt-ssh',
'scripts/salt-syndic',
])
NAME = 'salt'
VER = __version__ # pylint: disable=E0602
DESC = ('Portable, distributed, remote execution and '
'configuration management system')
DESC = 'Portable, distributed, remote execution and configuration management system'
REQUIREMENTS = []
with open(SALT_REQS) as rfh:
for line in rfh.readlines():
if not line or line.startswith('#'):
continue
if IS_WINDOWS_PLATFORM and 'libcloud' in line:
continue
REQUIREMENTS.append(line.strip())
SETUP_KWARGS = {'name': NAME,
SETUP_KWARGS = {'distclass': SaltDistribution,
'name': NAME,
'version': VER,
'description': DESC,
'author': 'Thomas S Hatch',
'author_email': 'thatch45@gmail.com',
'url': 'http://saltstack.org',
'cmdclass': {
'test': TestCommand,
'clean': Clean,
'build': Build,
'install': Install
},
'license': 'Apache Software License, Version 2.0',
'classifiers': ['Programming Language :: Python',
'Programming Language :: Cython',
'Programming Language :: Python :: 2.6',
@ -500,29 +609,11 @@ SETUP_KWARGS = {'name': NAME,
['doc/man/salt.7',
]),
],
# Required for esky builds
'install_requires': REQUIREMENTS,
# The dynamic module loading in salt.modules makes this
# package zip unsafe. Required for esky builds
'zip_safe': False
}
if IS_WINDOWS_PLATFORM is False:
SETUP_KWARGS['cmdclass']['sdist'] = CloudSdist
SETUP_KWARGS['cmdclass']['install_lib'] = InstallLib
#SETUP_KWARGS['packages'].extend(['salt.cloud',
# 'salt.cloud.clouds'])
SETUP_KWARGS['package_data']['salt.cloud'] = ['deploy/*.sh']
SETUP_KWARGS['data_files'][0][1].extend([
'doc/man/salt-master.1',
'doc/man/salt-key.1',
'doc/man/salt.1',
'doc/man/salt-syndic.1',
'doc/man/salt-run.1',
'doc/man/salt-ssh.1',
'doc/man/salt-cloud.1'
])
# bbfreeze explicit includes
# Sometimes the auto module traversal doesn't find everything, so we
@ -575,7 +666,6 @@ if IS_WINDOWS_PLATFORM:
'site',
'psutil',
])
SETUP_KWARGS['install_requires'].append('WMI')
elif sys.platform.startswith('linux'):
FREEZER_INCLUDES.append('spwd')
try:
@ -590,10 +680,10 @@ elif sys.platform.startswith('sunos'):
try:
from bbfreeze.modulegraph.modulegraph import ModuleGraph
mf = ModuleGraph(sys.path[:])
for arg in glob.glob("salt/modules/*.py"):
for arg in glob.glob('salt/modules/*.py'):
mf.run_script(arg)
for mod in mf.flatten():
if type(mod).__name__ != "Script" and mod.filename:
if type(mod).__name__ != 'Script' and mod.filename:
FREEZER_INCLUDES.append(str(os.path.basename(mod.identifier)))
except ImportError:
pass
@ -610,46 +700,6 @@ if HAS_ESKY:
}
SETUP_KWARGS['options'] = OPTIONS
if WITH_SETUPTOOLS:
SETUP_KWARGS['entry_points'] = {
'console_scripts': ['salt-call = salt.scripts:salt_call',
'salt-cp = salt.scripts:salt_cp',
'salt-minion = salt.scripts:salt_minion',
]
}
if IS_WINDOWS_PLATFORM is False:
SETUP_KWARGS['entry_points']['console_scripts'].extend([
'salt = salt.scripts:salt_main',
'salt-cloud = salt.scripts:salt_cloud',
'salt-key = salt.scripts:salt_key',
'salt-master = salt.scripts:salt_master',
'salt-run = salt.scripts:salt_run',
'salt-ssh = salt.scripts:salt_ssh',
'salt-syndic = salt.scripts:salt_syndic',
])
# Required for running the tests suite
SETUP_KWARGS['dependency_links'] = [
'https://github.com/saltstack/salt-testing/tarball/develop#egg=SaltTesting'
]
SETUP_KWARGS['tests_require'] = ['SaltTesting']
else:
SETUP_KWARGS['scripts'] = ['scripts/salt-call',
'scripts/salt-cp',
'scripts/salt-minion',
'scripts/salt-unity',
]
if IS_WINDOWS_PLATFORM is False:
SETUP_KWARGS['scripts'].extend([
'scripts/salt',
'scripts/salt-cloud',
'scripts/salt-key',
'scripts/salt-master',
'scripts/salt-run',
'scripts/salt-ssh',
'scripts/salt-syndic',
])
if __name__ == '__main__':
setup(**SETUP_KWARGS)

View File

@ -129,14 +129,14 @@ class GitFSTest(integration.ModuleCase):
with patch.dict(gitfs.__opts__, {'cachedir': self.master_opts['cachedir'],
'gitfs_remotes': ['file://' + self.tmp_repo_git],
'sock_dir': self.master_opts['sock_dir'],
'hash_type': 'blob_sha1'}):
'hash_type': 'sha1'}):
tmp_load = LOAD.copy()
tmp_load['loc'] = 0
tmp_load['path'] = 'testfile'
fnd = {'rel': 'testfile',
'path': 'testfile'}
ret = gitfs.file_hash(tmp_load, fnd)
self.assertDictEqual({'hash_type': 'blob_sha1', 'hsum': '0d234303e6451128d756c5c259175de37d767742'}, ret)
self.assertDictEqual({'hash_type': 'sha1', 'hsum': '6b18d04b61238ba13b5e4626b13ac5fb7432b5e2'}, ret)
def test_serve_file(self):
with patch.dict(gitfs.__opts__, {'cachedir': self.master_opts['cachedir'],

1273
tests/jenkins-ng.py Executable file

File diff suppressed because it is too large Load Diff

5
zeromq-requirements.txt Normal file
View File

@ -0,0 +1,5 @@
-r _requirements.txt
M2Crypto
pycrypto
pyzmq >= 2.2.0