Merge branch '2016.3' into 'develop'

Conflicts:
  - doc/conf.py
  - salt/minion.py
  - salt/modules/mac_brew.py
  - salt/modules/pkgng.py
  - salt/states/apache_module.py
  - salt/states/ini_manage.py
  - salt/states/win_servermanager.py
This commit is contained in:
rallytime 2016-06-02 10:13:34 -04:00
commit 9220ea0729
63 changed files with 1172 additions and 509 deletions

View file

@ -386,6 +386,10 @@
#include:
# - /etc/salt/extra_config
# - /etc/roles/webserver
# The syndic minion can verify that it is talking to the correct master via the
# key fingerprint of the higher-level master with the "syndic_finger" config.
#syndic_finger: ''
#
#
#

View file

@ -74,7 +74,7 @@ $( document ).ready(function() {
var $target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top
'scrollTop': $target.offset().top + 200
}, 900, 'swing', function () {
window.location.hash = target;
});
@ -89,7 +89,7 @@ $( document ).ready(function() {
var $target = $('dt[id="' + target + '"]');
$('html, body').stop().animate({
'scrollTop': $target.offset().top
'scrollTop': $target.offset().top + 200
}, 900, 'swing', function () {
window.location.hash = target;
});
@ -207,3 +207,70 @@ function getMetaStatus() {
}
}
}
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 60,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollIfAnchor(location.hash);
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
anchorOffset = $(match).offset().top - this.getFixedOffset();
$('html, body').animate({ scrollTop: anchorOffset});
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(this.scrollIfAnchor(elem.getAttribute('href'), true)) {
e.preventDefault();
}
}
};
$(document).ready($.proxy(anchorScrolls, 'init'));
})(window.document, window.history, window.location);

View file

@ -18,6 +18,7 @@ returners
roster
runners
sdb
thorium
serializers
states
tops

View file

@ -279,11 +279,11 @@ rst_prolog = """\
.. _`salt-packagers`: https://groups.google.com/forum/#!forum/salt-packagers
.. |windownload| raw:: html
<p>x86: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-2-x86-Setup.exe"><strong>Salt-Minion-{release}-2-x86-Setup.exe</strong></a>
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-2-x86-Setup.exe.md5"><strong>md5</strong></a></p>
<p>x86: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-x86-Setup.exe"><strong>Salt-Minion-{release}-x86-Setup.exe</strong></a>
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-x86-Setup.exe.md5"><strong>md5</strong></a></p>
<p>AMD64: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-2-AMD64-Setup.exe"><strong>Salt-Minion-{release}-2-AMD64-Setup.exe</strong></a>
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-2-AMD64-Setup.exe.md5"><strong>md5</strong></a></p>
<p>AMD64: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-AMD64-Setup.exe"><strong>Salt-Minion-{release}-AMD64-Setup.exe</strong></a>
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-AMD64-Setup.exe.md5"><strong>md5</strong></a></p>
""".format(release=release)

View file

@ -5,7 +5,6 @@ Salt Table of Contents
.. toctree::
:maxdepth: 2
index
topics/installation/index
topics/configuration/index
topics/using_salt

View file

@ -1,114 +0,0 @@
.. _get-started:
=========
SaltStack
=========
Salt, a new approach to infrastructure management, is easy enough to get
running in minutes, scalable enough to manage tens of thousands of servers,
and fast enough to communicate with those servers in *seconds*.
Salt delivers a dynamic communication bus for infrastructures that can be used
for orchestration, remote execution, configuration management and much more.
.. toctree::
:maxdepth: 1
topics/index
Get Started
===========
The Get Started Guide shows you how to:
* Install and configure SaltStack
* Remotely execute commands across all managed systems
* Design, develop, and deploy system configurations
.. toctree::
:maxdepth: 1
Get Started Guide <https://docs.saltstack.com/en/getstarted/>
If you just want to get Salt installed and start using it, *Salt in 10 minutes*
gets you up and running quickly.
.. toctree::
:maxdepth: 1
topics/tutorials/walkthrough
Install Salt
============
**Latest Stable Release**: |current_release_doc|
The installation document, found in the following link, outlines where to
obtain packages and installation specifics for platforms:
* :ref:`Installation <installation>`
The Salt Bootstrap project, found in the following repository, is a single
shell script, which automates the install correctly on multiple platforms:
* https://github.com/saltstack/salt-bootstrap
Demo Environments
=================
You can download one of the following `Vagrant <http://vagrantup.com>`_
projects to quickly set up a Salt demo environment:
- https://github.com/UtahDave/salt-vagrant-demo
- https://github.com/UtahDave/salt-vagrant-lxc
Example Formulas
================
A Github repo that contains a number of community-maintained formulas is
available at https://github.com/saltstack-formulas. Contributions are welcome!
A Github repo that contains formulas to install a number of Windows
applications is available at https://github.com/saltstack/salt-winrepo-ng. Note
that Salt makes this repo :ref:`available <windows-package-manager>` to your
Windows minions, and contributions are welcome!
Mailing List
============
Join the `salt-users mailing list`_. It is the best place to ask questions
about Salt and see whats going on with Salt development! The Salt mailing list
is hosted by Google Groups. It is open to new members.
https://groups.google.com/forum/#!forum/salt-users
.. _`salt-users mailing list`: https://groups.google.com/forum/#!forum/salt-users
There is also a low-traffic list used to announce new releases
called `salt-announce`_
https://groups.google.com/forum/#!forum/salt-announce
.. _`salt-announce`: https://groups.google.com/forum/#!forum/salt-announce
IRC
===
The ``#salt`` IRC channel is hosted on the popular `Freenode`__ network. You
can use the `Freenode webchat client`__ right from your browser.
`Logs of the IRC channel activity`__ are being collected courtesy of Moritz Lenz.
.. __: http://freenode.net/irc_servers.shtml
.. __: http://webchat.freenode.net/?channels=salt&uio=Mj10cnVlJjk9dHJ1ZSYxMD10cnVl83
.. __: http://irclog.perlgeek.de/salt/
If you wish to discuss the development of Salt itself join us in
``#salt-devel``.
Follow on GitHub
================
The Salt code is developed via GitHub. Follow Salt for constant updates on what
is happening in Salt development:
|saltrepo|
Hack the Source
===============
If you want to get involved with the development of source code or the
documentation efforts, please review the :ref:`Developing Salt Tutorial
<developing-tutorial>`.

View file

@ -2296,6 +2296,26 @@ configuration is the same as :conf_master:`file_roots`:
prod:
- /srv/pillar/prod
.. conf_master:: pillar_opts
``pillar_opts``
---------------
Default: ``False``
The ``pillar_opts`` option adds the master configuration file data to a dict in
the pillar called ``master``. This can be used to set simple configurations in
the master config file that can then be used on minions.
Note that setting this option to ``True`` means the master config file will be
included in all minion's pillars. While this makes global configuration of services
and systems easy, it may not be desired if sensitive data is stored in the master
configuration.
.. code-block:: yaml
pillar_opts: False
.. _master-configuration-ext-pillar:
.. conf_master:: ext_pillar

View file

@ -942,6 +942,20 @@ what you are doing! Transports are explained in :ref:`Salt Transports
transport: zeromq
.. conf_minion:: syndic_finger
``syndic_finger``
-----------------
Default: ``''``
The key fingerprint of the higher-level master for the syndic to verify it is
talking to the intended master.
.. code-block:: yaml
syndic_finger: 'ab:30:65:2a:d6:9e:20:4f:d8:b2:f3:a7:d4:65:50:10'
Minion Module Management
========================

View file

@ -341,3 +341,13 @@ string with quotes:
ValueError: month must be in 1..12
>>> yaml.safe_load('"4017-16-20"')
'4017-16-20'
Keys Limited to 1024 Characters
===============================
Simple keys are limited to a single line and cannot be longer that 1024 characters.
This is a limitation from PyYaml, as seen in a comment in `PyYAML's code`_, and
applies to anything parsed by YAML in Salt.
.. _PyYAML's code: http://pyyaml.org/browser/pyyaml/trunk/lib/yaml/scanner.py#L91

View file

@ -59,7 +59,7 @@ to set up the libvirt pki keys.
- contents: 'LIBVIRTD_ARGS="--listen"'
- require:
- pkg: libvirt
libvirt.keys:
virt.keys:
- require:
- pkg: libvirt
service.running:
@ -139,7 +139,7 @@ date:
.. code-block:: yaml
libvirt_keys:
libvirt.keys
virt.keys
Getting Virtual Machine Images Ready
====================================

View file

@ -17,6 +17,20 @@ things:
Otherwise, it will attempt to connect to a master and fail. The salt-call
command stands on its own and does not need the salt-minion daemon.
Minion Configuration
--------------------
Throughout this document there are several references to setting different
options to configure a masterless Minion. Salt Minions are easy to configure
via a configuration file that is located, by default, in ``/etc/salt/minion``.
Note, however, that on FreeBSD systems, the minion configuration file is located
in ``/usr/local/etc/salt/minion``.
You can learn more about minion configuration options in the
:ref:`Configuring the Salt Minion <configuration-salt-minion>` docs.
Telling Salt Call to Run Masterless
===================================
@ -39,7 +53,6 @@ Now the salt-call command will not look for a master and will assume that the
local system has all of the file and pillar resources.
Running States Masterless
=========================
@ -81,6 +94,7 @@ it unnecessary to change the configuration file:
salt-call state.apply --local
External Pillars
================

View file

@ -52,6 +52,8 @@ else
PKGDIR=$2
fi
CPUARCH=`uname -m`
############################################################################
# Additional Parameters Required for the script to function properly
############################################################################
@ -140,7 +142,7 @@ cp $SRCDIR/conf/minion $PKGDIR/etc/salt/minion.dist
cp $SRCDIR/conf/master $PKGDIR/etc/salt/master.dist
############################################################################
# Add Version to distribution.xml
# Add Version and CPU Arch to distribution.xml
############################################################################
echo -n -e "\033]0;Build_Pkg: Add Version to .xml\007"
@ -150,6 +152,10 @@ SEDSTR="s/@VERSION@/$VERSION/"
echo $SEDSTR
sed -i '' $SEDSTR distribution.xml
SEDSTR="s/@CPUARCH@/$CPUARCH/"
echo $SEDSTR
sed -i '' $SEDSTR distribution.xml
############################################################################
# Build the Package
############################################################################
@ -159,10 +165,10 @@ pkgbuild --root $PKGDIR \
--scripts $PKGDIR/scripts \
--identifier=com.saltstack.salt \
--version=$VERSION \
--ownership=recommended salt-src-$VERSION.pkg
--ownership=recommended salt-src-$VERSION-$CPUARCH.pkg
productbuild --resources=$PKGDIR/resources \
--distribution=distribution.xml \
--package-path=salt-src-$VERSION.pkg \
--version=$VERSION salt-$VERSION.pkg
--package-path=salt-src-$VERSION-$CPUARCH.pkg \
--version=$VERSION salt-$VERSION-$CPUARCH.pkg

View file

@ -9,7 +9,7 @@
</allowed-os-versions>
</volume-check>
<options rootVolumeOnly="true"
hostArchitectures="x86_64" />
hostArchitectures="@CPUARCH@" />
<domains enable_localSystem="true" />
<!-- Define background image -->
<background file="saltstack.png"
@ -25,7 +25,7 @@
<!-- List all component packages -->
<pkg-ref id="com.saltstack.salt"
version="@VERSION@"
auth="root">salt-src-@VERSION@.pkg</pkg-ref>
auth="root">salt-src-@VERSION@-@CPUARCH@.pkg</pkg-ref>
<!-- List them again here. They can now be organized
as a hierarchy if you want. -->
<choices-outline>

View file

@ -6,58 +6,50 @@ However it does document pretty thoroughly how I initially created a build envir
for packaging up esky builds for SmartOS
```bash
#!/bin/bash
#!/bin/sh
set -ux
export PATH=$PATH:/opt/local/gcc47/bin/
HERE=$(pwd)
## environment
export PATH=$PATH:/opt/local/gcc49/bin/
BLDPATH=/tmp/bbfreeze_loader
SALTBASE=/data
mv /opt/local /opt/local.backup ; hash -r
cd /
curl http://pkgsrc.joyent.com/packages/SmartOS/bootstrap/bootstrap-2014Q4-x86_64.tar.gz | gtar xz
hash -r
rm -rf /var/db/pkgin/
pkgin -y up
## packages
pkgin -y in build-essential salt swig py27-pip unzip py27-mysqldb libsodium mysql-client patchelf
pkgin -y rm salt py27-zmq
pip install --egg esky bbfreeze
pip install --no-use-wheel --egg esky bbfreeze
cd $HERE
## bzfreeze-loader
COMPILE="gcc -fno-strict-aliasing -O2 -pipe -O2 -DHAVE_DB_185_H -I/usr/include -I/opt/local/include -I/opt/local/include/db4 -I/opt/local/include/gettext -I/opt/local/include/ncurses -DNDEBUG -O2 -pipe -O2 -DHAVE_DB_185_H -I/usr/include -I/opt/local/include -I/opt/local/include/db4 -I/opt/local/include/gettext -I/opt/local/include/ncurses -fPIC -I/opt/local/include/python2.7 -static-libgcc"
LINK_OPTS="-L/opt/local/lib -L/opt/local/lib/python2.7/config -L/opt/local/lib -lsocket -lnsl -ldl -lrt -lm -static-libgcc"
mkdir -p ${BLDPATH}
cd ${BLDPATH}
curl -kO 'https://pypi.python.org/packages/source/b/bbfreeze-loader/bbfreeze-loader-1.1.0.zip'
unzip bbfreeze-loader-1.1.0.zip
${COMPILE} -c bbfreeze-loader-1.1.0/_bbfreeze_loader/console.c -o ${BLDPATH}/console.o
${COMPILE} -c bbfreeze-loader-1.1.0/_bbfreeze_loader/getpath.c -o ${BLDPATH}/getpath.o
gcc ${BLDPATH}/console.o ${BLDPATH}/getpath.o /opt/local/lib/python2.7/config/libpython2.7.a ${LINK_OPTS} -o ${BLDPATH}/console.exe
patchelf --set-rpath '$ORIGIN:$ORIGIN/../lib' ${BLDPATH}/console.exe
find /opt/local -name console.exe -exec cp ${BLDPATH}/console.exe {} \;
COMPILE="gcc -fno-strict-aliasing -O2 -pipe -O2 -DHAVE_DB_185_H -I/usr/include -I/opt/local/include -I/opt/local/include/db4 -I/opt/local/include/gettext -I/opt/local/include/ncurses -DNDEBUG -O2 -pipe -O2 -DHAVE_DB_185_H -I/usr/include -I/opt/local/include -I/opt/local/include/db4 -I/opt/local/include/gettext -I/opt/local/include/ncurses -fPIC -I/opt/local/include/python2.7 -static-libgcc"
$COMPILE -c bbfreeze-loader-1.1.0/_bbfreeze_loader/console.c -o $HERE/console.o
$COMPILE -c bbfreeze-loader-1.1.0/_bbfreeze_loader/getpath.c -o $HERE/getpath.o
gcc $HERE/console.o $HERE/getpath.o /opt/local/lib/python2.7/config/libpython2.7.a -L/opt/local/lib -L/opt/local/lib/python2.7/config -L/opt/local/lib -lsocket -lnsl -ldl -lrt -lm -static-libgcc -o $HERE/console.exe
patchelf --set-rpath '$ORIGIN:$ORIGIN/../lib' $HERE/console.exe
## clone saltstack repo
cd ${SALTBASE}
git clone git://github.com/saltstack/salt -b 2016.3
find /opt/local -name console.exe -exec mv $HERE/console.exe {} \;
## salt requirements
cd ${SALTBASE}/salt
until pip install --no-use-wheel --egg -r pkg/smartos/esky/zeromq_requirements.txt ; do sleep 1 ; done ;
until pip install --no-use-wheel --egg -r pkg/smartos/esky/raet_requirements.txt ; do sleep 1 ; done ;
git clone git://github.com/saltstack/salt -b 2014.7
cd $HERE/salt
# install all requirements
# (installing them as eggs seems to trigger esky pulling in the whole egg)
# this step is buggy... I had to run them repeatedly until they succeeded...
until pip install --egg -r pkg/smartos/esky/zeromq_requirements.txt ; do sleep 1 ; done ;
until pip install --egg -r pkg/smartos/esky/raet_requirements.txt ; do sleep 1 ; done ;
# install the sodium_grabber library
## sodium grabber
cd ${SALTBASE}/salt
python2.7 pkg/smartos/esky/sodium_grabber_installer.py install
# ugly workaround for odd zeromq linking breakage
cp /opt/local/lib/libzmq.so.4 /opt/local/lib/python2.7/site-packages/pyzmq-13.1.0-py2.7-solaris-2.11-i86pc.64bit.egg/zmq/
patchelf --set-rpath '$ORIGIN:$ORIGIN/../lib' /opt/local/lib/python2.7/site-packages/pyzmq-13.1.0-py2.7-solaris-2.11-i86pc.64bit.egg/zmq/libzmq.so.4
cp /opt/local/lib/libsodium.so.13 /opt/local/lib/python2.7/site-packages/pyzmq-13.1.0-py2.7-solaris-2.11-i86pc.64bit.egg/zmq/
patchelf --set-rpath '$ORIGIN:$ORIGIN/../lib' /opt/local/lib/python2.7/site-packages/pyzmq-13.1.0-py2.7-solaris-2.11-i86pc.64bit.egg/zmq/libsodium.so.13
## cleanup
rm -r ${BLDPATH}
# at this point you have a build environment that you could set aside and reuse to run further builds.
bash pkg/smartos/esky/build-tarball.sh
# Upload packages into Manta
#mmkdir -p /$MANTA_USER/public/salt
#for file in dist/salt*; do mput -m /$MANTA_USER/public/salt -f $file; done;
## build esky package
cd ${SALTBASE}/salt
pkg/smartos/esky/build-tarball.sh
```

View file

@ -24,5 +24,9 @@ chmod +x $BUILD_DIR/install/install.sh
unzip -d $BUILD_DIR/bin dist/*.zip
cp $BUILD_DIR/bin/*/libgcc_s.so.1 $BUILD_DIR/bin/
find build/output/salt/bin/ -mindepth 1 -maxdepth 1 -type d -not -name appdata -exec mv {} $BUILD_DIR/bin/appdata/ \;
PYZMQ=$(find ${BUILD_DIR}/bin/ -mindepth 1 -type d -name 'pyzmq-*.egg')/zmq
find /opt/local/lib/ -maxdepth 1 -type l -regextype sed -regex '.*/libzmq.so.[0-9]\+$' -exec cp {} ${PYZMQ}/ \;
find /opt/local/lib/ -maxdepth 1 -type l -regextype sed -regex '.*/libsodium.so.[0-9]\+$' -exec cp {} ${PYZMQ}/ \;
find ${PYZMQ}/ -maxdepth 1 -type f -name '*.so.*' -exec patchelf --set-rpath '$ORIGIN:$ORIGIN/../../:$ORIGIN/../lib' {} \;
gtar -C $BUILD_DIR/.. -czvf dist/salt-$(awk '/^Version:/{print $2}' < PKG-INFO)-esky-smartos.tar.gz salt
echo "tarball built"

View file

@ -2,5 +2,5 @@
#-r ../../../requirements/zeromq.txt
-r ../../../requirements/base.txt
pycrypto>=2.6.1
pyzmq == 13.1.0
pyzmq
-r requirements.txt

View file

@ -55,7 +55,7 @@ RETVAL=0
start() {
echo -n $"Starting salt-minion daemon: "
if [ -f $SUSE_RELEASE ]; then
startproc -f -p /var/run/$SERVICE.pid $SALTMINION -d $MINION_ARGS
startproc -p /var/run/$SERVICE.pid $SALTMINION -d $MINION_ARGS
rc_status -v
elif [ -e $DEBIAN_VERSION ]; then
if [ -f $LOCKFILE ]; then

View file

@ -26,10 +26,10 @@ if [%1]==[] (
)
:: Create Build Environment
cmd /c powershell -ExecutionPolicy RemoteSigned -File "%CurDir%build_env.ps1" -Silent
PowerShell.exe -ExecutionPolicy RemoteSigned -File "%CurDir%build_env.ps1" -Silent
:: Install Current Version of salt
cmd /c "%PyDir%\python.exe %SrcDir%\setup.py" install --force
"%PyDir%\python.exe" "%SrcDir%\setup.py" install --force
:: Build the Salt Package
call "%CurDir%build_pkg.bat" "%Version%"

View file

@ -257,16 +257,6 @@ $p = Start-Process "$($ini['Settings']['ScriptsDir'])\pip.exe" -ArgumentList "in
Write-Output " ----------------------------------------------------------------"
Write-Output " - Copying DLLs . . ."
Write-Output " ----------------------------------------------------------------"
ForEach ($key in $ini['CommonDLLs'].Keys) {
If ($arrInstalled -notcontains $key) {
Write-Output " - $key . . ."
$file = "$($ini['CommonDLLs'][$key])"
$url = "$($ini['Settings']['SaltRepo'])/$file"
$file = "$($ini['Settings']['PythonDir'])\$file"
DownloadFileWithProgress $url $file
}
}
# Architecture Specific DLL's
ForEach($key in $ini[$bitDLLs].Keys) {
If ($arrInstalled -notcontains $key) {

View file

@ -58,17 +58,12 @@ Function Get-Settings {
}
$ini.Add("32bitPrograms", $32bitPrograms)
# CPU Architecture Independent DLL's
$CommonDLLs = @{
"libsodium" = "libsodium-13.dll"
}
$ini.Add("CommonDLLs", $CommonDLLs)
# DLL's for 64 bit Windows
$64bitDLLs = @{
"Libeay" = "libeay32.dll"
"SSLeay" = "ssleay32.dll"
"OpenSSLLic" = "OpenSSL_License.txt"
"libsodium" = "libsodium.dll"
}
$ini.Add("64bitDLLs", $64bitDLLs)
@ -77,6 +72,7 @@ Function Get-Settings {
"Libeay" = "libeay32.dll"
"SSLeay" = "ssleay32.dll"
"OpenSSLLic" = "OpenSSL_License.txt"
"libsodium" = "libsodium.dll"
}
$ini.Add("32bitDLLs", $32bitDLLs)

View file

@ -12,7 +12,7 @@ ioflo==1.5.3
ioloop==0.1a0
ipaddress==1.0.16
Jinja2==2.8
libnacl==1.4.4
libnacl==1.4.5
Mako==1.0.4
MarkupSafe==0.23
msgpack-python==0.4.7

View file

@ -1,2 +1,2 @@
pip==8.1.1
setuptools==21.0.0
pip==8.1.2
setuptools==21.2.1

View file

@ -117,6 +117,10 @@ try:
PAM_AUTHENTICATE.restype = c_int
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
PAM_ACCT_MGMT = LIBPAM.pam_acct_mgmt
PAM_ACCT_MGMT.restype = c_int
PAM_ACCT_MGMT.argtypes = [PamHandle, c_int]
PAM_END = LIBPAM.pam_end
PAM_END.restype = c_int
PAM_END.argtypes = [PamHandle, c_int]
@ -171,6 +175,8 @@ def authenticate(username, password):
return False
retval = PAM_AUTHENTICATE(handle, 0)
if retval == 0:
PAM_ACCT_MGMT(handle, 0)
PAM_END(handle, 0)
return retval == 0

View file

@ -68,8 +68,7 @@ def beacon(config):
'''
ret = []
for diskusage in config:
mount = diskusage.keys()[0]
for mount in config:
try:
_current_usage = psutil.disk_usage(mount)
@ -79,7 +78,7 @@ def beacon(config):
continue
current_usage = _current_usage.percent
monitor_usage = diskusage[mount]
monitor_usage = config[mount]
if '%' in monitor_usage:
monitor_usage = re.sub('%', '', monitor_usage)
monitor_usage = float(monitor_usage)

View file

@ -2956,7 +2956,12 @@ def apply_minion_config(overrides=None,
# Enabling open mode requires that the value be set to True, and
# nothing else!
opts['open_mode'] = opts['open_mode'] is True
# Make sure ext_mods gets set if it is an untrue value
# (here to catch older bad configs)
opts['extension_modules'] = (
opts.get('extension_modules') or
os.path.join(opts['cachedir'], 'extmods')
)
# Set up the utils_dirs location from the extension_modules location
opts['utils_dirs'] = (
opts.get('utils_dirs') or

View file

@ -11,7 +11,6 @@ import re
# Import salt libs
import salt.utils
import salt.utils.decorators as decorators
# Solve the Chicken and egg problem where grains need to run before any
# of the modules are loaded and are generally available for any usage.
@ -29,91 +28,94 @@ def disks():
Return list of disk devices
'''
if salt.utils.is_freebsd():
return _freebsd_disks()
return _freebsd_geom()
elif salt.utils.is_linux():
return _linux_disks()
else:
log.trace('Disk grain does not support OS')
def _clean_keys(key):
key = key.replace(' ', '_')
key = key.replace('(', '')
key = key.replace(')', '')
return key
class _geomconsts(object):
GEOMNAME = 'Geom name'
MEDIASIZE = 'Mediasize'
SECTORSIZE = 'Sectorsize'
STRIPESIZE = 'Stripesize'
STRIPEOFFSET = 'Stripeoffset'
DESCR = 'descr' # model
LUNID = 'lunid'
LUNNAME = 'lunname'
IDENT = 'ident' # serial
ROTATIONRATE = 'rotationrate' # RPM or 0 for non-rotating
# Preserve the API where possible with Salt < 2016.3
_aliases = {
DESCR: 'device_model',
IDENT: 'serial_number',
ROTATIONRATE: 'media_RPM',
LUNID: 'WWN',
}
_datatypes = {
MEDIASIZE: ('re_int', r'(\d+)'),
SECTORSIZE: 'try_int',
STRIPESIZE: 'try_int',
STRIPEOFFSET: 'try_int',
ROTATIONRATE: 'try_int',
}
class _camconsts(object):
PROTOCOL = 'protocol'
DEVICE_MODEL = 'device model'
FIRMWARE_REVISION = 'firmware revision'
SERIAL_NUMBER = 'serial number'
WWN = 'WWN'
SECTOR_SIZE = 'sector size'
MEDIA_RPM = 'media RPM'
_identify_attribs = [_camconsts.__dict__[key] for key in
_camconsts.__dict__ if not key.startswith('__')]
def _datavalue(datatype, data):
if datatype == 'try_int':
try:
return int(data)
except ValueError:
return None
elif datatype is tuple and datatype[0] == 're_int':
search = re.search(datatype[1], data)
if search:
try:
return int(search.group(1))
except ValueError:
return None
return None
else:
return data
@decorators.memoize
def _freebsd_vbox():
# Don't tickle VirtualBox storage emulation bugs
camcontrol = salt.utils.which('camcontrol')
devlist = __salt__['cmd.run']('{0} devlist'.format(camcontrol))
if 'VBOX' in devlist:
return True
return False
_geom_attribs = [_geomconsts.__dict__[key] for key in
_geomconsts.__dict__ if not key.startswith('_')]
def _freebsd_disks():
def _freebsd_geom():
geom = salt.utils.which('geom')
ret = {'disks': {}, 'SSDs': []}
sysctl = salt.utils.which('sysctl')
devices = __salt__['cmd.run']('{0} -n kern.disks'.format(sysctl))
SSD_TOKEN = 'non-rotating'
for device in devices.split(' '):
if device.startswith('cd'):
log.debug('Disk grain skipping cd')
elif _freebsd_vbox():
log.debug('Disk grain skipping CAM identify/inquirty on VBOX')
ret['disks'][device] = {}
else:
cam = _freebsd_camcontrol(device)
ret['disks'][device] = cam
if cam.get(_clean_keys(_camconsts.MEDIA_RPM)) == SSD_TOKEN:
ret['SSDs'].append(device)
devices = __salt__['cmd.run']('{0} disk list'.format(geom))
devices = devices.split('\n\n')
return ret
def parse_geom_attribs(device):
tmp = {}
for line in device.split('\n'):
for attrib in _geom_attribs:
search = re.search(r'{0}:\s(.*)'.format(attrib), line)
if search:
value = _datavalue(_geomconsts._datatypes.get(attrib),
search.group(1))
tmp[attrib] = value
if attrib in _geomconsts._aliases:
tmp[_geomconsts._aliases[attrib]] = value
name = tmp.pop(_geomconsts.GEOMNAME)
if name.startswith('cd'):
return
def _freebsd_camcontrol(device):
camcontrol = salt.utils.which('camcontrol')
ret = {}
ret['disks'][name] = tmp
if tmp[_geomconsts.ROTATIONRATE] == 0:
log.trace('Device {0} reports itself as an SSD'.format(device))
ret['SSDs'].append(name)
def parse_identify_attribs(line):
for attrib in _identify_attribs:
search = re.search(r'^{0}\s+(.*)'.format(attrib), line)
if search:
ret[_clean_keys(attrib)] = search.group(1)
identify = __salt__['cmd.run']('{0} identify {1}'.format(camcontrol,
device))
for line in identify.splitlines():
parse_identify_attribs(line)
def parse_inquiry(inquiry):
if not ret.get(_clean_keys(_camconsts.DEVICE_MODEL)):
model = re.search(r'\s<(.+?)>', inquiry)
if model:
ret[_clean_keys(_camconsts.DEVICE_MODEL)] = model.group(1)
if not ret.get(_clean_keys(_camconsts.SERIAL_NUMBER)):
sn = re.search(r'\sSerial Number\s+(\w+)\s', inquiry)
if sn:
ret[_clean_keys(_camconsts.SERIAL_NUMBER)] = sn.group(1)
inquiry = __salt__['cmd.run']('{0} inquiry {1}'.format(camcontrol, device))
parse_inquiry(inquiry)
for device in devices:
parse_geom_attribs(device)
return ret

View file

@ -851,7 +851,7 @@ class Minion(MinionBase):
# module. If this is a proxy, however, we need to init the proxymodule
# before we can get the grains. We do this for proxies in the
# post_master_init
if not salt.utils.is_proxy():
if not salt.utils.is_proxy() and not self.opts.get('grains'):
self.opts['grains'] = salt.loader.grains(opts)
log.info('Creating minion process manager')
@ -1042,7 +1042,7 @@ class Minion(MinionBase):
mod_opts[key] = val
return mod_opts
def _load_modules(self, force_refresh=False, notify=False):
def _load_modules(self, force_refresh=False, notify=False, grains=None):
'''
Return the functions and the returners loaded up from the loader
module
@ -1070,7 +1070,8 @@ class Minion(MinionBase):
else:
proxy = None
self.opts['grains'] = salt.loader.grains(self.opts, force_refresh, proxy=proxy)
if grains is None:
self.opts['grains'] = salt.loader.grains(self.opts, force_refresh, proxy=proxy)
self.utils = salt.loader.utils(self.opts)
if self.opts.get('multimaster', False):
@ -1220,7 +1221,7 @@ class Minion(MinionBase):
minion_instance.connected = connected
if not hasattr(minion_instance, 'functions'):
functions, returners, function_errors, executors = (
minion_instance._load_modules()
minion_instance._load_modules(grains=opts['grains'])
)
minion_instance.functions = functions
minion_instance.returners = returners

View file

@ -4,8 +4,11 @@ Execution module to work with etcd
:depends: - python-etcd
In order to use an etcd server, a profile should be created in the master
configuration file:
Configuration
-------------
To work with an etcd server you must configure an etcd profile. The etcd config
can be set in either the Salt Minion configuration file or in pillar:
.. code-block:: yaml
@ -21,6 +24,17 @@ or clusters are available.
etcd.host: 127.0.0.1
etcd.port: 4001
.. note::
The etcd configuration can also be set in the Salt Master config file,
but in order to use any etcd configurations defined in the Salt Master
config, the :conf_master:`pillar_opts` must be set to ``True``.
Be aware that setting ``pillar_opts`` to ``True`` has security implications
as this makes all master configuration settings available in all minion's
pillars.
'''
from __future__ import absolute_import

View file

@ -448,6 +448,12 @@ def image_schema(profile=None):
'''
Returns names and descriptions of the schema "image"'s
properties for this profile's instance of glance
CLI Example:
.. code-block:: bash
salt '*' glance.image_schema
'''
return schema_get('image', profile)
@ -459,6 +465,13 @@ def image_update(id=None, name=None, profile=None, **kwargs): # pylint: disable
- min_ram (in MB)
- protected (bool)
- visibility ('public' or 'private')
CLI Example:
.. code-block:: bash
salt '*' glance.image_update id=c2eb2eb0-53e1-4a80-b990-8ec887eae7df
salt '*' glance.image_update name=f16-jeos
'''
if id:
image = image_show(id=id, profile=profile)
@ -512,6 +525,12 @@ def schema_get(name, profile=None):
- images
- member
- members
CLI Example:
.. code-block:: bash
salt '*' glance.schema_get name=f16-jeos
'''
g_client = _auth(profile)
pformat = pprint.PrettyPrinter(indent=4).pformat

View file

@ -21,6 +21,9 @@ from salt.exceptions import CommandExecutionError, MinionError
import salt.ext.six as six
from salt.ext.six.moves import zip
# Import third party libs
import json
log = logging.getLogger(__name__)
# Define the module's virtual name
@ -54,7 +57,9 @@ def _tap(tap, runas=None):
return True
cmd = 'brew tap {0}'.format(tap)
if _call_brew(cmd)['retcode']:
try:
_call_brew(cmd)
except CommandExecutionError:
log.error('Failed to tap "{0}"'.format(tap))
return False
@ -76,11 +81,18 @@ def _call_brew(cmd, redirect_stderr=False):
'''
user = __salt__['file.get_user'](_homebrew_bin())
runas = user if user != __opts__['user'] else None
return __salt__['cmd.run_all'](cmd,
runas=runas,
output_loglevel='trace',
python_shell=False,
redirect_stderr=redirect_stderr)
ret = __salt__['cmd.run_all'](cmd,
runas=runas,
output_loglevel='trace',
python_shell=False,
redirect_stderr=redirect_stderr)
if ret['retcode'] != 0:
raise CommandExecutionError(
'stdout: {stdout}\n'
'stderr: {stderr}\n'
'retcode: {retcode}\n'.format(**ret)
)
return ret
def list_pkgs(versions_as_list=False, **kwargs):
@ -161,7 +173,6 @@ def latest_version(*names, **kwargs):
salt '*' pkg.latest_version <package1> <package2> <package3>
'''
refresh = salt.utils.is_true(kwargs.pop('refresh', True))
if refresh:
refresh_db()
@ -403,20 +414,20 @@ def list_upgrades(refresh=True):
if refresh:
refresh_db()
cmd = 'brew outdated'
call = _call_brew(cmd)
if call['retcode'] != 0:
comment = ''
if 'stderr' in call:
comment += call['stderr']
if 'stdout' in call:
comment += call['stdout']
raise CommandExecutionError(
'{0}'.format(comment)
)
else:
out = call['stdout']
return out.splitlines()
res = _call_brew(['brew', 'outdated', '--json=v1'])
ret = {}
try:
data = json.loads(res['stdout'])
except ValueError as err:
msg = 'unable to interpret output from "brew outdated": {0}'.format(err)
log.error(msg)
raise CommandExecutionError(msg)
for pkg in data:
# current means latest available to brew
ret[pkg['name']] = pkg['current_version']
return ret
def upgrade_available(pkg):
@ -473,3 +484,49 @@ def upgrade(refresh=True):
ret['changes'] = salt.utils.compare_dicts(old, new)
return ret
def _info(*names):
'''
Return the information of the named package(s)
.. versionadded:: 2016.3.1
names
The names of the packages for which to return information.
'''
cmd = ['brew', 'info', '--json=v1']
cmd.extend(names)
res = _call_brew(cmd)
ret = {}
try:
data = json.loads(res['stdout'])
except ValueError as err:
msg = 'unable to interpret output from "brew info": {0}'.format(err)
log.error(msg)
raise CommandExecutionError(msg)
for pkg in data:
ret[pkg['name']] = pkg
return ret
def info_installed(*names):
'''
Return the information of the named package(s) installed on the system.
.. versionadded:: 2016.3.1
names
The names of the packages for which to return information.
CLI example:
.. code-block:: bash
salt '*' pkg.info_installed <package1>
salt '*' pkg.info_installed <package1> <package2> <package3> ...
'''
return _info(*names)

View file

@ -562,6 +562,12 @@ def list_(profile=None):
'''
To maintain the feel of the nova command line, this function simply calls
the server_list function.
CLI Example:
.. code-block:: bash
salt '*' nova.list
'''
return server_list(profile=profile)

View file

@ -99,7 +99,8 @@ def _get_pkgng_version(jail=None, chroot=None, root=None):
'''
return the version of 'pkg'
'''
return __salt__['cmd.run']([_pkg(jail, chroot, root), '--version']).strip()
cmd = _pkg(jail, chroot, root) + ['--version']
return __salt__['cmd.run'](cmd).strip()
def _get_version(name, results):
@ -300,9 +301,9 @@ def latest_version(*names, **kwargs):
for name in names:
# FreeBSD supports packages in format java/openjdk7
if '/' in name:
cmd = [_pkg(jail, chroot, root), 'search']
cmd = _pkg(jail, chroot, root) + ['search']
else:
cmd = [_pkg(jail, chroot, root), 'search', '-S', 'name', '-Q', 'version', '-e']
cmd = _pkg(jail, chroot, root) + ['search', '-S', 'name', '-Q', 'version', '-e']
if quiet:
cmd.append('-q')
cmd.append(name)

View file

@ -346,6 +346,27 @@ def psql_query(query, user=None, host=None, port=None, maintenance_db=None,
WITH updated AS (UPDATE pg_authid SET rolconnlimit = 2000 WHERE
rolname = 'rolename' RETURNING rolconnlimit) SELECT * FROM updated;
query
The query string.
user
Database username, if different from config or default.
host
Database host, if different from config or default.
port
Database port, if different from the config or default.
maintenance_db
The database to run the query against.
password
User password, if different from the config or default.
runas
User to run the command as.
CLI Example:
.. code-block:: bash

View file

@ -639,3 +639,29 @@ def version_cmp(ver1, ver2):
log.warning("Failed to compare version '{0}' to '{1}' using RPM: {2}".format(ver1, ver2, exc))
return salt.utils.version_cmp(ver1, ver2)
def checksum(*paths):
'''
Return if the signature of a RPM file is valid.
CLI Example:
.. code-block:: bash
salt '*' lowpkg.checksum /path/to/package1.rpm
salt '*' lowpkg.checksum /path/to/package1.rpm /path/to/package2.rpm
'''
ret = dict()
if not paths:
raise CommandExecutionError("No package files has been specified.")
for package_file in paths:
ret[package_file] = (bool(__salt__['file.file_exists'](package_file)) and
not __salt__['cmd.retcode'](["rpm", "-K", "--quiet", package_file],
ignore_retcode=True,
output_loglevel='trace',
python_shell=False))
return ret

View file

@ -52,7 +52,7 @@ import salt.utils.process
import salt.utils.url
import salt.wheel
from salt.exceptions import (
SaltReqTimeoutError, SaltRenderError, CommandExecutionError
SaltReqTimeoutError, SaltRenderError, CommandExecutionError, SaltInvocationError
)
__proxyenabled__ = ['*']
@ -1085,7 +1085,7 @@ def runner(_fun, **kwargs):
return rclient.cmd(_fun, kwarg=kwargs)
def wheel(_fun, **kwargs):
def wheel(_fun, *args, **kwargs):
'''
Execute a wheel module (this function must be run on the master)
@ -1093,6 +1093,13 @@ def wheel(_fun, **kwargs):
name
The name of the function to run
args
Any positional arguments to pass to the wheel function. A common example
of this would be the ``match`` arg needed for key functions.
.. versionadded:: v2015.8.11
kwargs
Any keyword arguments to pass to the wheel function
@ -1100,10 +1107,33 @@ def wheel(_fun, **kwargs):
.. code-block:: bash
salt '*' saltutil.wheel key.accept match=jerry
salt '*' saltutil.wheel key.accept jerry
'''
wclient = salt.wheel.WheelClient(__opts__)
return wclient.cmd(_fun, kwarg=kwargs)
if __opts__['__role'] == 'minion':
master_config = os.path.join(os.path.dirname(__opts__['conf_file']),
'master')
master_opts = salt.config.client_config(master_config)
wheel_client = salt.wheel.WheelClient(master_opts)
else:
wheel_client = salt.wheel.WheelClient(__opts__)
# The WheelClient cmd needs args, kwargs, and pub_data separated out from
# the "normal" kwargs structure, which at this point contains __pub_x keys.
pub_data = {}
valid_kwargs = {}
for key, val in six.iteritems(kwargs):
if key.startswith('__'):
pub_data[key] = val
else:
valid_kwargs[key] = val
try:
ret = wheel_client.cmd(_fun, arg=args, pub_data=pub_data, kwarg=valid_kwargs)
except SaltInvocationError:
raise CommandExecutionError('This command can only be executed on a minion '
'that is located on the master.')
return ret
# this is the only way I could figure out how to get the REAL file_roots

View file

@ -207,7 +207,22 @@ def low(data, queue=False, **kwargs):
return ret
def high(data, test=False, queue=False, **kwargs):
def _get_test_value(test=None, **kwargs):
'''
Determine the correct value for the test flag.
'''
ret = True
if test is None:
if salt.utils.test_mode(test=test, **kwargs):
ret = True
else:
ret = __opts__.get('test', None)
else:
ret = test
return ret
def high(data, test=None, queue=False, **kwargs):
'''
Execute the compound calls stored in a single set of high data
@ -225,12 +240,7 @@ def high(data, test=False, queue=False, **kwargs):
return conflict
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
elif test is not None:
opts['test'] = test
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
pillar = kwargs.get('pillar')
pillar_enc = kwargs.get('pillar_enc')
@ -664,13 +674,7 @@ def highstate(test=None,
opts = _get_opts(kwargs.get('localconfig'))
if test is None:
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
else:
opts['test'] = __opts__.get('test', None)
else:
opts['test'] = test
opts['test'] = _get_test_value(test, **kwargs)
if 'env' in kwargs:
salt.utils.warn_until(
@ -882,12 +886,7 @@ def sls(mods,
orig_test = __opts__.get('test', None)
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
elif test is not None:
opts['test'] = test
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
pillar = kwargs.get('pillar')
pillar_enc = kwargs.get('pillar_enc')
@ -1010,10 +1009,7 @@ def top(topfn,
return err
orig_test = __opts__.get('test', None)
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
pillar = kwargs.get('pillar')
pillar_enc = kwargs.get('pillar_enc')
@ -1130,10 +1126,7 @@ def sls_id(
return conflict
orig_test = __opts__.get('test', None)
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
if 'pillarenv' in kwargs:
opts['pillarenv'] = kwargs['pillarenv']
st_ = salt.state.HighState(opts)
@ -1195,10 +1188,7 @@ def show_low_sls(mods,
return conflict
orig_test = __opts__.get('test', None)
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
if 'pillarenv' in kwargs:
opts['pillarenv'] = kwargs['pillarenv']
st_ = salt.state.HighState(opts)
@ -1252,10 +1242,7 @@ def show_sls(mods, saltenv='base', test=None, queue=False, **kwargs):
orig_test = __opts__.get('test', None)
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
pillar = kwargs.get('pillar')
pillar_enc = kwargs.get('pillar_enc')
@ -1355,10 +1342,7 @@ def single(fun, name, test=None, queue=False, **kwargs):
'name': name})
orig_test = __opts__.get('test', None)
opts = _get_opts(kwargs.get('localconfig'))
if salt.utils.test_mode(test=test, **kwargs):
opts['test'] = True
else:
opts['test'] = __opts__.get('test', None)
opts['test'] = _get_test_value(test, **kwargs)
pillar = kwargs.get('pillar')
pillar_enc = kwargs.get('pillar_enc')
@ -1414,7 +1398,7 @@ def clear_cache():
return ret
def pkg(pkg_path, pkg_sum, hash_type, test=False, **kwargs):
def pkg(pkg_path, pkg_sum, hash_type, test=None, **kwargs):
'''
Execute a packaged state run, the packaged state run will exist in a
tarball available locally. This packaged state
@ -1458,10 +1442,7 @@ def pkg(pkg_path, pkg_sum, hash_type, test=False, **kwargs):
popts = _get_opts(kwargs.get('localconfig'))
popts['fileclient'] = 'local'
popts['file_roots'] = {}
if salt.utils.test_mode(test=test, **kwargs):
popts['test'] = True
else:
popts['test'] = __opts__.get('test', None)
popts['test'] = _get_test_value(test, **kwargs)
envs = os.listdir(root)
for fn_ in envs:
full = os.path.join(root, fn_)

View file

@ -171,12 +171,16 @@ def install(feature, recurse=False, restart=False):
management_tools)
out = _pshell_json(cmd)
ret = {'ExitCode': out['ExitCode'],
'DisplayName': out['FeatureResult'][0]['DisplayName'],
'RestartNeeded': out['FeatureResult'][0]['RestartNeeded'],
'Success': out['Success']}
return ret
if out['FeatureResult']:
return {'ExitCode': out['ExitCode'],
'DisplayName': out['FeatureResult'][0]['DisplayName'],
'RestartNeeded': out['FeatureResult'][0]['RestartNeeded'],
'Success': out['Success']}
else:
return {'ExitCode': out['ExitCode'],
'DisplayName': '{0} (already installed)'.format(feature),
'RestartNeeded': False,
'Success': out['Success']}
def remove(feature):
@ -206,9 +210,13 @@ def remove(feature):
'-WarningAction SilentlyContinue'.format(_cmd_quote(feature))
out = _pshell_json(cmd)
ret = {'ExitCode': out['ExitCode'],
'DisplayName': out['FeatureResult'][0]['DisplayName'],
'RestartNeeded': out['FeatureResult'][0]['RestartNeeded'],
'Success': out['Success']}
return ret
if out['FeatureResult']:
return {'ExitCode': out['ExitCode'],
'DisplayName': out['FeatureResult'][0]['DisplayName'],
'RestartNeeded': out['FeatureResult'][0]['RestartNeeded'],
'Success': out['Success']}
else:
return {'ExitCode': out['ExitCode'],
'DisplayName': '{0} (not installed)'.format(feature),
'RestartNeeded': False,
'Success': out['Success']}

View file

@ -1333,7 +1333,10 @@ def upgrade(refresh=True,
except MinionError as exc:
raise CommandExecutionError(exc)
targets = [x for x in pkg_params]
if pkg_params:
targets = [x for x in pkg_params]
else:
targets = None
cmd = [_yum(), '--quiet', '-y']
for args in (repo_arg, exclude_arg, branch_arg):
@ -1342,7 +1345,8 @@ def upgrade(refresh=True,
if skip_verify:
cmd.append('--nogpgcheck')
cmd.append('upgrade')
cmd.extend(targets)
if targets:
cmd.extend(targets)
__salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False)
__context__.pop('pkg.list_pkgs', None)

View file

@ -769,6 +769,7 @@ def mod_repo(repo, **kwargs):
# Modify added or existing repo according to the options
cmd_opt = []
global_cmd_opt = []
if 'enabled' in kwargs:
cmd_opt.append(kwargs['enabled'] and '--enable' or '--disable')
@ -784,18 +785,18 @@ def mod_repo(repo, **kwargs):
if 'gpgcheck' in kwargs:
cmd_opt.append(kwargs['gpgcheck'] and '--gpgcheck' or '--no-gpgcheck')
if kwargs.get('gpgautoimport') is True:
cmd_opt.append('--gpg-auto-import-keys')
if 'priority' in kwargs:
cmd_opt.append("--priority={0}".format(kwargs.get('priority', DEFAULT_PRIORITY)))
if 'humanname' in kwargs:
cmd_opt.append("--name='{0}'".format(kwargs.get('humanname')))
if kwargs.get('gpgautoimport') is True:
global_cmd_opt.append('--gpg-auto-import-keys')
if cmd_opt:
cmd_opt.append(repo)
__zypper__.refreshable.xml.call('mr', *cmd_opt)
cmd_opt = global_cmd_opt + ['mr'] + cmd_opt + [repo]
__zypper__.refreshable.xml.call(*cmd_opt)
# If repo nor added neither modified, error should be thrown
if not added and not cmd_opt:
@ -1583,6 +1584,9 @@ def download(*packages, **kwargs):
pkg_ret[key] = pkg_info
if pkg_ret:
failed = [pkg for pkg in packages if pkg not in pkg_ret]
if failed:
pkg_ret['_error'] = ('The following package(s) failed to download: {0}'.format(', '.join(failed)))
return pkg_ret
raise CommandExecutionError(

View file

@ -242,6 +242,54 @@ external template file.
following tags: `macro`, `set`, `load_yaml`, `load_json`, `import_yaml` and
`import_json`.
Escaping Jinja
==============
Occasionally, it may be necessary to escape Jinja syntax. There are two ways to
to do this in Jinja. One is escaping individual variables or strings and the
other is to escape entire blocks.
To escape a string commonly used in Jinja syntax such as ``{{``, you can use the
following syntax:
.. code-block:: jinja
{{ '{{' }}
For larger blocks that contain Jinja syntax that needs to be escaped, you can use
raw blocks:
.. code-block:: jinja
{% raw %]
some text that contains jinja characters that need to be escaped
{% endraw %}
See the `Escaping`_ section of Jinja's documentation to learn more.
A real-word example of needing to use raw tags to escape a larger block of code
is when using ``file.managed`` with the ``contents_pillar`` option to manage
files that contain something like consul-template, which shares a syntax subset
with Jinja. Raw blocks are necessary here because the Jinja in the pillar would
be rendered before the file.managed is ever called, so the Jinja syntax must be
escaped:
.. code-block:: jinja
{% raw %}
- contents_pillar: |
job "example-job" {
<snipped>
task "example" {
driver = "docker"
config {
image = "docker-registry.service.consul:5000/example-job:{{key "nomad/jobs/example-job/version"}}"
<snipped>
{% endraw %}
.. _`Escaping`: http://jinja.pocoo.org/docs/dev/templates/#escaping
Calling Salt Functions
======================

View file

@ -419,14 +419,14 @@ def clean_old_jobs():
for final in t_path_dirs:
f_path = os.path.join(t_path, final)
jid_file = os.path.join(f_path, 'jid')
if not os.path.isfile(jid_file):
if not os.path.isfile(jid_file) and os.path.exists(t_path):
# No jid file means corrupted cache entry, scrub it
# by removing the entire t_path directory
shutil.rmtree(t_path)
else:
elif os.path.isfile(jid_file):
jid_ctime = os.stat(jid_file).st_ctime
hours_difference = (cur - jid_ctime) / 3600.0
if hours_difference > __opts__['keep_jobs']:
if hours_difference > __opts__['keep_jobs'] and os.path.exists(t_path):
# Remove the entire t_path from the original JID dir
shutil.rmtree(t_path)

View file

@ -349,9 +349,13 @@ def list_jobs(ext_source=None,
if search_target and _match:
_match = False
if 'Target' in ret[item]:
for key in salt.utils.split_input(search_target):
if fnmatch.fnmatch(ret[item]['Target'], key):
_match = True
targets = ret[item]['Target']
if isinstance(targets, six.string_types):
targets = [targets]
for target in targets:
for key in salt.utils.split_input(search_target):
if fnmatch.fnmatch(target, key):
_match = True
if search_function and _match:
_match = False
@ -488,6 +492,33 @@ def print_job(jid, ext_source=None, outputter=None):
return ret
def exit_success(jid, ext_source=None):
'''
Check if a job has been executed and exit successfully
jid
The jid to look up.
ext_source
The external job cache to use. Default: `None`.
CLI Example:
.. code-block:: bash
salt-run jobs.exit_success 20160520145827701627
'''
ret = dict()
data = lookup_jid(
jid,
ext_source=ext_source
)
for minion in data:
if "retcode" in data[minion]:
ret[minion] = True if not data[minion]['retcode'] else False
return ret
def last_run(ext_source=None,
outputter=None,
metadata=None,

View file

@ -72,7 +72,7 @@ def enable(name):
Name of the Apache module
'''
salt.utils.warn_until(
'Carbon',
'Nitrogen',
'This functionality has been deprecated; use "apache_module.enabled" '
'instead.'
)
@ -122,7 +122,7 @@ def disable(name):
Name of the Apache module
'''
salt.utils.warn_until(
'Carbon',
'Nitrogen',
'This functionality has been deprecated; use "apache_module.disabled" '
'instead.'
)

View file

@ -498,7 +498,7 @@ def wait(name,
'Replace them with runas. '
'These arguments will be removed in Salt Oxygen.'
)
if kwargs['user'] is not None and runas is None:
if 'user' in kwargs and kwargs['user'] is not None and runas is None:
runas = kwargs.pop('user')
# Ignoring our arguments is intentional.
@ -629,7 +629,7 @@ def wait_script(name,
'Replace them with runas. '
'These arguments will be removed in Salt Oxygen.'
)
if kwargs['user'] is not None and runas is None:
if 'user' in kwargs and kwargs['user'] is not None and runas is None:
runas = kwargs.pop('user')
# Ignoring our arguments is intentional.
@ -812,17 +812,18 @@ def run(name,
'Replace them with runas. '
'These arguments will be removed in Salt Oxygen.'
)
if kwargs['user'] is not None and runas is None:
if 'user' in kwargs and kwargs['user'] is not None and runas is None:
runas = kwargs.pop('user')
cmd_kwargs = {'cwd': cwd,
'runas': runas,
'use_vt': use_vt,
'shell': shell or __grains__['shell'],
'env': env,
'umask': umask,
'output_loglevel': output_loglevel,
'quiet': quiet}
cmd_kwargs = copy.deepcopy(kwargs)
cmd_kwargs.update({'cwd': cwd,
'runas': runas,
'use_vt': use_vt,
'shell': shell or __grains__['shell'],
'env': env,
'umask': umask,
'output_loglevel': output_loglevel,
'quiet': quiet})
cret = mod_run_check(cmd_kwargs, onlyif, unless, creates)
if isinstance(cret, dict):
@ -1048,7 +1049,7 @@ def script(name,
'Replace them with runas. '
'These arguments will be removed in Salt Oxygen.'
)
if kwargs['user'] is not None and runas is None:
if 'user' in kwargs and kwargs['user'] is not None and runas is None:
runas = kwargs.pop('user')
cmd_kwargs = copy.deepcopy(kwargs)

View file

@ -10,11 +10,11 @@ Manage etcd Keys
This state module supports setting and removing keys from etcd.
Salt Master Configuration
-------------------------
Configuration
-------------
To work with an etcd server you must configure an etcd profile in the Salt
Master configuration, for example:
To work with an etcd server you must configure an etcd profile. The etcd config
can be set in either the Salt Minion configuration file or in pillar:
.. code-block:: yaml
@ -22,14 +22,25 @@ Master configuration, for example:
etcd.host: 127.0.0.1
etcd.port: 4001
You can also configure etcd without a profile however it is recommended that
you use profiles:
It is technically possible to configure etcd without using a profile, but this
is not considered to be a best practice, especially when multiple etcd servers
or clusters are available.
.. code-block:: yaml
etcd.host: 127.0.0.1
etcd.port: 4001
.. note::
The etcd configuration can also be set in the Salt Master config file,
but in order to use any etcd configurations defined in the Salt Master
config, the :conf_master:`pillar_opts` must be set to ``True``.
Be aware that setting ``pillar_opts`` to ``True`` has security implications
as this makes all master configuration settings available in all minion's
pillars.
Available Functions
-------------------

View file

@ -10,16 +10,19 @@ file on the minions, by default at: /etc/salt/grains
Note: This does NOT override any grains set in the minion file.
'''
# Import Python libs
from __future__ import absolute_import
from salt.defaults import DEFAULT_TARGET_DELIM
import re
# Import Salt libs
from salt.defaults import DEFAULT_TARGET_DELIM
def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
'''
Ensure that a grain is set
.. versionchanged:: 2016.3.0
.. versionchanged:: v2015.8.2
name
The grain name
@ -27,14 +30,16 @@ def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
value
The value to set on the grain
:param force: If force is True, the existing grain will be overwritten
force
If force is True, the existing grain will be overwritten
regardless of its existing or provided value type. Defaults to False
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
:param delimiter: A delimiter different from the default can be provided.
delimiter
A delimiter different from the default can be provided.
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
It is now capable to set a grain to a complex value (ie. lists and dicts)
and supports nested grains as well.
@ -59,11 +64,11 @@ def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
- name: icinga:Apache SSL
- value:
- command: check_https
- params: -H localhost -p 443 -S
- params: -H localhost -p 443 -S
with,a,custom,delimiter:
grains.present:
- value: yay
- value: yay
- delimiter: ,
'''
name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name)
@ -106,9 +111,10 @@ def list_present(name, value, delimiter=DEFAULT_TARGET_DELIM):
value
The value is present in the list type grain.
:param delimiter: A delimiter different from the default ``:`` can be provided.
delimiter
A delimiter different from the default ``:`` can be provided.
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
The grain should be `list type <http://docs.python.org/2/tutorial/datastructures.html#data-structures>`_
@ -198,9 +204,10 @@ def list_absent(name, value, delimiter=DEFAULT_TARGET_DELIM):
value
The value to delete from the grain list.
:param delimiter: A delimiter different from the default ``:`` can be provided.
delimiter
A delimiter different from the default ``:`` can be provided.
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
The grain should be `list type <http://docs.python.org/2/tutorial/datastructures.html#data-structures>`_
@ -273,19 +280,23 @@ def absent(name,
name
The grain name
:param destructive: If destructive is True, delete the entire grain. If
destructive
If destructive is True, delete the entire grain. If
destructive is False, set the grain's value to None. Defaults to False.
:param force: If force is True, the existing grain will be overwritten
force
If force is True, the existing grain will be overwritten
regardless of its existing or provided value type. Defaults to False
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
:param delimiter: A delimiter different from the default can be provided.
delimiter
A delimiter different from the default can be provided.
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
.. versionchanged:: v2015.8.2
.. versionchanged:: 2016.3.0
This state now support nested grains and complex values. It is also more
conservative: if a grain has a value that is a list or a dict, it will
not be removed unless the `force` parameter is True.
@ -367,13 +378,15 @@ def append(name, value, convert=False,
value
The value to append
:param convert: If convert is True, convert non-list contents into a list.
convert
If convert is True, convert non-list contents into a list.
If convert is False and the grain contains non-list contents, an error
is given. Defaults to False.
:param delimiter: A delimiter different from the default can be provided.
delimiter
A delimiter different from the default can be provided.
.. versionadded:: 2016.3.0
.. versionadded:: v2015.8.2
.. code-block:: yaml

View file

@ -72,7 +72,7 @@ def present(name,
password_hash=None,
allow_passwordless=False,
unix_socket=False,
password_column='Password',
password_column=None,
**connection_args):
'''
Ensure that the named user is present with the specified properties. A

View file

@ -274,6 +274,11 @@ def managed(name, ppa=None, **kwargs):
ret['result'] = False
ret['comment'] = 'You may not use both "keyid"/"keyserver" and ' \
'"key_url" argument.'
if 'repo' in kwargs:
ret['result'] = False
ret['comment'] = ('\'repo\' is not a supported argument for this '
'state. The \'name\' argument is probably what was '
'intended.')
return ret
repo = name

View file

@ -66,13 +66,21 @@ import time
from salt.exceptions import CommandExecutionError
import salt.utils
__virtualname__ = 'service'
def __virtual__():
'''
Only make these states available if a service provider has been detected or
assigned for this minion
'''
return 'service.start' in __salt__
if 'service.start' in __salt__:
return __virtualname__
else:
return (False, 'No service execution module loaded: '
'check support for service management on {0} '
''.format(__grains__.get('osfinger', __grains__['os']))
)
def _enabled_used_error(ret):
@ -284,7 +292,7 @@ def _available(name, ret):
def running(name, enable=None, sig=None, init_delay=None, **kwargs):
'''
Verify that the service is running
Ensure that the service is running
name
The name of the init or rc script used to manage the service
@ -478,7 +486,7 @@ def dead(name, enable=None, sig=None, **kwargs):
def enabled(name, **kwargs):
'''
Verify that the service is enabled on boot, only use this state if you
Ensure that the service is enabled on boot, only use this state if you
don't want to manage the running process, remember that if you want to
enable a running service to use the enable: True option for the running
or dead function.
@ -497,7 +505,7 @@ def enabled(name, **kwargs):
def disabled(name, **kwargs):
'''
Verify that the service is disabled on boot, only use this state if you
Ensure that the service is disabled on boot, only use this state if you
don't want to manage the running process, remember that if you want to
disable a service to use the enable: False option for the running or dead
function.

View file

@ -3,6 +3,9 @@
Manage Windows features via the ServerManager powershell module
'''
# Import salt modules
import salt.utils
def __virtual__():
'''
@ -52,10 +55,13 @@ def installed(name, recurse=False, force=False, restart=False):
'comment': ''}
# Determine if the feature is installed
if name not in __salt__['win_servermanager.list_installed']():
ret['changes'] = {'feature': '{0} will be installed recurse={1}'.format(name, recurse)}
old = __salt__['win_servermanager.list_installed']()
if name not in old:
ret['changes']['feature'] = \
'{0} will be installed recurse={1}'.format(name, recurse)
elif force and recurse:
ret['changes'] = {'feature': '{0} already installed but might install sub-features'.format(name)}
ret['changes']['feature'] = \
'{0} already installed but might install sub-features'.format(name)
else:
ret['comment'] = 'The feature {0} is already installed'.format(name)
return ret
@ -64,19 +70,26 @@ def installed(name, recurse=False, force=False, restart=False):
ret['result'] = None
return ret
# Install the features
ret['changes'] = {'feature': __salt__['win_servermanager.install'](name, recurse, restart)}
if ret['changes']['feature']:
ret['comment'] = ret['changes']['feature']
if 'Success' in ret['changes']['feature']:
ret['result'] = ret['changes']['feature']['Success']
if not ret['result']:
ret['comment'] = 'Failed to install {0}: {1}'.format(name, ret['changes']['feature']['ExitCode'])
else:
ret['comment'] = 'Installed {0}'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to install {0}.\nError Message:\n{1}'.format(name, ret['changes']['feature'])
ret['changes'] = {}
ret['changes'] = {}
# Install the features
status = __salt__['win_servermanager.install'](name, recurse, restart)
ret['result'] = status['Success']
if not ret['result']:
ret['comment'] = 'Failed to install {0}: {1}'\
.format(name, status['ExitCode'])
new = __salt__['win_servermanager.list_installed']()
changes = salt.utils.compare_dicts(old, new)
if changes:
ret['comment'] = 'Installed {0}'.format(name)
ret['changes'] = status
ret['changes']['feature'] = changes
return ret
@ -110,8 +123,9 @@ def removed(name):
'changes': {},
'comment': ''}
# Determine if the feature is installed
if name in __salt__['win_servermanager.list_installed']():
ret['changes'] = {'feature': '{0} will be removed'.format(name)}
old = __salt__['win_servermanager.list_installed']()
if name in old:
ret['changes']['feature'] = '{0} will be removed'.format(name)
else:
ret['comment'] = 'The feature {0} is not installed'.format(name)
return ret
@ -120,10 +134,22 @@ def removed(name):
ret['result'] = None
return ret
ret['changes'] = {}
# Remove the features
ret['changes'] = {'feature': __salt__['win_servermanager.remove'](name)}
ret['result'] = ret['changes']['feature']['Success']
status = __salt__['win_servermanager.remove'](name)
ret['result'] = status['Success']
if not ret['result']:
ret['comment'] = 'Failed to uninstall the feature {0}'.format(ret['changes']['feature']['ExitCode'])
ret['comment'] = 'Failed to uninstall the feature {0}'\
.format(status['ExitCode'])
new = __salt__['win_servermanager.list_installed']()
changes = salt.utils.compare_dicts(old, new)
if changes:
ret['comment'] = 'Removed {0}'.format(name)
ret['changes'] = status
ret['changes']['feature'] = changes
return ret

View file

@ -63,7 +63,7 @@ class SaltCacheLoader(BaseLoader):
self.searchpath = opts['file_roots'][saltenv]
else:
self.searchpath = [path.join(opts['cachedir'], 'files', saltenv)]
log.debug('Jinja search path: \'{0}\''.format(self.searchpath))
log.debug('Jinja search path: %s', self.searchpath)
self._file_client = None
self.cached = []
self.pillar_rend = pillar_rend
@ -154,10 +154,10 @@ class PrintableDict(OrderedDict):
for key, value in six.iteritems(self):
if isinstance(value, six.string_types):
# keeps quotes around strings
output.append('\'{0}\': \'{1}\''.format(key, value))
output.append('{0!r}: {1!r}'.format(key, value))
else:
# let default output
output.append('\'{0}\': {1!s}'.format(key, value))
output.append('{0!r}: {1!s}'.format(key, value))
return '{' + ', '.join(output) + '}'
def __repr__(self): # pylint: disable=W0221
@ -165,7 +165,7 @@ class PrintableDict(OrderedDict):
for key, value in six.iteritems(self):
# Raw string formatter required here because this is a repr
# function.
output.append('\'{0}\': {1!r}'.format(key, value))
output.append('{0!r}: {1!r}'.format(key, value))
return '{' + ', '.join(output) + '}'

View file

@ -100,7 +100,7 @@ SYS_TMP_DIR = os.path.realpath(
# Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long
# for unix sockets: ``error: AF_UNIX path too long``
# Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR}
os.environ.get('TMPDIR', tempfile.gettempdir()) if salt.utils.is_darwin() else '/tmp'
os.environ.get('TMPDIR', tempfile.gettempdir()) if not salt.utils.is_darwin() else '/tmp'
)
TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir')
FILES = os.path.join(INTEGRATION_TEST_DIR, 'files')

View file

@ -20,7 +20,7 @@ SYS_TMP_DIR = os.path.realpath(
# Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long
# for unix sockets: ``error: AF_UNIX path too long``
# Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR}
os.environ.get('TMPDIR', tempfile.gettempdir()) if salt.utils.is_darwin() else '/tmp'
os.environ.get('TMPDIR', tempfile.gettempdir()) if not salt.utils.is_darwin() else '/tmp'
)
# This tempdir path is defined on tests.integration.__init__
TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir')

View file

@ -12,7 +12,6 @@ from salttesting import skipIf
from salttesting.helpers import (
destructiveTest,
ensure_in_syspath,
requires_system_grains
)
ensure_in_syspath('../../')
@ -21,12 +20,17 @@ import integration
import salt.utils
from salt.exceptions import CommandExecutionError
# Import third party libs
import salt.ext.six as six
# Brew doesn't support local package installation - So, let's
# Grab some small packages available online for brew
ADD_PKG = 'algol68g'
DEL_PKG = 'acme'
@destructiveTest
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
class BrewModuleTest(integration.ModuleCase):
'''
Integration tests for the brew module
@ -51,10 +55,7 @@ class BrewModuleTest(integration.ModuleCase):
'You must have brew installed to run these tests'
)
@destructiveTest
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
@requires_system_grains
def test_brew_install(self, grains=None):
def test_brew_install(self):
'''
Tests the installation of packages
'''
@ -70,10 +71,7 @@ class BrewModuleTest(integration.ModuleCase):
self.run_function('pkg.remove', [ADD_PKG])
raise
@destructiveTest
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
@requires_system_grains
def test_remove(self, grains=None):
def test_remove(self):
'''
Tests the removal of packages
'''
@ -96,10 +94,7 @@ class BrewModuleTest(integration.ModuleCase):
self.run_function('pkg.remove', [DEL_PKG])
raise
@destructiveTest
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
@requires_system_grains
def test_mac_brew_pkg_version(self, grains=None):
def test_version(self):
'''
Test pkg.version for mac. Installs
a package and then checks we can get
@ -129,20 +124,79 @@ class BrewModuleTest(integration.ModuleCase):
self.run_function('pkg.remove', [ADD_PKG])
raise
@destructiveTest
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
@requires_system_grains
def test_mac_brew_refresh_db(self, grains=None):
def test_latest_version(self):
'''
Test pkg.latest_version:
- get the latest version available
- install the package
- get the latest version available
- check that the latest version is empty after installing it
'''
try:
self.run_function('pkg.remove', [ADD_PKG])
uninstalled_latest = self.run_function('pkg.latest_version', [ADD_PKG])
self.run_function('pkg.install', [ADD_PKG])
installed_latest = self.run_function('pkg.latest_version', [ADD_PKG])
version = self.run_function('pkg.version', [ADD_PKG])
try:
self.assertTrue(isinstance(uninstalled_latest, six.string_types))
self.assertEqual(installed_latest, '')
except AssertionError:
self.run_function('pkg.remove', [ADD_PKG])
raise
except CommandExecutionError:
self.run_function('pkg.remove', [ADD_PKG])
raise
def test_refresh_db(self):
'''
Integration test to ensure pkg.refresh_db works with brew
'''
refresh_brew = self.run_function('pkg.refresh_db')
self.assertTrue(refresh_brew)
@destructiveTest
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
@requires_system_grains
def tearDown(self, grains=None):
def test_list_upgrades(self):
'''
Test pkg.list_upgrades: data is in the form {'name1': 'version1',
'name2': 'version2', ... }
'''
try:
upgrades = self.run_function('pkg.list_upgrades')
try:
self.assertTrue(isinstance(upgrades, dict))
if len(upgrades):
for name in upgrades:
self.assertTrue(isinstance(name, six.string_types))
self.assertTrue(isinstance(upgrades[name], six.string_types))
except AssertionError:
self.run_function('pkg.remove', [ADD_PKG])
raise
except CommandExecutionError:
self.run_function('pkg.remove', [ADD_PKG])
raise
def test_info_installed(self):
'''
Test pkg.info_installed: info returned has certain fields used by
mac_brew.latest_version
'''
try:
self.run_function('pkg.install', [ADD_PKG])
info = self.run_function('pkg.info_installed', [ADD_PKG])
try:
self.assertTrue(ADD_PKG in info)
self.assertTrue('versions' in info[ADD_PKG])
self.assertTrue('revision' in info[ADD_PKG])
self.assertTrue('stable' in info[ADD_PKG]['versions'])
except AssertionError:
self.run_function('pkg.remove', [ADD_PKG])
raise
except CommandExecutionError:
self.run_function('pkg.remove', [ADD_PKG])
raise
def tearDown(self):
'''
Clean up after tests
'''

View file

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
'''
Integration tests for the saltutil module.
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing libs
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt libs
import integration
class SaltUtilModuleTest(integration.ModuleCase):
'''
Testcase for the saltutil execution module
'''
# Tests for the wheel function
def test_wheel_just_function(self):
'''
Tests using the saltutil.wheel function when passing only a function.
'''
ret = self.run_function('saltutil.wheel', ['minions.connected'])
self.assertEqual(ret, ['minion', 'sub_minion'])
def test_wheel_with_arg(self):
'''
Tests using the saltutil.wheel function when passing a function and an arg.
'''
ret = self.run_function('saltutil.wheel', ['key.list', 'minion'])
self.assertEqual(ret, {})
def test_wheel_no_arg_raise_error(self):
'''
Tests using the saltutil.wheel function when passing a function that requires
an arg, but one isn't supplied.
'''
self.assertRaises(TypeError, 'saltutil.wheel', ['key.list'])
def test_wheel_with_kwarg(self):
'''
Tests using the saltutil.wheel function when passing a function and a kwarg.
This function just generates a key pair, but doesn't do anything with it. We
just need this for testing purposes.
'''
ret = self.run_function('saltutil.wheel', ['key.gen'], keysize=1024)
self.assertIn('pub', ret)
self.assertIn('priv', ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(SaltUtilModuleTest)

View file

@ -86,6 +86,7 @@ class SysModuleTest(integration.ModuleCase):
'runtests_decorators.depends_will_fallback',
'runtests_decorators.missing_depends',
'runtests_decorators.missing_depends_will_fallback',
'swift.head',
'yumpkg.expand_repo_def',
'yumpkg5.expand_repo_def',
'container_resource.run',

View file

@ -8,12 +8,11 @@ Discover all instances of unittest.TestCase in this directory.
# Import python libs
from __future__ import absolute_import, print_function
import os
import tempfile
import time
# Import salt libs
from integration import TestDaemon, TMP # pylint: disable=W0403
from integration import INTEGRATION_TEST_DIR
from integration import SYS_TMP_DIR, INTEGRATION_TEST_DIR
from integration import CODE_DIR as SALT_ROOT
import salt.utils
@ -563,7 +562,7 @@ def main():
parser = SaltTestsuiteParser(
TEST_DIR,
xml_output_dir=XML_OUTPUT_DIR,
tests_logfile=os.path.join(tempfile.gettempdir(), 'salt-runtests.log')
tests_logfile=os.path.join(SYS_TMP_DIR, 'salt-runtests.log')
)
parser.parse_args()

View file

@ -8,6 +8,7 @@ from __future__ import absolute_import
# Import Salt Libs
from salt.modules import mac_brew
from salt.exceptions import CommandExecutionError
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
@ -38,7 +39,8 @@ class BrewTestCase(TestCase):
'''
Tests the return of the list of taps
'''
mock_taps = MagicMock(return_value={'stdout': TAPS_STRING})
mock_taps = MagicMock(return_value={'stdout': TAPS_STRING,
'retcode': 0})
mock_user = MagicMock(return_value='foo')
mock_cmd = MagicMock(return_value='')
with patch.dict(mac_brew.__salt__, {'file.get_user': mock_user,
@ -60,7 +62,9 @@ class BrewTestCase(TestCase):
'''
Tests if the tap installation failed
'''
mock_failure = MagicMock(return_value={'retcode': 1})
mock_failure = MagicMock(return_value={'stdout': '',
'stderr': '',
'retcode': 1})
mock_user = MagicMock(return_value='foo')
mock_cmd = MagicMock(return_value='')
with patch.dict(mac_brew.__salt__, {'cmd.run_all': mock_failure,
@ -147,10 +151,12 @@ class BrewTestCase(TestCase):
Tests an update of homebrew package repository failure
'''
mock_user = MagicMock(return_value='foo')
mock_failure = MagicMock(return_value={'retcode': 1})
mock_failure = MagicMock(return_value={'stdout': '',
'stderr': '',
'retcode': 1})
with patch.dict(mac_brew.__salt__, {'file.get_user': mock_user,
'cmd.run_all': mock_failure}):
self.assertFalse(mac_brew.refresh_db())
self.assertRaises(CommandExecutionError, mac_brew.refresh_db)
@patch('salt.modules.mac_brew._homebrew_bin',
MagicMock(return_value=HOMEBREW_BIN))

View file

@ -98,6 +98,22 @@ class RpmTestCase(TestCase):
self.assertDictEqual(rpm.owner('/usr/bin/python', '/usr/bin/vim'),
ret)
# 'checksum' function tests: 1
def test_checksum(self):
'''
Test if checksum validate as expected
'''
ret = {
"file1.rpm": True,
"file2.rpm": False,
"file3.rpm": False,
}
mock = MagicMock(side_effect=[True, 0, True, 1, False, 0])
with patch.dict(rpm.__salt__, {'file.file_exists': mock, 'cmd.retcode': mock}):
self.assertDictEqual(rpm.checksum("file1.rpm", "file2.rpm", "file3.rpm"), ret)
@patch('salt.modules.rpm.HAS_RPM', True)
def test_version_cmp_rpm(self):
'''

View file

@ -0,0 +1,19 @@
<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<message type="warning">Argument resolves to no package: foo</message>
<progress id="" name="(1/1) /var/cache/zypp/packages/SLE-12-x86_64-Pool/x86_64/nmap-6.46-1.72.x86_64.rpm"/>
<download-result>
<solvable>
<kind>package</kind>
<name>nmap</name>
<edition epoch="0" version="6.46" release="1.72"/>
<arch>x86_64</arch>
<repository name="SLE-12-x86_64-Pool" alias="SLE-12-x86_64-Pool"/>
</solvable>
<localfile path="/var/cache/zypp/packages/SLE-12-x86_64-Pool/x86_64/nmap-6.46-1.72.x86_64.rpm"/>
</download-result>
<progress id="" name="(1/1) /var/cache/zypp/packages/SLE-12-x86_64-Pool/x86_64/nmap-6.46-1.72.x86_64.rpm" done="0"/>
<message type="info">download: Done.</message>
</stream>

View file

@ -9,6 +9,7 @@ from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.mock import (
Mock,
MagicMock,
patch,
NO_MOCK,
@ -354,6 +355,30 @@ class ZypperTestCase(TestCase):
self.assertTrue(pkgs.get(pkg_name))
self.assertEqual(pkgs[pkg_name], pkg_version)
def test_download(self):
'''
Test package download
:return:
'''
download_out = {
'stdout': get_test_data('zypper-download.xml'),
'stderr': None,
'retcode': 0
}
test_out = {
'nmap': {
'repository-alias': u'SLE-12-x86_64-Pool',
'repository-name': u'SLE-12-x86_64-Pool'
}
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=download_out)}):
with patch.dict(zypper.__salt__, {'lowpkg.checksum': MagicMock(return_value=True)}):
self.assertEqual(zypper.download("nmap"), test_out)
test_out['_error'] = "The following package(s) failed to download: foo"
self.assertEqual(zypper.download("nmap", "foo"), test_out)
def test_remove_purge(self):
'''
Test package removal
@ -413,6 +438,39 @@ class ZypperTestCase(TestCase):
self.assertEqual(r_info['enabled'], alias == 'SLE12-SP1-x86_64-Update')
self.assertEqual(r_info['autorefresh'], alias == 'SLE12-SP1-x86_64-Update')
def test_modify_repo_gpg_auto_import_keys_parameter_position(self):
'''
Tests if when modifying a repo, --gpg-auto-import-keys is a global option
:return:
'''
zypper_patcher = patch.multiple(
'salt.modules.zypper',
**{
'_get_configured_repos': Mock(
**{
'return_value.sections.return_value': ['mock-repo-name']
}
),
'__zypper__': Mock(),
'get_repo': Mock()
}
)
with zypper_patcher:
zypper.mod_repo(
'mock-repo-name',
**{
'disabled': False,
'url': 'http://repo.url/some/path',
'gpgkey': 'http://repo.key',
'refresh': True,
'gpgautoimport': True
}
)
zypper.__zypper__.refreshable.xml.call.assert_called_once_with(
'--gpg-auto-import-keys', 'mr', '--refresh', 'mock-repo-name'
)
if __name__ == '__main__':
from integration import run_tests
run_tests(ZypperTestCase, needs_daemon=False)

View file

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
'''
unit tests for the jobs runner
'''
# Import Python Libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
patch
)
ensure_in_syspath('../../')
# Import Salt Libs
from salt.runners import jobs
import salt.minion
jobs.__opts__ = {'ext_job_cache': None, 'master_job_cache': 'local_cache'}
jobs.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class JobsTest(TestCase):
'''
Validate the jobs runner
'''
def test_list_jobs_with_search_target(self):
'''
test jobs.list_jobs runner with search_target args
'''
mock_jobs_cache = {
'20160524035503086853': {'Arguments': [],
'Function': 'test.ping',
'StartTime': '2016, May 24 03:55:03.086853',
'Target': 'node-1-1.com',
'Target-type': 'glob',
'User': 'root'},
'20160524035524895387': {'Arguments': [],
'Function': 'test.ping',
'StartTime': '2016, May 24 03:55:24.895387',
'Target': ['node-1-2.com', 'node-1-1.com'],
'Target-type': 'list',
'User': 'sudo_ubuntu'}
}
def return_mock_jobs():
return mock_jobs_cache
class MockMasterMinion(object):
returners = {'local_cache.get_jids': return_mock_jobs}
def __init__(self, *args, **kwargs):
pass
returns = {'all': mock_jobs_cache,
'node-1-1.com': mock_jobs_cache,
'node-1-2.com': {'20160524035524895387':
mock_jobs_cache['20160524035524895387']},
'non-existant': {}}
with patch.object(salt.minion, 'MasterMinion', MockMasterMinion):
self.assertEqual(jobs.list_jobs(), returns['all'])
self.assertEqual(jobs.list_jobs(search_target=['node-1-1*',
'node-1-2*']),
returns['all'])
self.assertEqual(jobs.list_jobs(search_target='node-1-1.com'),
returns['node-1-1.com'])
self.assertEqual(jobs.list_jobs(search_target='node-1-2.com'),
returns['node-1-2.com'])
self.assertEqual(jobs.list_jobs(search_target='non-existant'),
returns['non-existant'])
if __name__ == '__main__':
from integration import run_tests
run_tests(JobsTest, needs_daemon=False)

View file

@ -35,58 +35,92 @@ class WinServermanagerTestCase(TestCase):
'''
Test to install the windows feature
'''
ret = {'name': 'salt',
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(side_effect=['salt', 'stack', 'stack'])
mock1 = MagicMock(return_value={'Success': True})
mock_list = MagicMock(
side_effect=[{'spongebob': 'squarepants'},
{'squidward': 'patrick'},
{'spongebob': 'squarepants'},
{'spongebob': 'squarepants',
'squidward': 'patrick'}])
mock_install = MagicMock(
return_value={'Success': True,
'RestartNeeded': False,
'ExitCode': 1234})
with patch.dict(win_servermanager.__salt__,
{"win_servermanager.list_installed": mock,
"win_servermanager.install": mock1}):
ret.update({'comment': 'The feature salt is already installed'})
self.assertDictEqual(win_servermanager.installed('salt'), ret)
{"win_servermanager.list_installed": mock_list,
"win_servermanager.install": mock_install}):
ret = {'name': 'spongebob',
'changes': {},
'result': True,
'comment': 'The feature spongebob is already installed'}
self.assertDictEqual(win_servermanager.installed('spongebob'), ret)
with patch.dict(win_servermanager.__opts__, {"test": True}):
ret.update({'changes': {'feature':
'salt will be installed'
' recurse=False'}, 'result': None,
'comment': ''})
self.assertDictEqual(win_servermanager.installed('salt'), ret)
ret = {'name': 'spongebob',
'result': None,
'comment': '',
'changes': {
'feature': 'spongebob will be installed '
'recurse=False'}}
self.assertDictEqual(
win_servermanager.installed('spongebob'), ret)
with patch.dict(win_servermanager.__opts__, {"test": False}):
ret.update({'changes': {'feature': {'Success': True}},
'result': True, 'comment': 'Installed salt'})
self.assertDictEqual(win_servermanager.installed('salt'),
ret)
with patch.dict(win_servermanager.__opts__, {"test": False}):
ret = {'name': 'squidward',
'result': True,
'comment': 'Installed squidward',
'changes': {
'Success': True,
'RestartNeeded': False,
'ExitCode': 1234,
'feature': {'squidward': {'new': 'patrick',
'old': ''}}}}
self.assertDictEqual(
win_servermanager.installed('squidward'), ret)
def test_removed(self):
'''
Test to remove the windows feature
'''
ret = {'name': 'salt',
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(side_effect=['stack', 'salt', 'salt'])
mock1 = MagicMock(return_value={'Success': True})
mock_list = MagicMock(
side_effect=[{'spongebob': 'squarepants'},
{'squidward': 'patrick'},
{'spongebob': 'squarepants',
'squidward': 'patrick'},
{'spongebob': 'squarepants'}])
mock_remove = MagicMock(
return_value={'Success': True,
'RestartNeeded': False,
'ExitCode': 1234})
with patch.dict(win_servermanager.__salt__,
{"win_servermanager.list_installed": mock,
"win_servermanager.remove": mock1}):
ret.update({'comment': 'The feature salt is not installed'})
self.assertDictEqual(win_servermanager.removed('salt'), ret)
{"win_servermanager.list_installed": mock_list,
"win_servermanager.remove": mock_remove}):
ret = {'name': 'squidward',
'changes': {},
'result': True,
'comment': 'The feature squidward is not installed'}
self.assertDictEqual(
win_servermanager.removed('squidward'), ret)
with patch.dict(win_servermanager.__opts__, {"test": True}):
ret.update({'changes': {'feature':
'salt will be removed'},
'result': None, 'comment': ''})
self.assertDictEqual(win_servermanager.removed('salt'), ret)
ret = {'name': 'squidward',
'result': None,
'comment': '',
'changes': {'feature': 'squidward will be removed'}}
self.assertDictEqual(
win_servermanager.removed('squidward'), ret)
with patch.dict(win_servermanager.__opts__, {"test": False}):
ret.update({'changes': {'feature': {'Success': True}},
'result': True})
self.assertDictEqual(win_servermanager.removed('salt'),
ret)
with patch.dict(win_servermanager.__opts__, {"test": False}):
ret = {'name': 'squidward',
'result': True,
'comment': 'Removed squidward',
'changes': {
'Success': True,
'RestartNeeded': False,
'ExitCode': 1234,
'feature': {'squidward': {'new': '',
'old': 'patrick'}}}}
self.assertDictEqual(
win_servermanager.removed('squidward'), ret)
if __name__ == '__main__':